mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Replace WebSocketSubscription2021 with WebSocketChannel2023
This commit is contained in:
parent
cbbb10afa1
commit
702e8f5f59
@ -2,21 +2,21 @@
|
|||||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^6.0.0/components/context.jsonld",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^6.0.0/components/context.jsonld",
|
||||||
"@graph": [
|
"@graph": [
|
||||||
{
|
{
|
||||||
"comment": "Handles the generation and serialization of notifications for WebSocketSubscription2021.",
|
"comment": "Handles the generation and serialization of notifications for WebSocketChannel2023.",
|
||||||
"@id": "urn:solid-server:default:WebSocket2021NotificationHandler",
|
"@id": "urn:solid-server:default:WebSocket2023NotificationHandler",
|
||||||
"@type": "TypedNotificationHandler",
|
"@type": "TypedNotificationHandler",
|
||||||
"type": "http://www.w3.org/ns/solid/notifications#WebSocketSubscription2021",
|
"type": "http://www.w3.org/ns/solid/notifications#WebSocketChannel2023",
|
||||||
"source": {
|
"source": {
|
||||||
"@type": "ComposedNotificationHandler",
|
"@type": "ComposedNotificationHandler",
|
||||||
"generator": { "@id": "urn:solid-server:default:BaseNotificationGenerator" },
|
"generator": { "@id": "urn:solid-server:default:BaseNotificationGenerator" },
|
||||||
"serializer": { "@id": "urn:solid-server:default:BaseNotificationSerializer" },
|
"serializer": { "@id": "urn:solid-server:default:BaseNotificationSerializer" },
|
||||||
"emitter": { "@id": "urn:solid-server:default:WebSocket2021Emitter" }
|
"emitter": { "@id": "urn:solid-server:default:WebSocket2023Emitter" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"comment": "Emits serialized notifications through WebSockets.",
|
"comment": "Emits serialized notifications through WebSockets.",
|
||||||
"@id": "urn:solid-server:default:WebSocket2021Emitter",
|
"@id": "urn:solid-server:default:WebSocket2023Emitter",
|
||||||
"@type": "WebSocket2021Emitter",
|
"@type": "WebSocket2023Emitter",
|
||||||
"socketMap": { "@id": "urn:solid-server:default:WebSocketMap" }
|
"socketMap": { "@id": "urn:solid-server:default:WebSocketMap" }
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"@id": "urn:solid-server:default:NotificationHandler",
|
"@id": "urn:solid-server:default:NotificationHandler",
|
||||||
"@type": "WaterfallHandler",
|
"@type": "WaterfallHandler",
|
||||||
"handlers": [
|
"handlers": [
|
||||||
{ "@id": "urn:solid-server:default:WebSocket2021NotificationHandler" }
|
{ "@id": "urn:solid-server:default:WebSocket2023NotificationHandler" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
"@graph": [
|
"@graph": [
|
||||||
{
|
{
|
||||||
"comment": "Catches newly opened WebSockets and verifies if they belong to a subscription.",
|
"comment": "Catches newly opened WebSockets and verifies if they belong to a subscription.",
|
||||||
"@id": "urn:solid-server:default:WebSocket2021Listener",
|
"@id": "urn:solid-server:default:WebSocket2023Listener",
|
||||||
"@type": "WebSocket2021Listener",
|
"@type": "WebSocket2023Listener",
|
||||||
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" },
|
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" },
|
||||||
"route": { "@id": "urn:solid-server:default:WebSocket2021Route" },
|
"route": { "@id": "urn:solid-server:default:WebSocket2023Route" },
|
||||||
"handler": {
|
"handler": {
|
||||||
"@type": "SequenceHandler",
|
"@type": "SequenceHandler",
|
||||||
"handlers": [
|
"handlers": [
|
||||||
{ "@id": "urn:solid-server:default:WebSocket2021Storer" },
|
{ "@id": "urn:solid-server:default:WebSocket2023Storer" },
|
||||||
{ "@id": "urn:solid-server:default:WebSocket2021StateHandler" }
|
{ "@id": "urn:solid-server:default:WebSocket2023StateHandler" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -22,16 +22,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"comment": "Stores the opened WebSockets for reuse.",
|
"comment": "Stores the opened WebSockets for reuse.",
|
||||||
"@id": "urn:solid-server:default:WebSocket2021Storer",
|
"@id": "urn:solid-server:default:WebSocket2023Storer",
|
||||||
"@type": "WebSocket2021Storer",
|
"@type": "WebSocket2023Storer",
|
||||||
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" },
|
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" },
|
||||||
"socketMap": { "@id": "urn:solid-server:default:WebSocketMap" }
|
"socketMap": { "@id": "urn:solid-server:default:WebSocketMap" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"comment": "Handles the state feature of a WebSocketSubscription2021 subscription.",
|
"comment": "Handles the state feature of a WebSocketChannel2023 subscription.",
|
||||||
"@id": "urn:solid-server:default:WebSocket2021StateHandler",
|
"@id": "urn:solid-server:default:WebSocket2023StateHandler",
|
||||||
"@type": "BaseStateHandler",
|
"@type": "BaseStateHandler",
|
||||||
"handler": { "@id": "urn:solid-server:default:WebSocket2021NotificationHandler" },
|
"handler": { "@id": "urn:solid-server:default:WebSocket2023NotificationHandler" },
|
||||||
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" }
|
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" }
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"@id": "urn:solid-server:default:ServerConfigurator",
|
"@id": "urn:solid-server:default:ServerConfigurator",
|
||||||
"@type": "ParallelHandler",
|
"@type": "ParallelHandler",
|
||||||
"handlers": [
|
"handlers": [
|
||||||
{ "@id": "urn:solid-server:default:WebSocket2021Listener" }
|
{ "@id": "urn:solid-server:default:WebSocket2023Listener" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^6.0.0/components/context.jsonld",
|
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^6.0.0/components/context.jsonld",
|
||||||
"@graph": [
|
"@graph": [
|
||||||
{
|
{
|
||||||
"comment": "Handles the subscriptions targeting a WebSocketSubscription2021.",
|
"comment": "Handles the subscriptions targeting a WebSocketChannel2023.",
|
||||||
"@id": "urn:solid-server:default:WebSocket2021Subscriber",
|
"@id": "urn:solid-server:default:WebSocket2023Subscriber",
|
||||||
"@type": "OperationRouterHandler",
|
"@type": "OperationRouterHandler",
|
||||||
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
|
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
|
||||||
"allowedMethods": [ "HEAD", "GET", "POST" ],
|
"allowedMethods": [ "HEAD", "GET", "POST" ],
|
||||||
"allowedPathNames": [ "/WebSocketSubscription2021/$" ],
|
"allowedPathNames": [ "/WebSocketChannel2023/$" ],
|
||||||
"handler": {
|
"handler": {
|
||||||
"@type": "NotificationSubscriber",
|
"@type": "NotificationSubscriber",
|
||||||
"channelType": { "@id": "urn:solid-server:default:WebSocketSubscription2021" },
|
"channelType": { "@id": "urn:solid-server:default:WebSocketChannel2023Type" },
|
||||||
"converter": { "@id": "urn:solid-server:default:RepresentationConverter" },
|
"converter": { "@id": "urn:solid-server:default:RepresentationConverter" },
|
||||||
"credentialsExtractor": { "@id": "urn:solid-server:default:CredentialsExtractor" },
|
"credentialsExtractor": { "@id": "urn:solid-server:default:CredentialsExtractor" },
|
||||||
"permissionReader": { "@id": "urn:solid-server:default:PermissionReader" },
|
"permissionReader": { "@id": "urn:solid-server:default:PermissionReader" },
|
||||||
@ -19,23 +19,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"@id": "urn:solid-server:default:WebSocket2021Route",
|
"@id": "urn:solid-server:default:WebSocket2023Route",
|
||||||
"@type": "RelativePathInteractionRoute",
|
"@type": "RelativePathInteractionRoute",
|
||||||
"base": { "@id": "urn:solid-server:default:NotificationRoute" },
|
"base": { "@id": "urn:solid-server:default:NotificationRoute" },
|
||||||
"relativePath": "/WebSocketSubscription2021/"
|
"relativePath": "/WebSocketChannel2023/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"comment": "Contains all the metadata relevant for a WebSocketSubscription2021.",
|
"comment": "Contains all the metadata relevant for a WebSocketChannel2023.",
|
||||||
"@id": "urn:solid-server:default:WebSocketSubscription2021",
|
"@id": "urn:solid-server:default:WebSocketChannel2023Type",
|
||||||
"@type": "WebSocketSubscription2021",
|
"@type": "WebSocketChannel2023Type",
|
||||||
"route": { "@id": "urn:solid-server:default:WebSocket2021Route" }
|
"route": { "@id": "urn:solid-server:default:WebSocket2023Route" }
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"@id": "urn:solid-server:default:NotificationTypeHandler",
|
"@id": "urn:solid-server:default:NotificationTypeHandler",
|
||||||
"@type": "WaterfallHandler",
|
"@type": "WaterfallHandler",
|
||||||
"handlers": [
|
"handlers": [
|
||||||
{ "@id": "urn:solid-server:default:WebSocket2021Subscriber" }
|
{ "@id": "urn:solid-server:default:WebSocket2023Subscriber" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -44,7 +44,7 @@
|
|||||||
"@type": "NotificationDescriber",
|
"@type": "NotificationDescriber",
|
||||||
"subscriptions": [
|
"subscriptions": [
|
||||||
{
|
{
|
||||||
"@id": "urn:solid-server:default:WebSocketSubscription2021"
|
"@id": "urn:solid-server:default:WebSocketChannel2023Type"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -96,15 +96,14 @@ It will pull the relevant subscriptions from the storage and call the stored `No
|
|||||||
For every subscription type, a `NotificationHandler` should be added to the `WaterfallHandler`
|
For every subscription type, a `NotificationHandler` should be added to the `WaterfallHandler`
|
||||||
that handles notifications for the specific type.
|
that handles notifications for the specific type.
|
||||||
|
|
||||||
## WebSocketSubscription2021
|
## WebSocketChannel2023
|
||||||
|
|
||||||
To add support for [WebSocketSubscription2021](https://solidproject.org/TR/2022/websocket-subscription-2021-20220509)
|
To add support for [WebSocketChannel2023](https://solid.github.io/notifications/websocket-channel-2023) notifications,
|
||||||
notifications,
|
|
||||||
components were added as described in the documentation above.
|
components were added as described in the documentation above.
|
||||||
|
|
||||||
For discovery, a `NotificationDescriber` was added with the corresponding settings.
|
For discovery, a `NotificationDescriber` was added with the corresponding settings.
|
||||||
|
|
||||||
As `SubscriptionType`, there is a specific `WebSocketSubscription2021` that contains all the necessary information.
|
As `NotificationChannelType`, there is a specific `WebSocketChannel2023Type` that contains all the necessary information.
|
||||||
|
|
||||||
### Handling notifications
|
### Handling notifications
|
||||||
|
|
||||||
@ -120,13 +119,13 @@ flowchart TB
|
|||||||
direction LR
|
direction LR
|
||||||
BaseNotificationGenerator("<strong>BaseNotificationGenerator</strong><br><i>NotificationGenerator</i>")
|
BaseNotificationGenerator("<strong>BaseNotificationGenerator</strong><br><i>NotificationGenerator</i>")
|
||||||
BaseNotificationSerializer("<strong>BaseNotificationSerializer</strong><br><i>NotificationSerializer</i>")
|
BaseNotificationSerializer("<strong>BaseNotificationSerializer</strong><br><i>NotificationSerializer</i>")
|
||||||
WebSocket2021Emitter("<strong>WebSocket2021Emitter</strong><br>WebSocket2021Emitter")
|
WebSocket2023Emitter("<strong>WebSocket2023Emitter</strong><br>WebSocket2023Emitter")
|
||||||
BaseNotificationGenerator --> BaseNotificationSerializer --> WebSocket2021Emitter
|
BaseNotificationGenerator --> BaseNotificationSerializer --> WebSocket2023Emitter
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
A `TypedNotificationHandler` is a handler that can be used to filter out subscriptions for a specific type,
|
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.
|
making sure only WebSocketChannel2023 subscriptions will be handled.
|
||||||
|
|
||||||
A `ComposedNotificationHandler` combines 3 interfaces to handle the notifications:
|
A `ComposedNotificationHandler` combines 3 interfaces to handle the notifications:
|
||||||
|
|
||||||
@ -140,17 +139,17 @@ 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
|
`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.
|
and handles any necessary content negotiation based on the `accept` notification feature.
|
||||||
|
|
||||||
A `WebSocket2021Emitter` is a specific emitter that checks
|
A `WebSocket2023Emitter` is a specific emitter that checks
|
||||||
whether the current open WebSockets correspond to the subscription.
|
whether the current open WebSockets correspond to the subscription.
|
||||||
|
|
||||||
### WebSockets
|
### WebSockets
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
WebSocket2021Listener("<strong>WebSocket2021Listener</strong><br>WebSocket2021Listener")
|
WebSocket2023Listener("<strong>WebSocket2023Listener</strong><br>WebSocket2023Listener")
|
||||||
WebSocket2021Listener --> WebSocket2021ListenerArgs
|
WebSocket2023Listener --> WebSocket2023ListenerArgs
|
||||||
|
|
||||||
subgraph WebSocket2021ListenerArgs[" "]
|
subgraph WebSocket2023ListenerArgs[" "]
|
||||||
direction LR
|
direction LR
|
||||||
NotificationChannelStorage("<strong>NotificationChannelStorage</strong><br>NotificationChannelStorage")
|
NotificationChannelStorage("<strong>NotificationChannelStorage</strong><br>NotificationChannelStorage")
|
||||||
SequenceHandler("<br>SequenceHandler")
|
SequenceHandler("<br>SequenceHandler")
|
||||||
@ -160,17 +159,17 @@ flowchart TB
|
|||||||
|
|
||||||
subgraph SequenceHandlerArgs[" "]
|
subgraph SequenceHandlerArgs[" "]
|
||||||
direction TB
|
direction TB
|
||||||
WebSocket2021Storer("<strong>WebSocket2021Storer</strong><br>WebSocket2021Storer")
|
WebSocket2023Storer("<strong>WebSocket2023Storer</strong><br>WebSocket2023Storer")
|
||||||
WebSocket2021StateHandler("<strong>WebSocket2021StateHandler</strong><br>BaseStateHandler")
|
WebSocket2023StateHandler("<strong>WebSocket2023StateHandler</strong><br>BaseStateHandler")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
To detect and store WebSocket connections, the `WebSocket2021Listener` is added as a listener to the HTTP server.
|
To detect and store WebSocket connections, the `WebSocket2023Listener` is added as a listener to the HTTP server.
|
||||||
For all WebSocket connections that get opened, it verifies whether they correspond to an existing subscription.
|
For all WebSocket connections that get opened, it verifies whether they correspond to an existing subscription.
|
||||||
If yes, the information gets sent out to its stored `WebSocket2021Handler`.
|
If yes, the information gets sent out to its stored `WebSocket2023Handler`.
|
||||||
|
|
||||||
In this case, this is a `SequenceHandler`, which contains a `WebSocket2021Storer` and a `BaseStateHandler`.
|
In this case, this is a `SequenceHandler`, which contains a `WebSocket2023Storer` and a `BaseStateHandler`.
|
||||||
The `WebSocket2021Storer` will store the WebSocket in the same map used by the `WebSocket2021Emitter`,
|
The `WebSocket2023Storer` will store the WebSocket in the same map used by the `WebSocket2023Emitter`,
|
||||||
so that class can emit events later on, as mentioned above.
|
so that class can emit events later on, as mentioned above.
|
||||||
The state handler will make sure that a notification gets sent out if the subscription has a `state` feature request,
|
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.
|
as defined in the notification specification.
|
||||||
@ -179,7 +178,7 @@ as defined in the notification specification.
|
|||||||
|
|
||||||
The additions required to support
|
The additions required to support
|
||||||
[WebHookSubscription2021](https://github.com/solid/notifications/blob/main/webhook-subscription-2021.md)
|
[WebHookSubscription2021](https://github.com/solid/notifications/blob/main/webhook-subscription-2021.md)
|
||||||
are quite similar to those needed for WebSocketSubscription2021:
|
are quite similar to those needed for WebSocketChannel2023:
|
||||||
|
|
||||||
* For discovery, there is a `WebHookDescriber`, which is an extension of a `NotificationDescriber`.
|
* For discovery, there is a `WebHookDescriber`, which is an extension of a `NotificationDescriber`.
|
||||||
* The `WebHookSubscription2021` class contains all the necessary typing information.
|
* The `WebHookSubscription2021` class contains all the necessary typing information.
|
||||||
|
16
src/index.ts
16
src/index.ts
@ -326,14 +326,14 @@ export * from './server/notifications/WebHookSubscription2021/WebHookSubscriptio
|
|||||||
export * from './server/notifications/WebHookSubscription2021/WebHookUnsubscriber';
|
export * from './server/notifications/WebHookSubscription2021/WebHookUnsubscriber';
|
||||||
export * from './server/notifications/WebHookSubscription2021/WebHookWebId';
|
export * from './server/notifications/WebHookSubscription2021/WebHookWebId';
|
||||||
|
|
||||||
// Server/Notifications/WebSocketSubscription2021
|
// Server/Notifications/WebSocketChannel2023
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Emitter';
|
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Emitter';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Handler';
|
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Handler';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Listener';
|
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Listener';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Storer';
|
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Storer';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Util';
|
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Util';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocketMap';
|
export * from './server/notifications/WebSocketChannel2023/WebSocketMap';
|
||||||
export * from './server/notifications/WebSocketSubscription2021/WebSocketSubscription2021';
|
export * from './server/notifications/WebSocketChannel2023/WebSocketChannel2023Type';
|
||||||
|
|
||||||
// Server/Notifications
|
// Server/Notifications
|
||||||
export * from './server/notifications/ActivityEmitter';
|
export * from './server/notifications/ActivityEmitter';
|
||||||
|
@ -6,11 +6,11 @@ import { NotificationEmitter } from '../NotificationEmitter';
|
|||||||
import type { NotificationEmitterInput } from '../NotificationEmitter';
|
import type { NotificationEmitterInput } from '../NotificationEmitter';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits notifications on WebSocketSubscription2021 subscription.
|
* Emits notifications on WebSocketChannel2023 subscription.
|
||||||
* Uses the WebSockets found in the provided map.
|
* Uses the WebSockets found in the provided map.
|
||||||
* The key should be the identifier of the matching channel.
|
* The key should be the identifier of the matching channel.
|
||||||
*/
|
*/
|
||||||
export class WebSocket2021Emitter extends NotificationEmitter {
|
export class WebSocket2023Emitter extends NotificationEmitter {
|
||||||
protected readonly logger = getLoggerFor(this);
|
protected readonly logger = getLoggerFor(this);
|
||||||
|
|
||||||
private readonly socketMap: SetMultiMap<string, WebSocket>;
|
private readonly socketMap: SetMultiMap<string, WebSocket>;
|
@ -2,12 +2,12 @@ import type { WebSocket } from 'ws';
|
|||||||
import { AsyncHandler } from '../../../util/handlers/AsyncHandler';
|
import { AsyncHandler } from '../../../util/handlers/AsyncHandler';
|
||||||
import type { NotificationChannel } from '../NotificationChannel';
|
import type { NotificationChannel } from '../NotificationChannel';
|
||||||
|
|
||||||
export interface WebSocket2021HandlerInput {
|
export interface WebSocket2023HandlerInput {
|
||||||
channel: NotificationChannel;
|
channel: NotificationChannel;
|
||||||
webSocket: WebSocket;
|
webSocket: WebSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A handler that is called when a valid WebSocketSubscription2021 connection has been made.
|
* A handler that is called when a valid WebSocketChannel2023 connection has been made.
|
||||||
*/
|
*/
|
||||||
export abstract class WebSocket2021Handler extends AsyncHandler<WebSocket2021HandlerInput> {}
|
export abstract class WebSocket2023Handler extends AsyncHandler<WebSocket2023HandlerInput> {}
|
@ -4,21 +4,21 @@ import type { InteractionRoute } from '../../../identity/interaction/routing/Int
|
|||||||
import { getLoggerFor } from '../../../logging/LogUtil';
|
import { getLoggerFor } from '../../../logging/LogUtil';
|
||||||
import { WebSocketServerConfigurator } from '../../WebSocketServerConfigurator';
|
import { WebSocketServerConfigurator } from '../../WebSocketServerConfigurator';
|
||||||
import type { NotificationChannelStorage } from '../NotificationChannelStorage';
|
import type { NotificationChannelStorage } from '../NotificationChannelStorage';
|
||||||
import type { WebSocket2021Handler } from './WebSocket2021Handler';
|
import type { WebSocket2023Handler } from './WebSocket2023Handler';
|
||||||
import { parseWebSocketRequest } from './WebSocket2021Util';
|
import { parseWebSocketRequest } from './WebSocket2023Util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens for WebSocket connections and verifies if they are valid WebSocketSubscription2021 connections,
|
* Listens for WebSocket connections and verifies if they are valid WebSocketChannel2023 connections,
|
||||||
* in which case its {@link WebSocket2021Handler} will be alerted.
|
* in which case its {@link WebSocket2023Handler} will be alerted.
|
||||||
*/
|
*/
|
||||||
export class WebSocket2021Listener extends WebSocketServerConfigurator {
|
export class WebSocket2023Listener extends WebSocketServerConfigurator {
|
||||||
protected readonly logger = getLoggerFor(this);
|
protected readonly logger = getLoggerFor(this);
|
||||||
|
|
||||||
private readonly storage: NotificationChannelStorage;
|
private readonly storage: NotificationChannelStorage;
|
||||||
private readonly handler: WebSocket2021Handler;
|
private readonly handler: WebSocket2023Handler;
|
||||||
private readonly path: string;
|
private readonly path: string;
|
||||||
|
|
||||||
public constructor(storage: NotificationChannelStorage, handler: WebSocket2021Handler, route: InteractionRoute) {
|
public constructor(storage: NotificationChannelStorage, handler: WebSocket2023Handler, route: InteractionRoute) {
|
||||||
super();
|
super();
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
@ -3,11 +3,11 @@ import { getLoggerFor } from '../../../logging/LogUtil';
|
|||||||
import type { SetMultiMap } from '../../../util/map/SetMultiMap';
|
import type { SetMultiMap } from '../../../util/map/SetMultiMap';
|
||||||
import { setSafeInterval } from '../../../util/TimerUtil';
|
import { setSafeInterval } from '../../../util/TimerUtil';
|
||||||
import type { NotificationChannelStorage } from '../NotificationChannelStorage';
|
import type { NotificationChannelStorage } from '../NotificationChannelStorage';
|
||||||
import type { WebSocket2021HandlerInput } from './WebSocket2021Handler';
|
import type { WebSocket2023HandlerInput } from './WebSocket2023Handler';
|
||||||
import { WebSocket2021Handler } from './WebSocket2021Handler';
|
import { WebSocket2023Handler } from './WebSocket2023Handler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of the WebSockets that were opened for a WebSocketSubscription2021 channel.
|
* Keeps track of the WebSockets that were opened for a WebSocketChannel2023 channel.
|
||||||
* The WebSockets are stored in the map using the identifier of the matching channel.
|
* The WebSockets are stored in the map using the identifier of the matching channel.
|
||||||
*
|
*
|
||||||
* `cleanupTimer` defines in minutes how often the stored WebSockets are closed
|
* `cleanupTimer` defines in minutes how often the stored WebSockets are closed
|
||||||
@ -15,7 +15,7 @@ import { WebSocket2021Handler } from './WebSocket2021Handler';
|
|||||||
* Defaults to 60 minutes.
|
* Defaults to 60 minutes.
|
||||||
* Open WebSockets will not receive notifications if their channel expired.
|
* Open WebSockets will not receive notifications if their channel expired.
|
||||||
*/
|
*/
|
||||||
export class WebSocket2021Storer extends WebSocket2021Handler {
|
export class WebSocket2023Storer extends WebSocket2023Handler {
|
||||||
protected readonly logger = getLoggerFor(this);
|
protected readonly logger = getLoggerFor(this);
|
||||||
|
|
||||||
private readonly storage: NotificationChannelStorage;
|
private readonly storage: NotificationChannelStorage;
|
||||||
@ -34,7 +34,7 @@ export class WebSocket2021Storer extends WebSocket2021Handler {
|
|||||||
timer.unref();
|
timer.unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle({ webSocket, channel }: WebSocket2021HandlerInput): Promise<void> {
|
public async handle({ webSocket, channel }: WebSocket2023HandlerInput): Promise<void> {
|
||||||
this.socketMap.add(channel.id, webSocket);
|
this.socketMap.add(channel.id, webSocket);
|
||||||
webSocket.on('error', (): boolean => this.socketMap.deleteEntry(channel.id, webSocket));
|
webSocket.on('error', (): boolean => this.socketMap.deleteEntry(channel.id, webSocket));
|
||||||
webSocket.on('close', (): boolean => this.socketMap.deleteEntry(channel.id, webSocket));
|
webSocket.on('close', (): boolean => this.socketMap.deleteEntry(channel.id, webSocket));
|
@ -5,45 +5,45 @@ import { getLoggerFor } from '../../../logging/LogUtil';
|
|||||||
import { NOTIFY } from '../../../util/Vocabularies';
|
import { NOTIFY } from '../../../util/Vocabularies';
|
||||||
import { BaseChannelType } from '../BaseChannelType';
|
import { BaseChannelType } from '../BaseChannelType';
|
||||||
import type { NotificationChannel } from '../NotificationChannel';
|
import type { NotificationChannel } from '../NotificationChannel';
|
||||||
import { generateWebSocketUrl } from './WebSocket2021Util';
|
import { generateWebSocketUrl } from './WebSocket2023Util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link NotificationChannel} containing the necessary fields for a WebSocketSubscription2021 channel.
|
* A {@link NotificationChannel} containing the necessary fields for a WebSocketChannel2023 channel.
|
||||||
*/
|
*/
|
||||||
export interface WebSocketSubscription2021Channel extends NotificationChannel {
|
export interface WebSocketChannel2023 extends NotificationChannel {
|
||||||
/**
|
/**
|
||||||
* The "notify:WebSocketSubscription2021" type.
|
* The "notify:WebSocketChannel2023" type.
|
||||||
*/
|
*/
|
||||||
type: typeof NOTIFY.WebSocketSubscription2021;
|
type: typeof NOTIFY.WebSocketChannel2023;
|
||||||
/**
|
/**
|
||||||
* The WebSocket through which the channel will send notifications.
|
* The WebSocket through which the channel will send notifications.
|
||||||
*/
|
*/
|
||||||
source: string;
|
receiveFrom: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isWebSocket2021Channel(channel: NotificationChannel): channel is WebSocketSubscription2021Channel {
|
export function isWebSocket2023Channel(channel: NotificationChannel): channel is WebSocketChannel2023 {
|
||||||
return channel.type === NOTIFY.WebSocketSubscription2021;
|
return channel.type === NOTIFY.WebSocketChannel2023;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The notification channel type WebSocketSubscription2021 as described in
|
* The notification channel type WebSocketChannel2023 as described in
|
||||||
* https://solidproject.org/TR/websocket-subscription-2021
|
* https://solidproject.org/TR/websocket-subscription-2021
|
||||||
*
|
*
|
||||||
* Requires read permissions on a resource to be able to receive notifications.
|
* Requires read permissions on a resource to be able to receive notifications.
|
||||||
*/
|
*/
|
||||||
export class WebSocketSubscription2021 extends BaseChannelType {
|
export class WebSocketChannel2023Type extends BaseChannelType {
|
||||||
protected readonly logger = getLoggerFor(this);
|
protected readonly logger = getLoggerFor(this);
|
||||||
|
|
||||||
public constructor(route: InteractionRoute, features?: string[]) {
|
public constructor(route: InteractionRoute, features?: string[]) {
|
||||||
super(NOTIFY.terms.WebSocketSubscription2021, route, features);
|
super(NOTIFY.terms.WebSocketChannel2023, route, features);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initChannel(data: Store, credentials: Credentials): Promise<WebSocketSubscription2021Channel> {
|
public async initChannel(data: Store, credentials: Credentials): Promise<WebSocketChannel2023> {
|
||||||
const channel = await super.initChannel(data, credentials);
|
const channel = await super.initChannel(data, credentials);
|
||||||
return {
|
return {
|
||||||
...channel,
|
...channel,
|
||||||
type: NOTIFY.WebSocketSubscription2021,
|
type: NOTIFY.WebSocketChannel2023,
|
||||||
source: generateWebSocketUrl(this.path, channel.id),
|
receiveFrom: generateWebSocketUrl(this.path, channel.id),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -197,6 +197,7 @@ export const NOTIFY = createVocabulary('http://www.w3.org/ns/solid/notifications
|
|||||||
'endAt',
|
'endAt',
|
||||||
'feature',
|
'feature',
|
||||||
'rate',
|
'rate',
|
||||||
|
'receiveFrom',
|
||||||
'startAt',
|
'startAt',
|
||||||
'state',
|
'state',
|
||||||
'subscription',
|
'subscription',
|
||||||
@ -206,7 +207,7 @@ export const NOTIFY = createVocabulary('http://www.w3.org/ns/solid/notifications
|
|||||||
'webid',
|
'webid',
|
||||||
|
|
||||||
'WebHookSubscription2021',
|
'WebHookSubscription2021',
|
||||||
'WebSocketSubscription2021',
|
'WebSocketChannel2023',
|
||||||
);
|
);
|
||||||
|
|
||||||
export const OIDC = createVocabulary('http://www.w3.org/ns/solid/oidc#',
|
export const OIDC = createVocabulary('http://www.w3.org/ns/solid/oidc#',
|
||||||
|
@ -14,17 +14,16 @@ import {
|
|||||||
getPresetConfigPath,
|
getPresetConfigPath,
|
||||||
getTestConfigPath,
|
getTestConfigPath,
|
||||||
getTestFolder,
|
getTestFolder,
|
||||||
instantiateFromConfig,
|
instantiateFromConfig, removeFolder,
|
||||||
removeFolder,
|
|
||||||
} from './Config';
|
} from './Config';
|
||||||
import quad = DataFactory.quad;
|
import quad = DataFactory.quad;
|
||||||
import namedNode = DataFactory.namedNode;
|
import namedNode = DataFactory.namedNode;
|
||||||
|
|
||||||
const port = getPort('WebSocketSubscription2021');
|
const port = getPort('WebSocketChannel2023');
|
||||||
const baseUrl = `http://localhost:${port}/`;
|
const baseUrl = `http://localhost:${port}/`;
|
||||||
const notificationType = NOTIFY.WebSocketSubscription2021;
|
const notificationType = NOTIFY.WebSocketChannel2023;
|
||||||
|
|
||||||
const rootFilePath = getTestFolder('WebSocketSubscription2021');
|
const rootFilePath = getTestFolder('WebSocketChannel2023');
|
||||||
const stores: [string, any][] = [
|
const stores: [string, any][] = [
|
||||||
[ 'in-memory storage', {
|
[ 'in-memory storage', {
|
||||||
configs: [ 'storage/backend/memory.json', 'util/resource-locker/memory.json' ],
|
configs: [ 'storage/backend/memory.json', 'util/resource-locker/memory.json' ],
|
||||||
@ -37,7 +36,7 @@ const stores: [string, any][] = [
|
|||||||
}],
|
}],
|
||||||
];
|
];
|
||||||
|
|
||||||
describe.each(stores)('A server supporting WebSocketSubscription2021 using %s', (name, { configs, teardown }): void => {
|
describe.each(stores)('A server supporting WebSocketChannel2023 using %s', (name, { configs, teardown }): void => {
|
||||||
let app: App;
|
let app: App;
|
||||||
let store: ResourceStore;
|
let store: ResourceStore;
|
||||||
const webId = 'http://example.com/card/#me';
|
const webId = 'http://example.com/card/#me';
|
||||||
@ -89,7 +88,7 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
|
|||||||
// Find the notification channel for websockets
|
// Find the notification channel for websockets
|
||||||
const subscriptions = quads.getObjects(storageDescriptionUrl, NOTIFY.terms.subscription, null);
|
const subscriptions = quads.getObjects(storageDescriptionUrl, NOTIFY.terms.subscription, null);
|
||||||
const websocketSubscriptions = subscriptions.filter((channel): boolean => quads.has(
|
const websocketSubscriptions = subscriptions.filter((channel): boolean => quads.has(
|
||||||
quad(channel as NamedNode, NOTIFY.terms.channelType, namedNode(`${NOTIFY.namespace}WebSocketSubscription2021`)),
|
quad(channel as NamedNode, NOTIFY.terms.channelType, namedNode(`${NOTIFY.namespace}WebSocketChannel2023`)),
|
||||||
));
|
));
|
||||||
expect(websocketSubscriptions).toHaveLength(1);
|
expect(websocketSubscriptions).toHaveLength(1);
|
||||||
subscriptionUrl = websocketSubscriptions[0].value;
|
subscriptionUrl = websocketSubscriptions[0].value;
|
||||||
@ -97,7 +96,7 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
|
|||||||
|
|
||||||
it('supports subscribing.', async(): Promise<void> => {
|
it('supports subscribing.', async(): Promise<void> => {
|
||||||
const response = await subscribe(notificationType, webId, subscriptionUrl, topic);
|
const response = await subscribe(notificationType, webId, subscriptionUrl, topic);
|
||||||
webSocketUrl = (response as any).source;
|
webSocketUrl = (response as any).receiveFrom;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits Created events.', async(): Promise<void> => {
|
it('emits Created events.', async(): Promise<void> => {
|
||||||
@ -167,7 +166,7 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
|
|||||||
|
|
||||||
const channel = {
|
const channel = {
|
||||||
'@context': [ 'https://www.w3.org/ns/solid/notification/v1' ],
|
'@context': [ 'https://www.w3.org/ns/solid/notification/v1' ],
|
||||||
type: NOTIFY.WebSocketSubscription2021,
|
type: notificationType,
|
||||||
topic: restricted,
|
topic: restricted,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -199,9 +198,9 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
|
|||||||
});
|
});
|
||||||
expect(response.status).toBe(201);
|
expect(response.status).toBe(201);
|
||||||
|
|
||||||
const { source } = await subscribe(notificationType, webId, subscriptionUrl, topic, { state: 'abc' }) as any;
|
const { receiveFrom } = await subscribe(notificationType, webId, subscriptionUrl, topic, { state: 'abc' }) as any;
|
||||||
|
|
||||||
const socket = new WebSocket(source);
|
const socket = new WebSocket(receiveFrom);
|
||||||
const notificationPromise = new Promise<Buffer>((resolve): any => socket.on('message', resolve));
|
const notificationPromise = new Promise<Buffer>((resolve): any => socket.on('message', resolve));
|
||||||
await new Promise<void>((resolve): any => socket.on('open', resolve));
|
await new Promise<void>((resolve): any => socket.on('open', resolve));
|
||||||
|
|
||||||
@ -213,10 +212,10 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('removes expired channels.', async(): Promise<void> => {
|
it('removes expired channels.', async(): Promise<void> => {
|
||||||
const { source } =
|
const { receiveFrom } =
|
||||||
await subscribe(notificationType, webId, subscriptionUrl, topic, { endAt: '1988-03-09T14:48:00.000Z' }) as any;
|
await subscribe(notificationType, webId, subscriptionUrl, topic, { endAt: '1988-03-09T14:48:00.000Z' }) as any;
|
||||||
|
|
||||||
const socket = new WebSocket(source);
|
const socket = new WebSocket(receiveFrom);
|
||||||
const messagePromise = new Promise<Buffer>((resolve): any => socket.on('message', resolve));
|
const messagePromise = new Promise<Buffer>((resolve): any => socket.on('message', resolve));
|
||||||
await new Promise<void>((resolve): any => socket.on('close', resolve));
|
await new Promise<void>((resolve): any => socket.on('close', resolve));
|
||||||
|
|
||||||
@ -245,7 +244,8 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
|
|||||||
const parser = new Parser({ baseIRI: subscriptionUrl });
|
const parser = new Parser({ baseIRI: subscriptionUrl });
|
||||||
const quads = new Store(parser.parse(await response.text()));
|
const quads = new Store(parser.parse(await response.text()));
|
||||||
|
|
||||||
expect(quads.getObjects(null, RDF.terms.type, null)).toEqual([ NOTIFY.terms.WebSocketSubscription2021 ]);
|
expect(quads.getObjects(null, RDF.terms.type, null)).toEqual([ NOTIFY.terms.WebSocketChannel2023 ]);
|
||||||
expect(quads.getObjects(null, NOTIFY.terms.topic, null)).toEqual([ namedNode(topic) ]);
|
expect(quads.getObjects(null, NOTIFY.terms.topic, null)).toEqual([ namedNode(topic) ]);
|
||||||
|
expect(quads.countQuads(null, NOTIFY.terms.receiveFrom, null, null)).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -27,9 +27,9 @@ describe('A KeyValueChannelStorage', (): void => {
|
|||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
resetAllMocks();
|
resetAllMocks();
|
||||||
channel = {
|
channel = {
|
||||||
id: `WebSocketSubscription2021:${v4()}:http://example.com/foo`,
|
id: `WebSocketChannel2023:${v4()}:http://example.com/foo`,
|
||||||
topic,
|
topic,
|
||||||
type: 'WebSocketSubscription2021',
|
type: 'WebSocketChannel2023',
|
||||||
};
|
};
|
||||||
|
|
||||||
internalMap = new Map();
|
internalMap = new Map();
|
||||||
|
@ -3,12 +3,12 @@ import type { WebSocket } from 'ws';
|
|||||||
import { BasicRepresentation } from '../../../../../src/http/representation/BasicRepresentation';
|
import { BasicRepresentation } from '../../../../../src/http/representation/BasicRepresentation';
|
||||||
import type { NotificationChannel } from '../../../../../src/server/notifications/NotificationChannel';
|
import type { NotificationChannel } from '../../../../../src/server/notifications/NotificationChannel';
|
||||||
import {
|
import {
|
||||||
WebSocket2021Emitter,
|
WebSocket2023Emitter,
|
||||||
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Emitter';
|
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Emitter';
|
||||||
import type { SetMultiMap } from '../../../../../src/util/map/SetMultiMap';
|
import type { SetMultiMap } from '../../../../../src/util/map/SetMultiMap';
|
||||||
import { WrappedSetMultiMap } from '../../../../../src/util/map/WrappedSetMultiMap';
|
import { WrappedSetMultiMap } from '../../../../../src/util/map/WrappedSetMultiMap';
|
||||||
|
|
||||||
describe('A WebSocket2021Emitter', (): void => {
|
describe('A WebSocket2023Emitter', (): void => {
|
||||||
const channel: NotificationChannel = {
|
const channel: NotificationChannel = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
topic: 'http://example.com/foo',
|
topic: 'http://example.com/foo',
|
||||||
@ -17,7 +17,7 @@ describe('A WebSocket2021Emitter', (): void => {
|
|||||||
|
|
||||||
let webSocket: jest.Mocked<WebSocket>;
|
let webSocket: jest.Mocked<WebSocket>;
|
||||||
let socketMap: SetMultiMap<string, WebSocket>;
|
let socketMap: SetMultiMap<string, WebSocket>;
|
||||||
let emitter: WebSocket2021Emitter;
|
let emitter: WebSocket2023Emitter;
|
||||||
|
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
webSocket = new EventEmitter() as any;
|
webSocket = new EventEmitter() as any;
|
||||||
@ -26,7 +26,7 @@ describe('A WebSocket2021Emitter', (): void => {
|
|||||||
|
|
||||||
socketMap = new WrappedSetMultiMap();
|
socketMap = new WrappedSetMultiMap();
|
||||||
|
|
||||||
emitter = new WebSocket2021Emitter(socketMap);
|
emitter = new WebSocket2023Emitter(socketMap);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits notifications to the stored WebSockets.', async(): Promise<void> => {
|
it('emits notifications to the stored WebSockets.', async(): Promise<void> => {
|
@ -10,11 +10,11 @@ import type {
|
|||||||
NotificationChannelStorage,
|
NotificationChannelStorage,
|
||||||
} from '../../../../../src/server/notifications/NotificationChannelStorage';
|
} from '../../../../../src/server/notifications/NotificationChannelStorage';
|
||||||
import type {
|
import type {
|
||||||
WebSocket2021Handler,
|
WebSocket2023Handler,
|
||||||
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Handler';
|
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Handler';
|
||||||
import {
|
import {
|
||||||
WebSocket2021Listener,
|
WebSocket2023Listener,
|
||||||
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Listener';
|
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Listener';
|
||||||
import { flushPromises } from '../../../../util/Util';
|
import { flushPromises } from '../../../../util/Util';
|
||||||
|
|
||||||
jest.mock('ws', (): any => ({
|
jest.mock('ws', (): any => ({
|
||||||
@ -26,7 +26,7 @@ jest.mock('ws', (): any => ({
|
|||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('A WebSocket2021Listener', (): void => {
|
describe('A WebSocket2023Listener', (): void => {
|
||||||
const channel: NotificationChannel = {
|
const channel: NotificationChannel = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
topic: 'http://example.com/foo',
|
topic: 'http://example.com/foo',
|
||||||
@ -37,9 +37,9 @@ describe('A WebSocket2021Listener', (): void => {
|
|||||||
let webSocket: WebSocket;
|
let webSocket: WebSocket;
|
||||||
let upgradeRequest: HttpRequest;
|
let upgradeRequest: HttpRequest;
|
||||||
let storage: jest.Mocked<NotificationChannelStorage>;
|
let storage: jest.Mocked<NotificationChannelStorage>;
|
||||||
let handler: jest.Mocked<WebSocket2021Handler>;
|
let handler: jest.Mocked<WebSocket2023Handler>;
|
||||||
const route = new AbsolutePathInteractionRoute('http://example.com/foo');
|
const route = new AbsolutePathInteractionRoute('http://example.com/foo');
|
||||||
let listener: WebSocket2021Listener;
|
let listener: WebSocket2023Listener;
|
||||||
|
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
server = new EventEmitter() as any;
|
server = new EventEmitter() as any;
|
||||||
@ -57,7 +57,7 @@ describe('A WebSocket2021Listener', (): void => {
|
|||||||
handleSafe: jest.fn(),
|
handleSafe: jest.fn(),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
listener = new WebSocket2021Listener(storage, handler, route);
|
listener = new WebSocket2023Listener(storage, handler, route);
|
||||||
await listener.handle(server);
|
await listener.handle(server);
|
||||||
});
|
});
|
||||||
|
|
@ -6,13 +6,13 @@ import type {
|
|||||||
} from '../../../../../src/server/notifications/NotificationChannelStorage';
|
} from '../../../../../src/server/notifications/NotificationChannelStorage';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
WebSocket2021Storer,
|
WebSocket2023Storer,
|
||||||
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Storer';
|
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Storer';
|
||||||
import type { SetMultiMap } from '../../../../../src/util/map/SetMultiMap';
|
import type { SetMultiMap } from '../../../../../src/util/map/SetMultiMap';
|
||||||
import { WrappedSetMultiMap } from '../../../../../src/util/map/WrappedSetMultiMap';
|
import { WrappedSetMultiMap } from '../../../../../src/util/map/WrappedSetMultiMap';
|
||||||
import { flushPromises } from '../../../../util/Util';
|
import { flushPromises } from '../../../../util/Util';
|
||||||
|
|
||||||
describe('A WebSocket2021Storer', (): void => {
|
describe('A WebSocket2023Storer', (): void => {
|
||||||
const channel: NotificationChannel = {
|
const channel: NotificationChannel = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
topic: 'http://example.com/foo',
|
topic: 'http://example.com/foo',
|
||||||
@ -21,7 +21,7 @@ describe('A WebSocket2021Storer', (): void => {
|
|||||||
let webSocket: jest.Mocked<WebSocket>;
|
let webSocket: jest.Mocked<WebSocket>;
|
||||||
let storage: jest.Mocked<NotificationChannelStorage>;
|
let storage: jest.Mocked<NotificationChannelStorage>;
|
||||||
let socketMap: SetMultiMap<string, WebSocket>;
|
let socketMap: SetMultiMap<string, WebSocket>;
|
||||||
let storer: WebSocket2021Storer;
|
let storer: WebSocket2023Storer;
|
||||||
|
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
webSocket = new EventEmitter() as any;
|
webSocket = new EventEmitter() as any;
|
||||||
@ -33,7 +33,7 @@ describe('A WebSocket2021Storer', (): void => {
|
|||||||
|
|
||||||
socketMap = new WrappedSetMultiMap();
|
socketMap = new WrappedSetMultiMap();
|
||||||
|
|
||||||
storer = new WebSocket2021Storer(storage, socketMap);
|
storer = new WebSocket2023Storer(storage, socketMap);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('stores WebSockets.', async(): Promise<void> => {
|
it('stores WebSockets.', async(): Promise<void> => {
|
||||||
@ -60,7 +60,7 @@ describe('A WebSocket2021Storer', (): void => {
|
|||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
// Need to create class after fake timers have been enabled
|
// Need to create class after fake timers have been enabled
|
||||||
storer = new WebSocket2021Storer(storage, socketMap);
|
storer = new WebSocket2023Storer(storage, socketMap);
|
||||||
|
|
||||||
const webSocket2: jest.Mocked<WebSocket> = new EventEmitter() as any;
|
const webSocket2: jest.Mocked<WebSocket> = new EventEmitter() as any;
|
||||||
webSocket2.close = jest.fn();
|
webSocket2.close = jest.fn();
|
@ -1,9 +1,9 @@
|
|||||||
import type { IncomingMessage } from 'http';
|
import type { IncomingMessage } from 'http';
|
||||||
import {
|
import {
|
||||||
generateWebSocketUrl, parseWebSocketRequest,
|
generateWebSocketUrl, parseWebSocketRequest,
|
||||||
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Util';
|
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Util';
|
||||||
|
|
||||||
describe('WebSocket2021Util', (): void => {
|
describe('WebSocket2023Util', (): void => {
|
||||||
describe('#generateWebSocketUrl', (): void => {
|
describe('#generateWebSocketUrl', (): void => {
|
||||||
it('generates a WebSocket link with a query parameter.', async(): Promise<void> => {
|
it('generates a WebSocket link with a query parameter.', async(): Promise<void> => {
|
||||||
expect(generateWebSocketUrl('http://example.com/', '123456')).toBe('ws://example.com/?auth=123456');
|
expect(generateWebSocketUrl('http://example.com/', '123456')).toBe('ws://example.com/?auth=123456');
|
@ -6,14 +6,14 @@ import type { NotificationChannel } from '../../../../../src/server/notification
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
generateWebSocketUrl,
|
generateWebSocketUrl,
|
||||||
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Util';
|
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Util';
|
||||||
import type {
|
import type {
|
||||||
WebSocketSubscription2021Channel,
|
WebSocketChannel2023,
|
||||||
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocketSubscription2021';
|
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocketChannel2023Type';
|
||||||
import {
|
import {
|
||||||
isWebSocket2021Channel,
|
isWebSocket2023Channel,
|
||||||
WebSocketSubscription2021,
|
WebSocketChannel2023Type,
|
||||||
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocketSubscription2021';
|
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocketChannel2023Type';
|
||||||
import { NOTIFY, RDF } from '../../../../../src/util/Vocabularies';
|
import { NOTIFY, RDF } from '../../../../../src/util/Vocabularies';
|
||||||
import quad = DataFactory.quad;
|
import quad = DataFactory.quad;
|
||||||
import blankNode = DataFactory.blankNode;
|
import blankNode = DataFactory.blankNode;
|
||||||
@ -21,35 +21,35 @@ import namedNode = DataFactory.namedNode;
|
|||||||
|
|
||||||
jest.mock('uuid', (): any => ({ v4: (): string => '4c9b88c1-7502-4107-bb79-2a3a590c7aa3' }));
|
jest.mock('uuid', (): any => ({ v4: (): string => '4c9b88c1-7502-4107-bb79-2a3a590c7aa3' }));
|
||||||
|
|
||||||
describe('A WebSocketSubscription2021', (): void => {
|
describe('A WebSocketChannel2023', (): void => {
|
||||||
let data: Store;
|
let data: Store;
|
||||||
let channel: WebSocketSubscription2021Channel;
|
let channel: WebSocketChannel2023;
|
||||||
const subject = blankNode();
|
const subject = blankNode();
|
||||||
const topic = 'https://storage.example/resource';
|
const topic = 'https://storage.example/resource';
|
||||||
const route = new AbsolutePathInteractionRoute('http://example.com/foo');
|
const route = new AbsolutePathInteractionRoute('http://example.com/foo');
|
||||||
let channelType: WebSocketSubscription2021;
|
let channelType: WebSocketChannel2023Type;
|
||||||
|
|
||||||
beforeEach(async(): Promise<void> => {
|
beforeEach(async(): Promise<void> => {
|
||||||
data = new Store();
|
data = new Store();
|
||||||
data.addQuad(quad(subject, RDF.terms.type, NOTIFY.terms.WebSocketSubscription2021));
|
data.addQuad(quad(subject, RDF.terms.type, NOTIFY.terms.WebSocketChannel2023));
|
||||||
data.addQuad(quad(subject, NOTIFY.terms.topic, namedNode(topic)));
|
data.addQuad(quad(subject, NOTIFY.terms.topic, namedNode(topic)));
|
||||||
|
|
||||||
const id = 'http://example.com/foo/4c9b88c1-7502-4107-bb79-2a3a590c7aa3';
|
const id = 'http://example.com/foo/4c9b88c1-7502-4107-bb79-2a3a590c7aa3';
|
||||||
channel = {
|
channel = {
|
||||||
id,
|
id,
|
||||||
type: NOTIFY.WebSocketSubscription2021,
|
type: NOTIFY.WebSocketChannel2023,
|
||||||
topic,
|
topic,
|
||||||
source: generateWebSocketUrl(route.getPath(), id),
|
receiveFrom: generateWebSocketUrl(route.getPath(), id),
|
||||||
};
|
};
|
||||||
|
|
||||||
channelType = new WebSocketSubscription2021(route);
|
channelType = new WebSocketChannel2023Type(route);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exposes a utility function to verify if a channel is a websocket channel.', async(): Promise<void> => {
|
it('exposes a utility function to verify if a channel is a websocket channel.', async(): Promise<void> => {
|
||||||
expect(isWebSocket2021Channel(channel)).toBe(true);
|
expect(isWebSocket2023Channel(channel)).toBe(true);
|
||||||
|
|
||||||
(channel as NotificationChannel).type = 'something else';
|
(channel as NotificationChannel).type = 'something else';
|
||||||
expect(isWebSocket2021Channel(channel)).toBe(false);
|
expect(isWebSocket2023Channel(channel)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly parses notification channel bodies.', async(): Promise<void> => {
|
it('correctly parses notification channel bodies.', async(): Promise<void> => {
|
@ -31,7 +31,7 @@ const portNames = [
|
|||||||
'Subdomains',
|
'Subdomains',
|
||||||
'WebHookSubscription2021',
|
'WebHookSubscription2021',
|
||||||
'WebHookSubscription2021-client',
|
'WebHookSubscription2021-client',
|
||||||
'WebSocketSubscription2021',
|
'WebSocketChannel2023',
|
||||||
|
|
||||||
// Unit
|
// Unit
|
||||||
'BaseServerFactory',
|
'BaseServerFactory',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user