feat: Replace WebSocketSubscription2021 with WebSocketChannel2023

This commit is contained in:
Joachim Van Herwegen 2023-02-03 16:20:22 +01:00
parent cbbb10afa1
commit 702e8f5f59
21 changed files with 141 additions and 141 deletions

View File

@ -2,21 +2,21 @@
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^6.0.0/components/context.jsonld",
"@graph": [
{
"comment": "Handles the generation and serialization of notifications for WebSocketSubscription2021.",
"@id": "urn:solid-server:default:WebSocket2021NotificationHandler",
"comment": "Handles the generation and serialization of notifications for WebSocketChannel2023.",
"@id": "urn:solid-server:default:WebSocket2023NotificationHandler",
"@type": "TypedNotificationHandler",
"type": "http://www.w3.org/ns/solid/notifications#WebSocketSubscription2021",
"type": "http://www.w3.org/ns/solid/notifications#WebSocketChannel2023",
"source": {
"@type": "ComposedNotificationHandler",
"generator": { "@id": "urn:solid-server:default:BaseNotificationGenerator" },
"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.",
"@id": "urn:solid-server:default:WebSocket2021Emitter",
"@type": "WebSocket2021Emitter",
"@id": "urn:solid-server:default:WebSocket2023Emitter",
"@type": "WebSocket2023Emitter",
"socketMap": { "@id": "urn:solid-server:default:WebSocketMap" }
},
@ -24,7 +24,7 @@
"@id": "urn:solid-server:default:NotificationHandler",
"@type": "WaterfallHandler",
"handlers": [
{ "@id": "urn:solid-server:default:WebSocket2021NotificationHandler" }
{ "@id": "urn:solid-server:default:WebSocket2023NotificationHandler" }
]
}
]

View File

@ -3,15 +3,15 @@
"@graph": [
{
"comment": "Catches newly opened WebSockets and verifies if they belong to a subscription.",
"@id": "urn:solid-server:default:WebSocket2021Listener",
"@type": "WebSocket2021Listener",
"@id": "urn:solid-server:default:WebSocket2023Listener",
"@type": "WebSocket2023Listener",
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" },
"route": { "@id": "urn:solid-server:default:WebSocket2021Route" },
"route": { "@id": "urn:solid-server:default:WebSocket2023Route" },
"handler": {
"@type": "SequenceHandler",
"handlers": [
{ "@id": "urn:solid-server:default:WebSocket2021Storer" },
{ "@id": "urn:solid-server:default:WebSocket2021StateHandler" }
{ "@id": "urn:solid-server:default:WebSocket2023Storer" },
{ "@id": "urn:solid-server:default:WebSocket2023StateHandler" }
]
}
},
@ -22,16 +22,16 @@
},
{
"comment": "Stores the opened WebSockets for reuse.",
"@id": "urn:solid-server:default:WebSocket2021Storer",
"@type": "WebSocket2021Storer",
"@id": "urn:solid-server:default:WebSocket2023Storer",
"@type": "WebSocket2023Storer",
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" },
"socketMap": { "@id": "urn:solid-server:default:WebSocketMap" }
},
{
"comment": "Handles the state feature of a WebSocketSubscription2021 subscription.",
"@id": "urn:solid-server:default:WebSocket2021StateHandler",
"comment": "Handles the state feature of a WebSocketChannel2023 subscription.",
"@id": "urn:solid-server:default:WebSocket2023StateHandler",
"@type": "BaseStateHandler",
"handler": { "@id": "urn:solid-server:default:WebSocket2021NotificationHandler" },
"handler": { "@id": "urn:solid-server:default:WebSocket2023NotificationHandler" },
"storage": { "@id": "urn:solid-server:default:SubscriptionStorage" }
},
@ -39,7 +39,7 @@
"@id": "urn:solid-server:default:ServerConfigurator",
"@type": "ParallelHandler",
"handlers": [
{ "@id": "urn:solid-server:default:WebSocket2021Listener" }
{ "@id": "urn:solid-server:default:WebSocket2023Listener" }
]
}
]

View File

@ -2,15 +2,15 @@
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^6.0.0/components/context.jsonld",
"@graph": [
{
"comment": "Handles the subscriptions targeting a WebSocketSubscription2021.",
"@id": "urn:solid-server:default:WebSocket2021Subscriber",
"comment": "Handles the subscriptions targeting a WebSocketChannel2023.",
"@id": "urn:solid-server:default:WebSocket2023Subscriber",
"@type": "OperationRouterHandler",
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"allowedMethods": [ "HEAD", "GET", "POST" ],
"allowedPathNames": [ "/WebSocketSubscription2021/$" ],
"allowedPathNames": [ "/WebSocketChannel2023/$" ],
"handler": {
"@type": "NotificationSubscriber",
"channelType": { "@id": "urn:solid-server:default:WebSocketSubscription2021" },
"channelType": { "@id": "urn:solid-server:default:WebSocketChannel2023Type" },
"converter": { "@id": "urn:solid-server:default:RepresentationConverter" },
"credentialsExtractor": { "@id": "urn:solid-server:default:CredentialsExtractor" },
"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",
"base": { "@id": "urn:solid-server:default:NotificationRoute" },
"relativePath": "/WebSocketSubscription2021/"
"relativePath": "/WebSocketChannel2023/"
},
{
"comment": "Contains all the metadata relevant for a WebSocketSubscription2021.",
"@id": "urn:solid-server:default:WebSocketSubscription2021",
"@type": "WebSocketSubscription2021",
"route": { "@id": "urn:solid-server:default:WebSocket2021Route" }
"comment": "Contains all the metadata relevant for a WebSocketChannel2023.",
"@id": "urn:solid-server:default:WebSocketChannel2023Type",
"@type": "WebSocketChannel2023Type",
"route": { "@id": "urn:solid-server:default:WebSocket2023Route" }
},
{
"@id": "urn:solid-server:default:NotificationTypeHandler",
"@type": "WaterfallHandler",
"handlers": [
{ "@id": "urn:solid-server:default:WebSocket2021Subscriber" }
{ "@id": "urn:solid-server:default:WebSocket2023Subscriber" }
]
},
@ -44,7 +44,7 @@
"@type": "NotificationDescriber",
"subscriptions": [
{
"@id": "urn:solid-server:default:WebSocketSubscription2021"
"@id": "urn:solid-server:default:WebSocketChannel2023Type"
}
]
}

View File

@ -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`
that handles notifications for the specific type.
## WebSocketSubscription2021
## WebSocketChannel2023
To add support for [WebSocketSubscription2021](https://solidproject.org/TR/2022/websocket-subscription-2021-20220509)
notifications,
To add support for [WebSocketChannel2023](https://solid.github.io/notifications/websocket-channel-2023) 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.
As `NotificationChannelType`, there is a specific `WebSocketChannel2023Type` that contains all the necessary information.
### Handling notifications
@ -120,13 +119,13 @@ flowchart TB
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
WebSocket2023Emitter("<strong>WebSocket2023Emitter</strong><br>WebSocket2023Emitter")
BaseNotificationGenerator --> BaseNotificationSerializer --> WebSocket2023Emitter
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.
making sure only WebSocketChannel2023 subscriptions will be handled.
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
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.
### WebSockets
```mermaid
flowchart TB
WebSocket2021Listener("<strong>WebSocket2021Listener</strong><br>WebSocket2021Listener")
WebSocket2021Listener --> WebSocket2021ListenerArgs
WebSocket2023Listener("<strong>WebSocket2023Listener</strong><br>WebSocket2023Listener")
WebSocket2023Listener --> WebSocket2023ListenerArgs
subgraph WebSocket2021ListenerArgs[" "]
subgraph WebSocket2023ListenerArgs[" "]
direction LR
NotificationChannelStorage("<strong>NotificationChannelStorage</strong><br>NotificationChannelStorage")
SequenceHandler("<br>SequenceHandler")
@ -160,17 +159,17 @@ flowchart TB
subgraph SequenceHandlerArgs[" "]
direction TB
WebSocket2021Storer("<strong>WebSocket2021Storer</strong><br>WebSocket2021Storer")
WebSocket2021StateHandler("<strong>WebSocket2021StateHandler</strong><br>BaseStateHandler")
WebSocket2023Storer("<strong>WebSocket2023Storer</strong><br>WebSocket2023Storer")
WebSocket2023StateHandler("<strong>WebSocket2023StateHandler</strong><br>BaseStateHandler")
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.
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`.
The `WebSocket2021Storer` will store the WebSocket in the same map used by the `WebSocket2021Emitter`,
In this case, this is a `SequenceHandler`, which contains a `WebSocket2023Storer` and a `BaseStateHandler`.
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.
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.
@ -179,7 +178,7 @@ as defined in the notification specification.
The additions required to support
[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`.
* The `WebHookSubscription2021` class contains all the necessary typing information.

View File

@ -326,14 +326,14 @@ export * from './server/notifications/WebHookSubscription2021/WebHookSubscriptio
export * from './server/notifications/WebHookSubscription2021/WebHookUnsubscriber';
export * from './server/notifications/WebHookSubscription2021/WebHookWebId';
// Server/Notifications/WebSocketSubscription2021
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Emitter';
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Handler';
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Listener';
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Storer';
export * from './server/notifications/WebSocketSubscription2021/WebSocket2021Util';
export * from './server/notifications/WebSocketSubscription2021/WebSocketMap';
export * from './server/notifications/WebSocketSubscription2021/WebSocketSubscription2021';
// Server/Notifications/WebSocketChannel2023
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Emitter';
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Handler';
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Listener';
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Storer';
export * from './server/notifications/WebSocketChannel2023/WebSocket2023Util';
export * from './server/notifications/WebSocketChannel2023/WebSocketMap';
export * from './server/notifications/WebSocketChannel2023/WebSocketChannel2023Type';
// Server/Notifications
export * from './server/notifications/ActivityEmitter';

View File

@ -6,11 +6,11 @@ import { NotificationEmitter } 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.
* 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);
private readonly socketMap: SetMultiMap<string, WebSocket>;

View File

@ -2,12 +2,12 @@ import type { WebSocket } from 'ws';
import { AsyncHandler } from '../../../util/handlers/AsyncHandler';
import type { NotificationChannel } from '../NotificationChannel';
export interface WebSocket2021HandlerInput {
export interface WebSocket2023HandlerInput {
channel: NotificationChannel;
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> {}

View File

@ -4,21 +4,21 @@ import type { InteractionRoute } from '../../../identity/interaction/routing/Int
import { getLoggerFor } from '../../../logging/LogUtil';
import { WebSocketServerConfigurator } from '../../WebSocketServerConfigurator';
import type { NotificationChannelStorage } from '../NotificationChannelStorage';
import type { WebSocket2021Handler } from './WebSocket2021Handler';
import { parseWebSocketRequest } from './WebSocket2021Util';
import type { WebSocket2023Handler } from './WebSocket2023Handler';
import { parseWebSocketRequest } from './WebSocket2023Util';
/**
* Listens for WebSocket connections and verifies if they are valid WebSocketSubscription2021 connections,
* in which case its {@link WebSocket2021Handler} will be alerted.
* Listens for WebSocket connections and verifies if they are valid WebSocketChannel2023 connections,
* in which case its {@link WebSocket2023Handler} will be alerted.
*/
export class WebSocket2021Listener extends WebSocketServerConfigurator {
export class WebSocket2023Listener extends WebSocketServerConfigurator {
protected readonly logger = getLoggerFor(this);
private readonly storage: NotificationChannelStorage;
private readonly handler: WebSocket2021Handler;
private readonly handler: WebSocket2023Handler;
private readonly path: string;
public constructor(storage: NotificationChannelStorage, handler: WebSocket2021Handler, route: InteractionRoute) {
public constructor(storage: NotificationChannelStorage, handler: WebSocket2023Handler, route: InteractionRoute) {
super();
this.storage = storage;
this.handler = handler;

View File

@ -3,11 +3,11 @@ import { getLoggerFor } from '../../../logging/LogUtil';
import type { SetMultiMap } from '../../../util/map/SetMultiMap';
import { setSafeInterval } from '../../../util/TimerUtil';
import type { NotificationChannelStorage } from '../NotificationChannelStorage';
import type { WebSocket2021HandlerInput } from './WebSocket2021Handler';
import { WebSocket2021Handler } from './WebSocket2021Handler';
import type { WebSocket2023HandlerInput } from './WebSocket2023Handler';
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.
*
* `cleanupTimer` defines in minutes how often the stored WebSockets are closed
@ -15,7 +15,7 @@ import { WebSocket2021Handler } from './WebSocket2021Handler';
* Defaults to 60 minutes.
* 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);
private readonly storage: NotificationChannelStorage;
@ -34,7 +34,7 @@ export class WebSocket2021Storer extends WebSocket2021Handler {
timer.unref();
}
public async handle({ webSocket, channel }: WebSocket2021HandlerInput): Promise<void> {
public async handle({ webSocket, channel }: WebSocket2023HandlerInput): Promise<void> {
this.socketMap.add(channel.id, webSocket);
webSocket.on('error', (): boolean => this.socketMap.deleteEntry(channel.id, webSocket));
webSocket.on('close', (): boolean => this.socketMap.deleteEntry(channel.id, webSocket));

View File

@ -5,45 +5,45 @@ import { getLoggerFor } from '../../../logging/LogUtil';
import { NOTIFY } from '../../../util/Vocabularies';
import { BaseChannelType } from '../BaseChannelType';
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.
*/
source: string;
receiveFrom: string;
}
export function isWebSocket2021Channel(channel: NotificationChannel): channel is WebSocketSubscription2021Channel {
return channel.type === NOTIFY.WebSocketSubscription2021;
export function isWebSocket2023Channel(channel: NotificationChannel): channel is WebSocketChannel2023 {
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
*
* 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);
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);
return {
...channel,
type: NOTIFY.WebSocketSubscription2021,
source: generateWebSocketUrl(this.path, channel.id),
type: NOTIFY.WebSocketChannel2023,
receiveFrom: generateWebSocketUrl(this.path, channel.id),
};
}
}

View File

@ -197,6 +197,7 @@ export const NOTIFY = createVocabulary('http://www.w3.org/ns/solid/notifications
'endAt',
'feature',
'rate',
'receiveFrom',
'startAt',
'state',
'subscription',
@ -206,7 +207,7 @@ export const NOTIFY = createVocabulary('http://www.w3.org/ns/solid/notifications
'webid',
'WebHookSubscription2021',
'WebSocketSubscription2021',
'WebSocketChannel2023',
);
export const OIDC = createVocabulary('http://www.w3.org/ns/solid/oidc#',

View File

@ -14,17 +14,16 @@ import {
getPresetConfigPath,
getTestConfigPath,
getTestFolder,
instantiateFromConfig,
removeFolder,
instantiateFromConfig, removeFolder,
} from './Config';
import quad = DataFactory.quad;
import namedNode = DataFactory.namedNode;
const port = getPort('WebSocketSubscription2021');
const port = getPort('WebSocketChannel2023');
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][] = [
[ 'in-memory storage', {
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 store: ResourceStore;
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
const subscriptions = quads.getObjects(storageDescriptionUrl, NOTIFY.terms.subscription, null);
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);
subscriptionUrl = websocketSubscriptions[0].value;
@ -97,7 +96,7 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
it('supports subscribing.', async(): Promise<void> => {
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> => {
@ -167,7 +166,7 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
const channel = {
'@context': [ 'https://www.w3.org/ns/solid/notification/v1' ],
type: NOTIFY.WebSocketSubscription2021,
type: notificationType,
topic: restricted,
};
@ -199,9 +198,9 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
});
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));
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> => {
const { source } =
const { receiveFrom } =
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));
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 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.countQuads(null, NOTIFY.terms.receiveFrom, null, null)).toBe(1);
});
});

View File

@ -27,9 +27,9 @@ describe('A KeyValueChannelStorage', (): void => {
beforeEach(async(): Promise<void> => {
resetAllMocks();
channel = {
id: `WebSocketSubscription2021:${v4()}:http://example.com/foo`,
id: `WebSocketChannel2023:${v4()}:http://example.com/foo`,
topic,
type: 'WebSocketSubscription2021',
type: 'WebSocketChannel2023',
};
internalMap = new Map();

View File

@ -3,12 +3,12 @@ import type { WebSocket } from 'ws';
import { BasicRepresentation } from '../../../../../src/http/representation/BasicRepresentation';
import type { NotificationChannel } from '../../../../../src/server/notifications/NotificationChannel';
import {
WebSocket2021Emitter,
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Emitter';
WebSocket2023Emitter,
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Emitter';
import type { SetMultiMap } from '../../../../../src/util/map/SetMultiMap';
import { WrappedSetMultiMap } from '../../../../../src/util/map/WrappedSetMultiMap';
describe('A WebSocket2021Emitter', (): void => {
describe('A WebSocket2023Emitter', (): void => {
const channel: NotificationChannel = {
id: 'id',
topic: 'http://example.com/foo',
@ -17,7 +17,7 @@ describe('A WebSocket2021Emitter', (): void => {
let webSocket: jest.Mocked<WebSocket>;
let socketMap: SetMultiMap<string, WebSocket>;
let emitter: WebSocket2021Emitter;
let emitter: WebSocket2023Emitter;
beforeEach(async(): Promise<void> => {
webSocket = new EventEmitter() as any;
@ -26,7 +26,7 @@ describe('A WebSocket2021Emitter', (): void => {
socketMap = new WrappedSetMultiMap();
emitter = new WebSocket2021Emitter(socketMap);
emitter = new WebSocket2023Emitter(socketMap);
});
it('emits notifications to the stored WebSockets.', async(): Promise<void> => {

View File

@ -10,11 +10,11 @@ import type {
NotificationChannelStorage,
} from '../../../../../src/server/notifications/NotificationChannelStorage';
import type {
WebSocket2021Handler,
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Handler';
WebSocket2023Handler,
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Handler';
import {
WebSocket2021Listener,
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Listener';
WebSocket2023Listener,
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Listener';
import { flushPromises } from '../../../../util/Util';
jest.mock('ws', (): any => ({
@ -26,7 +26,7 @@ jest.mock('ws', (): any => ({
})),
}));
describe('A WebSocket2021Listener', (): void => {
describe('A WebSocket2023Listener', (): void => {
const channel: NotificationChannel = {
id: 'id',
topic: 'http://example.com/foo',
@ -37,9 +37,9 @@ describe('A WebSocket2021Listener', (): void => {
let webSocket: WebSocket;
let upgradeRequest: HttpRequest;
let storage: jest.Mocked<NotificationChannelStorage>;
let handler: jest.Mocked<WebSocket2021Handler>;
let handler: jest.Mocked<WebSocket2023Handler>;
const route = new AbsolutePathInteractionRoute('http://example.com/foo');
let listener: WebSocket2021Listener;
let listener: WebSocket2023Listener;
beforeEach(async(): Promise<void> => {
server = new EventEmitter() as any;
@ -57,7 +57,7 @@ describe('A WebSocket2021Listener', (): void => {
handleSafe: jest.fn(),
} as any;
listener = new WebSocket2021Listener(storage, handler, route);
listener = new WebSocket2023Listener(storage, handler, route);
await listener.handle(server);
});

View File

@ -6,13 +6,13 @@ import type {
} from '../../../../../src/server/notifications/NotificationChannelStorage';
import {
WebSocket2021Storer,
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Storer';
WebSocket2023Storer,
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Storer';
import type { SetMultiMap } from '../../../../../src/util/map/SetMultiMap';
import { WrappedSetMultiMap } from '../../../../../src/util/map/WrappedSetMultiMap';
import { flushPromises } from '../../../../util/Util';
describe('A WebSocket2021Storer', (): void => {
describe('A WebSocket2023Storer', (): void => {
const channel: NotificationChannel = {
id: 'id',
topic: 'http://example.com/foo',
@ -21,7 +21,7 @@ describe('A WebSocket2021Storer', (): void => {
let webSocket: jest.Mocked<WebSocket>;
let storage: jest.Mocked<NotificationChannelStorage>;
let socketMap: SetMultiMap<string, WebSocket>;
let storer: WebSocket2021Storer;
let storer: WebSocket2023Storer;
beforeEach(async(): Promise<void> => {
webSocket = new EventEmitter() as any;
@ -33,7 +33,7 @@ describe('A WebSocket2021Storer', (): void => {
socketMap = new WrappedSetMultiMap();
storer = new WebSocket2021Storer(storage, socketMap);
storer = new WebSocket2023Storer(storage, socketMap);
});
it('stores WebSockets.', async(): Promise<void> => {
@ -60,7 +60,7 @@ describe('A WebSocket2021Storer', (): void => {
jest.useFakeTimers();
// 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;
webSocket2.close = jest.fn();

View File

@ -1,9 +1,9 @@
import type { IncomingMessage } from 'http';
import {
generateWebSocketUrl, parseWebSocketRequest,
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Util';
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Util';
describe('WebSocket2021Util', (): void => {
describe('WebSocket2023Util', (): void => {
describe('#generateWebSocketUrl', (): 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');

View File

@ -6,14 +6,14 @@ import type { NotificationChannel } from '../../../../../src/server/notification
import {
generateWebSocketUrl,
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocket2021Util';
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocket2023Util';
import type {
WebSocketSubscription2021Channel,
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocketSubscription2021';
WebSocketChannel2023,
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocketChannel2023Type';
import {
isWebSocket2021Channel,
WebSocketSubscription2021,
} from '../../../../../src/server/notifications/WebSocketSubscription2021/WebSocketSubscription2021';
isWebSocket2023Channel,
WebSocketChannel2023Type,
} from '../../../../../src/server/notifications/WebSocketChannel2023/WebSocketChannel2023Type';
import { NOTIFY, RDF } from '../../../../../src/util/Vocabularies';
import quad = DataFactory.quad;
import blankNode = DataFactory.blankNode;
@ -21,35 +21,35 @@ import namedNode = DataFactory.namedNode;
jest.mock('uuid', (): any => ({ v4: (): string => '4c9b88c1-7502-4107-bb79-2a3a590c7aa3' }));
describe('A WebSocketSubscription2021', (): void => {
describe('A WebSocketChannel2023', (): void => {
let data: Store;
let channel: WebSocketSubscription2021Channel;
let channel: WebSocketChannel2023;
const subject = blankNode();
const topic = 'https://storage.example/resource';
const route = new AbsolutePathInteractionRoute('http://example.com/foo');
let channelType: WebSocketSubscription2021;
let channelType: WebSocketChannel2023Type;
beforeEach(async(): Promise<void> => {
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)));
const id = 'http://example.com/foo/4c9b88c1-7502-4107-bb79-2a3a590c7aa3';
channel = {
id,
type: NOTIFY.WebSocketSubscription2021,
type: NOTIFY.WebSocketChannel2023,
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> => {
expect(isWebSocket2021Channel(channel)).toBe(true);
expect(isWebSocket2023Channel(channel)).toBe(true);
(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> => {

View File

@ -31,7 +31,7 @@ const portNames = [
'Subdomains',
'WebHookSubscription2021',
'WebHookSubscription2021-client',
'WebSocketSubscription2021',
'WebSocketChannel2023',
// Unit
'BaseServerFactory',