mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Replace WebHookSubscription2021 with WebHookChannel2023
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { DataFactory, Store } from 'n3';
|
||||
import type { Credentials } from '../../../../../src/authentication/Credentials';
|
||||
import {
|
||||
AbsolutePathInteractionRoute,
|
||||
} from '../../../../../src/identity/interaction/routing/AbsolutePathInteractionRoute';
|
||||
@@ -12,12 +11,12 @@ import { CONTEXT_NOTIFICATION } from '../../../../../src/server/notifications/No
|
||||
import type { NotificationChannel } from '../../../../../src/server/notifications/NotificationChannel';
|
||||
import type { StateHandler } from '../../../../../src/server/notifications/StateHandler';
|
||||
import type {
|
||||
WebHookSubscription2021Channel,
|
||||
} from '../../../../../src/server/notifications/WebHookSubscription2021/WebHookSubscription2021';
|
||||
WebhookChannel2023,
|
||||
} from '../../../../../src/server/notifications/WebHookChannel2023/WebhookChannel2023Type';
|
||||
import {
|
||||
isWebHook2021Channel,
|
||||
WebHookSubscription2021,
|
||||
} from '../../../../../src/server/notifications/WebHookSubscription2021/WebHookSubscription2021';
|
||||
isWebHook2023Channel,
|
||||
WebhookChannel2023Type,
|
||||
} from '../../../../../src/server/notifications/WebHookChannel2023/WebhookChannel2023Type';
|
||||
import { NOTIFY, RDF } from '../../../../../src/util/Vocabularies';
|
||||
import quad = DataFactory.quad;
|
||||
import blankNode = DataFactory.blankNode;
|
||||
@@ -31,79 +30,59 @@ jest.mock('../../../../../src/logging/LogUtil', (): any => {
|
||||
|
||||
jest.mock('uuid', (): any => ({ v4: (): string => '4c9b88c1-7502-4107-bb79-2a3a590c7aa3' }));
|
||||
|
||||
describe('A WebHookSubscription2021', (): void => {
|
||||
const credentials: Credentials = { agent: { webId: 'http://example.org/alice' }};
|
||||
const target = 'http://example.org/somewhere-else';
|
||||
describe('A WebhookChannel2023Type', (): void => {
|
||||
const sendTo = 'http://example.org/somewhere-else';
|
||||
const topic = 'https://storage.example/resource';
|
||||
const subject = blankNode();
|
||||
let data: Store;
|
||||
let channel: WebHookSubscription2021Channel;
|
||||
let channel: WebhookChannel2023;
|
||||
const route = new AbsolutePathInteractionRoute('http://example.com/webhooks/');
|
||||
const webIdRoute = new RelativePathInteractionRoute(route, '/webid');
|
||||
let stateHandler: jest.Mocked<StateHandler>;
|
||||
let channelType: WebHookSubscription2021;
|
||||
let channelType: WebhookChannel2023Type;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
data = new Store();
|
||||
data.addQuad(quad(subject, RDF.terms.type, NOTIFY.terms.WebHookSubscription2021));
|
||||
data.addQuad(quad(subject, RDF.terms.type, NOTIFY.terms.WebHookChannel2023));
|
||||
data.addQuad(quad(subject, NOTIFY.terms.topic, namedNode(topic)));
|
||||
data.addQuad(quad(subject, NOTIFY.terms.target, namedNode(target)));
|
||||
data.addQuad(quad(subject, NOTIFY.terms.sendTo, namedNode(sendTo)));
|
||||
|
||||
const id = 'http://example.com/webhooks/4c9b88c1-7502-4107-bb79-2a3a590c7aa3';
|
||||
channel = {
|
||||
id,
|
||||
type: NOTIFY.WebHookSubscription2021,
|
||||
type: NOTIFY.WebHookChannel2023,
|
||||
topic: 'https://storage.example/resource',
|
||||
target,
|
||||
webId: 'http://example.org/alice',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
unsubscribe_endpoint: id,
|
||||
sendTo,
|
||||
};
|
||||
|
||||
stateHandler = {
|
||||
handleSafe: jest.fn(),
|
||||
} as any;
|
||||
|
||||
channelType = new WebHookSubscription2021(route, webIdRoute, stateHandler);
|
||||
channelType = new WebhookChannel2023Type(route, webIdRoute, stateHandler);
|
||||
});
|
||||
|
||||
it('exposes a utility function to verify if a channel is a webhook channel.', async(): Promise<void> => {
|
||||
expect(isWebHook2021Channel(channel)).toBe(true);
|
||||
expect(isWebHook2023Channel(channel)).toBe(true);
|
||||
|
||||
(channel as NotificationChannel).type = 'something else';
|
||||
expect(isWebHook2021Channel(channel)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns a correct description of the subscription service.', async(): Promise<void> => {
|
||||
expect(channelType.getDescription()).toEqual({
|
||||
'@context': [ 'https://www.w3.org/ns/solid/notification/v1' ],
|
||||
id: 'http://example.com/webhooks/',
|
||||
channelType: 'http://www.w3.org/ns/solid/notifications#WebHookSubscription2021',
|
||||
feature: [ 'accept', 'endAt', 'rate', 'startAt', 'state', 'notify:webhookAuth' ],
|
||||
'http://www.w3.org/ns/solid/notifications#webid': { id: 'http://example.com/webhooks/webid' },
|
||||
});
|
||||
expect(isWebHook2023Channel(channel)).toBe(false);
|
||||
});
|
||||
|
||||
it('correctly parses notification channel bodies.', async(): Promise<void> => {
|
||||
await expect(channelType.initChannel(data, credentials)).resolves.toEqual(channel);
|
||||
await expect(channelType.initChannel(data)).resolves.toEqual(channel);
|
||||
});
|
||||
|
||||
it('errors if the credentials do not contain a WebID.', async(): Promise<void> => {
|
||||
await expect(channelType.initChannel(data, {})).rejects
|
||||
.toThrow('A WebHookSubscription2021 subscription request needs to be authenticated with a WebID.');
|
||||
});
|
||||
|
||||
it('removes the WebID when converting back to JSON-LD.', async(): Promise<void> => {
|
||||
it('adds the WebID when generating a JSON-LD representation of a channel.', async(): Promise<void> => {
|
||||
await expect(channelType.toJsonLd(channel)).resolves.toEqual({
|
||||
'@context': [
|
||||
CONTEXT_NOTIFICATION,
|
||||
],
|
||||
id: channel.id,
|
||||
type: NOTIFY.WebHookSubscription2021,
|
||||
target,
|
||||
type: NOTIFY.WebHookChannel2023,
|
||||
sendTo,
|
||||
topic,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
unsubscribe_endpoint: channel.unsubscribe_endpoint,
|
||||
sender: 'http://example.com/webhooks/webid',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,10 +9,10 @@ import {
|
||||
import type { Logger } from '../../../../../src/logging/Logger';
|
||||
import { getLoggerFor } from '../../../../../src/logging/LogUtil';
|
||||
import type { Notification } from '../../../../../src/server/notifications/Notification';
|
||||
import { WebHookEmitter } from '../../../../../src/server/notifications/WebHookSubscription2021/WebHookEmitter';
|
||||
import type {
|
||||
WebHookSubscription2021Channel,
|
||||
} from '../../../../../src/server/notifications/WebHookSubscription2021/WebHookSubscription2021';
|
||||
WebhookChannel2023,
|
||||
} from '../../../../../src/server/notifications/WebHookChannel2023/WebhookChannel2023Type';
|
||||
import { WebHookEmitter } from '../../../../../src/server/notifications/WebHookChannel2023/WebHookEmitter';
|
||||
import { NotImplementedHttpError } from '../../../../../src/util/errors/NotImplementedHttpError';
|
||||
import { matchesAuthorizationScheme } from '../../../../../src/util/HeaderUtil';
|
||||
import { trimTrailingSlashes } from '../../../../../src/util/PathUtil';
|
||||
@@ -42,14 +42,11 @@ describe('A WebHookEmitter', (): void => {
|
||||
published: '123',
|
||||
};
|
||||
let representation: Representation;
|
||||
const channel: WebHookSubscription2021Channel = {
|
||||
const channel: WebhookChannel2023 = {
|
||||
id: 'id',
|
||||
topic: 'http://example.com/foo',
|
||||
type: NOTIFY.WebHookSubscription2021,
|
||||
target: 'http://example.org/somewhere-else',
|
||||
webId: 'http://example.org/other/#me',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
unsubscribe_endpoint: 'http://example.org/unsubscribe',
|
||||
type: NOTIFY.WebHookChannel2023,
|
||||
sendTo: 'http://example.org/somewhere-else',
|
||||
};
|
||||
|
||||
let privateJwk: AlgJwk;
|
||||
@@ -120,7 +117,7 @@ describe('A WebHookEmitter', (): void => {
|
||||
// CHeck the DPoP proof
|
||||
const decodedDpopProof = await jwtVerify(dpop, publicObject);
|
||||
expect(decodedDpopProof.payload).toMatchObject({
|
||||
htu: channel.target,
|
||||
htu: channel.sendTo,
|
||||
htm: 'POST',
|
||||
iat: now,
|
||||
jti: expect.stringContaining('-'),
|
||||
@@ -142,7 +139,7 @@ describe('A WebHookEmitter', (): void => {
|
||||
|
||||
expect(logger.error).toHaveBeenCalledTimes(1);
|
||||
expect(logger.error).toHaveBeenLastCalledWith(
|
||||
`There was an issue emitting a WebHook notification with target ${channel.target}: invalid request`,
|
||||
`There was an issue emitting a WebHook notification with target ${channel.sendTo}: invalid request`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import type { Operation } from '../../../../../src/http/Operation';
|
||||
import { BasicRepresentation } from '../../../../../src/http/representation/BasicRepresentation';
|
||||
import type { HttpRequest } from '../../../../../src/server/HttpRequest';
|
||||
import type { HttpResponse } from '../../../../../src/server/HttpResponse';
|
||||
import { WebHookWebId } from '../../../../../src/server/notifications/WebHookSubscription2021/WebHookWebId';
|
||||
import { WebHookWebId } from '../../../../../src/server/notifications/WebHookChannel2023/WebHookWebId';
|
||||
import { readableToString } from '../../../../../src/util/StreamUtil';
|
||||
import { SOLID } from '../../../../../src/util/Vocabularies';
|
||||
const { namedNode, quad } = DataFactory;
|
||||
Reference in New Issue
Block a user