mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
docs: Add notification architecture documentation
This commit is contained in:
parent
b1f7a6a8b1
commit
7b6ddfa272
@ -21,6 +21,8 @@ flowchart LR
|
|||||||
StaticAssetHandler("<strong>StaticAssetHandler</strong><br>StaticAssetHandler")
|
StaticAssetHandler("<strong>StaticAssetHandler</strong><br>StaticAssetHandler")
|
||||||
SetupHandler("<strong>SetupHandler</strong><br><i>HttpHandler</i>")
|
SetupHandler("<strong>SetupHandler</strong><br><i>HttpHandler</i>")
|
||||||
OidcHandler("<strong>OidcHandler</strong><br><i>HttpHandler</i>")
|
OidcHandler("<strong>OidcHandler</strong><br><i>HttpHandler</i>")
|
||||||
|
NotificationHttpHandler("<strong>NotificationHttpHandler</strong><br><i>HttpHandler</i>")
|
||||||
|
StorageDescriptionHandler("<strong>StorageDescriptionHandler</strong><br><i>HttpHandler</i>")
|
||||||
AuthResourceHttpHandler("<strong>AuthResourceHttpHandler</strong><br><i>HttpHandler</i>")
|
AuthResourceHttpHandler("<strong>AuthResourceHttpHandler</strong><br><i>HttpHandler</i>")
|
||||||
IdentityProviderHttpHandler("<strong>IdentityProviderHttpHandler</strong><br><i>HttpHandler</i>")
|
IdentityProviderHttpHandler("<strong>IdentityProviderHttpHandler</strong><br><i>HttpHandler</i>")
|
||||||
LdpHandler("<strong>LdpHandler</strong><br><i>HttpHandler</i>")
|
LdpHandler("<strong>LdpHandler</strong><br><i>HttpHandler</i>")
|
||||||
@ -28,7 +30,9 @@ flowchart LR
|
|||||||
|
|
||||||
StaticAssetHandler --> SetupHandler
|
StaticAssetHandler --> SetupHandler
|
||||||
SetupHandler --> OidcHandler
|
SetupHandler --> OidcHandler
|
||||||
OidcHandler --> AuthResourceHttpHandler
|
OidcHandler --> NotificationHttpHandler
|
||||||
|
NotificationHttpHandler --> StorageDescriptionHandler
|
||||||
|
StorageDescriptionHandler --> AuthResourceHttpHandler
|
||||||
AuthResourceHttpHandler --> IdentityProviderHttpHandler
|
AuthResourceHttpHandler --> IdentityProviderHttpHandler
|
||||||
IdentityProviderHttpHandler --> LdpHandler
|
IdentityProviderHttpHandler --> LdpHandler
|
||||||
```
|
```
|
||||||
@ -66,6 +70,19 @@ to the Solid-OIDC [specification](https://solid.github.io/solid-oidc/).
|
|||||||
The OIDC component is configured to work on the `/.oidc/` subpath,
|
The OIDC component is configured to work on the `/.oidc/` subpath,
|
||||||
so this handler catches all those requests and sends them to the internal OIDC library that is used.
|
so this handler catches all those requests and sends them to the internal OIDC library that is used.
|
||||||
|
|
||||||
|
## NotificationHttpHandler
|
||||||
|
|
||||||
|
The `urn:solid-server:default:NotificationHttpHandler` catches all notification subscription requests.
|
||||||
|
By default these are requests targeting `/.notifications/`.
|
||||||
|
Which specific subscription type is targeted is then based on the next part of the URL.
|
||||||
|
|
||||||
|
## StorageDescriptionHandler
|
||||||
|
|
||||||
|
The `urn:solid-server:default:StorageDescriptionHandler` returns the relevant RDF data
|
||||||
|
for requests targeting a storage description resource.
|
||||||
|
It does this by knowing which URL suffix is used for such resources,
|
||||||
|
and verifying that the associated container is an actual storage container.
|
||||||
|
|
||||||
## AuthResourceHttpHandler
|
## AuthResourceHttpHandler
|
||||||
|
|
||||||
The `urn:solid-server:default:AuthResourceHttpHandler` is identical
|
The `urn:solid-server:default:AuthResourceHttpHandler` is identical
|
||||||
|
@ -108,25 +108,32 @@ to add any custom initializers that need to run.
|
|||||||
|
|
||||||
The `ServerInitializer` is the initializer that finally starts up the server by listening to the relevant port,
|
The `ServerInitializer` is the initializer that finally starts up the server by listening to the relevant port,
|
||||||
once all the initialization described above is finished.
|
once all the initialization described above is finished.
|
||||||
This is an example of a component that differs based on some of the choices made during configuration.
|
It takes as input 2 components: a `HttpServerFactory` and a `ServerListener`.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
ServerInitializer("<strong>ServerInitializer</strong><br>ServerInitializer")
|
ServerInitializer("<strong>ServerInitializer</strong><br>ServerInitializer")
|
||||||
ServerInitializer --> WebSocketServerFactory("<strong>ServerFactory</strong><br>WebSocketServerFactory")
|
ServerInitializer --> ServerInitializerArgs
|
||||||
WebSocketServerFactory --> BaseHttpServerFactory("<br>BaseHttpServerFactory")
|
|
||||||
BaseHttpServerFactory --> HttpHandler("<strong>HttpHandler</strong><br><i>HttpHandler</i>")
|
|
||||||
|
|
||||||
ServerInitializer2("<strong>ServerInitializer</strong><br>ServerInitializer")
|
subgraph ServerInitializerArgs[" "]
|
||||||
ServerInitializer2 ---> BaseHttpServerFactory2("<strong>ServerFactory</strong><br>BaseHttpServerFactory")
|
direction LR
|
||||||
BaseHttpServerFactory2 --> HttpHandler2("<strong>HttpHandler</strong><br><i>HttpHandler</i>")
|
ServerFactory("<strong>ServerFactory</strong><br>BaseServerFactory")
|
||||||
|
ServerListener("<strong>ServerListener</strong><br>ParallelHandler")
|
||||||
|
end
|
||||||
|
|
||||||
|
ServerListener --> HandlerServerListener("<strong>HandlerServerListener</strong><br>HandlerServerListener")
|
||||||
|
|
||||||
|
HandlerServerListener --> HttpHandler("<strong>HttpHandler</strong><br><i>HttpHandler</i>")
|
||||||
```
|
```
|
||||||
|
|
||||||
Depending on if the configurations necessary for websockets are imported or not,
|
The `HttpServerFactory` is responsible for starting a server on a given port.
|
||||||
the `urn:solid-server:default:ServerFactory` identifier will point to a different resource.
|
Depending on the configuration this can be an HTTP or an HTTPS server.
|
||||||
There will always be a `BaseHttpServerFactory` that starts the HTTP(S) server,
|
The created server emits events when it receives requests.
|
||||||
but there might also be a `WebSocketServerFactory` wrapped around it to handle websocket support.
|
|
||||||
Although not indicated here, the parameters for initializing the `BaseHttpServerFactory`
|
|
||||||
might also differ in case an HTTPS configuration is imported.
|
|
||||||
|
|
||||||
The `HttpHandler` it takes as input is responsible for how [HTTP requests get resolved](http-handler.md).
|
A `ServerListener` is a class that takes the created server as input and attaches a listener to interpret events.
|
||||||
|
One listener that is always used is the `urn:solid-server:default:HandlerServerListener`,
|
||||||
|
which calls an `HttpHandler` [to resolve HTTP requests](http-handler.md).
|
||||||
|
|
||||||
|
Sometimes it is necessary to add additional listeners,
|
||||||
|
these can then be added to the `urn:solid-server:default:ServerListener` as it is a `ParallellHandler`.
|
||||||
|
An example of this is when WebSockets are used [to handle notifications](notifications.md).
|
||||||
|
172
documentation/markdown/architecture/features/notifications.md
Normal file
172
documentation/markdown/architecture/features/notifications.md
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
# Notifications
|
||||||
|
|
||||||
|
This section covers the architecture used to support Notifications protocol
|
||||||
|
as described in <https://solidproject.org/TR/notifications-protocol>.
|
||||||
|
|
||||||
|
There are 3 core architectural components to this that each have separate entry points:
|
||||||
|
|
||||||
|
* Exposing metadata to allow discovery of the subscription type.
|
||||||
|
* Handling subscriptions targeting a resource.
|
||||||
|
* Emitting notifications when there is activity on a resource.
|
||||||
|
|
||||||
|
## Discovery
|
||||||
|
|
||||||
|
Discovery is done through the storage description resource(s).
|
||||||
|
The server returns the same triples for every such resource
|
||||||
|
as the notification subscription URL is always located in the root of the server.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
StorageDescriptionHandler("<br>StorageDescriptionHandler")
|
||||||
|
StorageDescriptionHandler --> StorageDescriber("<strong>StorageDescriber</strong><br>ArrayUnionHandler")
|
||||||
|
StorageDescriber --> StorageDescriberArgs
|
||||||
|
|
||||||
|
subgraph StorageDescriberArgs[" "]
|
||||||
|
direction LR
|
||||||
|
NotificationDescriber("<br>NotificationDescriber")
|
||||||
|
NotificationDescriber2("<br>NotificationDescriber")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
The server uses a `StorageDescriptionHandler` to generate the necessary RDF data
|
||||||
|
and to handle content negotiation.
|
||||||
|
To generate the data we have multiple `StorageDescriber`s,
|
||||||
|
whose results get merged together in an `ArrayUnionHandler`.
|
||||||
|
|
||||||
|
One specific instance of a `StorageDescriber` is a `NotificationSubcriber`,
|
||||||
|
that contains all the necessary presets to describe a notification subscription type.
|
||||||
|
When adding a new subscription type,
|
||||||
|
a new instance of such a class should be added to the `urn:solid-server:default:StorageDescriber`.
|
||||||
|
|
||||||
|
## Subscription
|
||||||
|
|
||||||
|
To subscribe, a client has to send a specific JSON-LD request to the URl found during discovery.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
NotificationTypeHandler("<strong>NotificationTypeHandler</strong><br>WaterfallHandler")
|
||||||
|
NotificationTypeHandler --> NotificationTypeHandlerArgs
|
||||||
|
|
||||||
|
subgraph NotificationTypeHandlerArgs[" "]
|
||||||
|
direction LR
|
||||||
|
OperationRouterHandler("<br>OperationRouterHandler") --> NotificationSubscriber("<br>NotificationSubscriber")
|
||||||
|
NotificationSubscriber --> SubscriptionType("<br><i>SubscriptionType</i>")
|
||||||
|
OperationRouterHandler2("<br>OperationRouterHandler") --> NotificationSubscriber2("<br>NotificationSubscriber")
|
||||||
|
NotificationSubscriber2 --> SubscriptionType2("<br><i>SubscriptionType</i>")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Every subscription type should have a subscription URL relative to the root notification URL,
|
||||||
|
which in our configs is set to `/.notifications/`.
|
||||||
|
For every type there is then a `OperationRouterHandler` that accepts requests to that specific URL,
|
||||||
|
after which a `NotificationSubscriber` handles all checks related to subscribing,
|
||||||
|
for which it uses a `SubscriptionType` that contains all the information necessary for a specific type.
|
||||||
|
If the subscription is valid and has authorization, the results will be saved in a `SubscriptionStorage`.
|
||||||
|
|
||||||
|
## Activity
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
ListeningActivityHandler("<strong>ListeningActivityHandler</strong><br>ListeningActivityHandler")
|
||||||
|
ListeningActivityHandler --> ListeningActivityHandlerArgs
|
||||||
|
|
||||||
|
subgraph ListeningActivityHandlerArgs[" "]
|
||||||
|
SubscriptionStorage("<strong>SubscriptionStorage</strong><br><i>SubscriptionStorage</i>")
|
||||||
|
ResourceStore("<strong>ResourceStore</strong><br><i>ActivityEmitter</i>")
|
||||||
|
NotificationHandler("<strong>NotificationHandler</strong><br>WaterfallHandler")
|
||||||
|
end
|
||||||
|
|
||||||
|
NotificationHandler --> NotificationHandlerArgs
|
||||||
|
subgraph NotificationHandlerArgs[" "]
|
||||||
|
direction TB
|
||||||
|
NotificationHandler1("<br><i>NotificationHandler</i>")
|
||||||
|
NotificationHandler2("<br><i>NotificationHandler</i>")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
An `ActivityEmitter` is a class that emits events every time data changes in the server.
|
||||||
|
The `MonitoringStore` is an implementation of this in the server.
|
||||||
|
The `ListeningActivityHandler` is the class that listens to these events
|
||||||
|
and makes sure relevant notifications get sent out.
|
||||||
|
|
||||||
|
It will pull the relevant subscriptions from the storage and call the stored `NotificationHandler` for each of time.
|
||||||
|
For every subscription type, a `NotificationHandler` should be added to the `WaterfallHandler`
|
||||||
|
that handles notifications for the specific type.
|
||||||
|
|
||||||
|
## WebSocketSubscription2021
|
||||||
|
|
||||||
|
To add support for WebSocketSubscription2021 notifications,
|
||||||
|
components were added as described in the documentation above.
|
||||||
|
|
||||||
|
For discovery a `NotificationDescriber` was added with the corresponding settings.
|
||||||
|
|
||||||
|
As `SubscriptionType` there is a specific `WebSocketSubscription2021` that contains all the necessary information.
|
||||||
|
|
||||||
|
### Handling notifications
|
||||||
|
|
||||||
|
As `NotificationHandler` the following architecture is used:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
TypedNotificationHandler("<br>TypedNotificationHandler")
|
||||||
|
TypedNotificationHandler --> ComposedNotificationHandler("<br>ComposedNotificationHandler")
|
||||||
|
ComposedNotificationHandler --> ComposedNotificationHandlerArgs
|
||||||
|
|
||||||
|
subgraph ComposedNotificationHandlerArgs[" "]
|
||||||
|
direction LR
|
||||||
|
BaseNotificationGenerator("<strong>BaseNotificationGenerator</strong><br><i>NotificationGenerator</i>")
|
||||||
|
BaseNotificationSerializer("<strong>BaseNotificationSerializer</strong><br><i>NotificationSerializer</i>")
|
||||||
|
WebSocket2021Emitter("<strong>WebSocket2021Emitter</strong><br>WebSocket2021Emitter")
|
||||||
|
BaseNotificationGenerator --> BaseNotificationSerializer --> WebSocket2021Emitter
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
A `TypedNotificationHandler` is a handler that can be used to filter out subscriptions for a specific type,
|
||||||
|
making sure only WebSocketSubscription2021 subscriptions will be handled.
|
||||||
|
|
||||||
|
A `ComposedNotificationHandler` combines 3 interfaces to handle the notifications:
|
||||||
|
|
||||||
|
* A `NotificationGenerator` converts the information into a Notification object.
|
||||||
|
* A `NotificationSerializer` converts a Notification object into a serialized Representation.
|
||||||
|
* A `NotificationEmitter` takes a Representation and sends it out in a way specific to that subscription type.
|
||||||
|
|
||||||
|
`urn:solid-server:default:BaseNotificationGenerator` is a generator that fills in the default Notification template,
|
||||||
|
and also caches the result so it can be reused by multiple subscriptions.
|
||||||
|
|
||||||
|
`urn:solid-server:default:BaseNotificationSerializer` converts the Notification to a JSON-LD representation
|
||||||
|
and handles any necessary content negotiation based on the `accept` notification feature.
|
||||||
|
|
||||||
|
A `WebSocket2021Emitter` is a specific emitter that checks the current open WebSockets
|
||||||
|
if they correspond to the subscription.
|
||||||
|
|
||||||
|
### WebSockets
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
WebSocket2021Listener("<strong>WebSocket2021Listener</strong><br>WebSocket2021Listener")
|
||||||
|
WebSocket2021Listener --> WebSocket2021ListenerArgs
|
||||||
|
|
||||||
|
subgraph WebSocket2021ListenerArgs[" "]
|
||||||
|
direction LR
|
||||||
|
SubscriptionStorage("<strong>SubscriptionStorage</strong><br>SubscriptionStorage")
|
||||||
|
SequenceHandler("<br>SequenceHandler")
|
||||||
|
end
|
||||||
|
|
||||||
|
SequenceHandler --> SequenceHandlerArgs
|
||||||
|
|
||||||
|
subgraph SequenceHandlerArgs[" "]
|
||||||
|
direction TB
|
||||||
|
WebSocket2021Storer("<strong>WebSocket2021Storer</strong><br>WebSocket2021Storer")
|
||||||
|
WebSocket2021StateHandler("<strong>WebSocket2021StateHandler</strong><br>BaseStateHandler")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
To detect and store WebSocket connections, the `WebSocket2021Listener` is added as a listener to the HTTP server.
|
||||||
|
For all WebSocket connections that get opened, it verifies if they correspond to an existing subscription.
|
||||||
|
If yes the information gets sent out to its stored `WebSocket2021Handler`.
|
||||||
|
|
||||||
|
In this case this is a `SequenceHandler` which contains a `WebSocket2021Storer` and a `BaseStateHandler`.
|
||||||
|
The `WebSocket2021Storer` will store the WebSocket in the same map used by the `WebSocket2021Emitter`,
|
||||||
|
so that class can later on emit events as mentioned above.
|
||||||
|
The state handler will make sure that a notification gets sent out if the subscription has a `state` feature request,
|
||||||
|
as defined in the notification specification.
|
@ -61,3 +61,4 @@ Below are the sections that go deeper into the features of the server and how th
|
|||||||
* [How the server is initialized and started](features/initialization.md)
|
* [How the server is initialized and started](features/initialization.md)
|
||||||
* [How HTTP requests are handled](features/http-handler.md)
|
* [How HTTP requests are handled](features/http-handler.md)
|
||||||
* [How the server handles a standard Solid request](features/protocol/overview.md)
|
* [How the server handles a standard Solid request](features/protocol/overview.md)
|
||||||
|
* [How Notification components are configured](features/notifications.md)
|
||||||
|
@ -96,6 +96,7 @@ nav:
|
|||||||
- Parsing: architecture/features/protocol/parsing.md
|
- Parsing: architecture/features/protocol/parsing.md
|
||||||
- Authorization: architecture/features/protocol/authorization.md
|
- Authorization: architecture/features/protocol/authorization.md
|
||||||
- Resource Store: architecture/features/protocol/resource-store.md
|
- Resource Store: architecture/features/protocol/resource-store.md
|
||||||
|
- Notifications: architecture/features/notifications.md
|
||||||
- Contributing:
|
- Contributing:
|
||||||
- Pull requests: contributing/making-changes.md
|
- Pull requests: contributing/making-changes.md
|
||||||
- Releases: contributing/release.md
|
- Releases: contributing/release.md
|
||||||
|
Loading…
x
Reference in New Issue
Block a user