mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Use URLs for channel identifiers
In the future these can potentially be used to dereference them
This commit is contained in:
@@ -5,6 +5,9 @@ import { createRemoteJWKSet, jwtVerify } from 'jose';
|
||||
import type { NamedNode } from 'n3';
|
||||
import { DataFactory, Parser, Store } from 'n3';
|
||||
import type { App } from '../../src/init/App';
|
||||
import type {
|
||||
WebHookSubscription2021Channel,
|
||||
} from '../../src/server/notifications/WebHookSubscription2021/WebHookSubscription2021';
|
||||
import { matchesAuthorizationScheme } from '../../src/util/HeaderUtil';
|
||||
import { joinUrl, trimTrailingSlashes } from '../../src/util/PathUtil';
|
||||
import { readJsonStream } from '../../src/util/StreamUtil';
|
||||
@@ -181,4 +184,15 @@ describe.each(stores)('A server supporting WebHookSubscription2021 using %s', (n
|
||||
// Close the connection so the server can shut down
|
||||
response.end();
|
||||
});
|
||||
|
||||
it('allows a user to unsubscribe.', async(): Promise<void> => {
|
||||
const json = await subscribe(notificationType, webId, subscriptionUrl, topic, { [NOTIFY.target]: target });
|
||||
const unsubscribeUrl = (json as WebHookSubscription2021Channel).unsubscribe_endpoint;
|
||||
let response = await fetch(unsubscribeUrl, { method: 'DELETE' });
|
||||
expect(response.status).toBe(403);
|
||||
response = await fetch(unsubscribeUrl, { method: 'DELETE', headers: { authorization: `WebID ${webId}` }});
|
||||
expect(response.status).toBe(205);
|
||||
response = await fetch(joinUrl(subscriptionUrl, 'abc'), { method: 'DELETE' });
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import { BasicRepresentation } from '../../src/http/representation/BasicRepresen
|
||||
import type { App } from '../../src/init/App';
|
||||
import type { ResourceStore } from '../../src/storage/ResourceStore';
|
||||
import { joinUrl } from '../../src/util/PathUtil';
|
||||
import { NOTIFY } from '../../src/util/Vocabularies';
|
||||
import { NOTIFY, RDF } from '../../src/util/Vocabularies';
|
||||
import { expectNotification, subscribe } from '../util/NotificationUtil';
|
||||
import { getPort } from '../util/Util';
|
||||
import {
|
||||
@@ -223,4 +223,29 @@ describe.each(stores)('A server supporting WebSocketSubscription2021 using %s',
|
||||
const message = (await messagePromise).toString();
|
||||
expect(message).toBe('Notification channel has expired');
|
||||
});
|
||||
|
||||
it('can use other RDF formats and content negotiation when creating a channel.', async(): Promise<void> => {
|
||||
const turtleChannel = `
|
||||
_:id <${RDF.type}> <${notificationType}> ;
|
||||
<http://www.w3.org/ns/solid/notifications#topic> <${topic}>.
|
||||
`;
|
||||
|
||||
const response = await fetch(subscriptionUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
authorization: `WebID ${webId}`,
|
||||
'content-type': 'text/turtle',
|
||||
accept: 'text/turtle',
|
||||
},
|
||||
body: turtleChannel,
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('content-type')).toBe('text/turtle');
|
||||
|
||||
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, NOTIFY.terms.topic, null)).toEqual([ namedNode(topic) ]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ class DummyChannelType extends BaseChannelType {
|
||||
}
|
||||
|
||||
describe('A BaseChannelType', (): void => {
|
||||
const id = '4c9b88c1-7502-4107-bb79-2a3a590c7aa3:https://storage.example/resource';
|
||||
const id = 'http://example.com/DummyType/4c9b88c1-7502-4107-bb79-2a3a590c7aa3';
|
||||
const credentials: Credentials = {};
|
||||
const channelType = new DummyChannelType();
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import {
|
||||
generateWebHookUnsubscribeUrl, parseWebHookUnsubscribeUrl,
|
||||
} from '../../../../../src/server/notifications/WebHookSubscription2021/WebHook2021Util';
|
||||
|
||||
describe('WebHook2021Util', (): void => {
|
||||
describe('#generateWebHookUnsubscribeUrl', (): void => {
|
||||
it('generates the URL with the identifier.', async(): Promise<void> => {
|
||||
expect(generateWebHookUnsubscribeUrl('http://example.com/unsubscribe', '123$456'))
|
||||
.toBe('http://example.com/unsubscribe/123%24456');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#parseWebHookUnsubscribeUrl', (): void => {
|
||||
it('returns the parsed identifier from the URL.', async(): Promise<void> => {
|
||||
expect(parseWebHookUnsubscribeUrl('http://example.com/unsubscribe/123%24456')).toBe('123$456');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
isWebHook2021Channel,
|
||||
WebHookSubscription2021,
|
||||
} from '../../../../../src/server/notifications/WebHookSubscription2021/WebHookSubscription2021';
|
||||
import { joinUrl } from '../../../../../src/util/PathUtil';
|
||||
import { NOTIFY, RDF } from '../../../../../src/util/Vocabularies';
|
||||
import quad = DataFactory.quad;
|
||||
import blankNode = DataFactory.blankNode;
|
||||
@@ -41,7 +40,6 @@ describe('A WebHookSubscription2021', (): void => {
|
||||
let channel: WebHookSubscription2021Channel;
|
||||
const route = new AbsolutePathInteractionRoute('http://example.com/webhooks/');
|
||||
const webIdRoute = new RelativePathInteractionRoute(route, '/webid');
|
||||
const unsubscribeRoute = new RelativePathInteractionRoute(route, '/unsubscribe');
|
||||
let stateHandler: jest.Mocked<StateHandler>;
|
||||
let channelType: WebHookSubscription2021;
|
||||
|
||||
@@ -51,7 +49,7 @@ describe('A WebHookSubscription2021', (): void => {
|
||||
data.addQuad(quad(subject, NOTIFY.terms.topic, namedNode(topic)));
|
||||
data.addQuad(quad(subject, NOTIFY.terms.target, namedNode(target)));
|
||||
|
||||
const id = '4c9b88c1-7502-4107-bb79-2a3a590c7aa3:https://storage.example/resource';
|
||||
const id = 'http://example.com/webhooks/4c9b88c1-7502-4107-bb79-2a3a590c7aa3';
|
||||
channel = {
|
||||
id,
|
||||
type: NOTIFY.WebHookSubscription2021,
|
||||
@@ -59,14 +57,14 @@ describe('A WebHookSubscription2021', (): void => {
|
||||
target,
|
||||
webId: 'http://example.org/alice',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
unsubscribe_endpoint: joinUrl(unsubscribeRoute.getPath(), encodeURIComponent(id)),
|
||||
unsubscribe_endpoint: id,
|
||||
};
|
||||
|
||||
stateHandler = {
|
||||
handleSafe: jest.fn(),
|
||||
} as any;
|
||||
|
||||
channelType = new WebHookSubscription2021(route, webIdRoute, unsubscribeRoute, stateHandler);
|
||||
channelType = new WebHookSubscription2021(route, webIdRoute, stateHandler);
|
||||
});
|
||||
|
||||
it('exposes a utility function to verify if a channel is a webhook channel.', async(): Promise<void> => {
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('A WebHookUnsubscriber', (): void => {
|
||||
beforeEach(async(): Promise<void> => {
|
||||
operation = {
|
||||
method: 'DELETE',
|
||||
target: { path: 'http://example.com/.notifications/webhooks/unsubscribe/134' },
|
||||
target: { path: 'http://example.com/.notifications/webhooks/134' },
|
||||
preferences: {},
|
||||
body: new BasicRepresentation(),
|
||||
};
|
||||
@@ -58,6 +58,6 @@ describe('A WebHookUnsubscriber', (): void => {
|
||||
await expect(unsubscriber.handle({ operation, request, response }))
|
||||
.resolves.toEqual(new ResetResponseDescription());
|
||||
expect(storage.delete).toHaveBeenCalledTimes(1);
|
||||
expect(storage.delete).toHaveBeenLastCalledWith('134');
|
||||
expect(storage.delete).toHaveBeenLastCalledWith('http://example.com/.notifications/webhooks/134');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('A WebSocketSubscription2021', (): void => {
|
||||
data.addQuad(quad(subject, RDF.terms.type, NOTIFY.terms.WebSocketSubscription2021));
|
||||
data.addQuad(quad(subject, NOTIFY.terms.topic, namedNode(topic)));
|
||||
|
||||
const id = '4c9b88c1-7502-4107-bb79-2a3a590c7aa3:https://storage.example/resource';
|
||||
const id = 'http://example.com/foo/4c9b88c1-7502-4107-bb79-2a3a590c7aa3';
|
||||
channel = {
|
||||
id,
|
||||
type: NOTIFY.WebSocketSubscription2021,
|
||||
|
||||
Reference in New Issue
Block a user