From 130a91fdfb12210d102ecba8f4f409ecbd3719bd Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Fri, 28 May 2021 16:39:28 +0200 Subject: [PATCH] fix: Remove unneeded pod files --- config/http/README.md | 2 +- config/http/handler/default.json | 3 +- config/pod/handler/dynamic.json | 2 - config/pod/handler/http/manager.json | 15 ---- config/pod/handler/static.json | 12 +-- src/pods/PodManagerHttpHandler.ts | 70 ---------------- src/pods/settings/PodSettingsJsonParser.ts | 41 ---------- src/pods/settings/PodSettingsParser.ts | 8 -- test/integration/PodCreation.test.ts | 75 ----------------- test/unit/pods/PodManagerHttpHandler.test.ts | 80 ------------------- .../settings/PodSettingsJsonParser.test.ts | 50 ------------ 11 files changed, 4 insertions(+), 354 deletions(-) delete mode 100644 config/pod/handler/http/manager.json delete mode 100644 src/pods/PodManagerHttpHandler.ts delete mode 100644 src/pods/settings/PodSettingsJsonParser.ts delete mode 100644 src/pods/settings/PodSettingsParser.ts delete mode 100644 test/integration/PodCreation.test.ts delete mode 100644 test/unit/pods/PodManagerHttpHandler.test.ts delete mode 100644 test/unit/pods/settings/PodSettingsJsonParser.test.ts diff --git a/config/http/README.md b/config/http/README.md index 50b27b51a..88d33711d 100644 --- a/config/http/README.md +++ b/config/http/README.md @@ -3,7 +3,7 @@ Options related to the base support of HTTP requests by the server. ## Handler Sets up all the handlers a request will potentially pass through. -* *default*: The full setup, that is middleware + static files + pod creation + IDP + LDP. +* *default*: The full setup, that is middleware + static files + IDP + LDP. * *simple*: A simpler setup in which the IDP is disabled. ## Middleware diff --git a/config/http/handler/default.json b/config/http/handler/default.json index e18b07c23..e3ceae77a 100644 --- a/config/http/handler/default.json +++ b/config/http/handler/default.json @@ -11,12 +11,11 @@ "@type": "WaterfallHandler", "handlers": [ { "@id": "urn:solid-server:default:StaticAssetHandler" }, - { "@id": "urn:solid-server:default:PodManagerHandler" }, { "@id": "urn:solid-server:default:IdentityProviderHandler" }, { "@id": "urn:solid-server:default:LdpHandler" } ] } ] - }, + } ] } diff --git a/config/pod/handler/dynamic.json b/config/pod/handler/dynamic.json index e87108e97..fb0bdfe48 100644 --- a/config/pod/handler/dynamic.json +++ b/config/pod/handler/dynamic.json @@ -1,7 +1,6 @@ { "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld", "import": [ - "files-scs:config/pod/handler/http/manager.json", "files-scs:config/pod/handler/pod-generators/templated.json", "files-scs:config/pod/handler/resource-generators/templated.json" ], @@ -11,7 +10,6 @@ "comment": "Generates pods based on config templates and stores the results in the correct places", "@id": "urn:solid-server:default:PodManager", "@type": "ConfigPodManager", - "idGenerator": { "@id": "urn:solid-server:default:IdentifierGenerator" }, "podGenerator": { "@id": "urn:solid-server:default:PodGenerator" }, "routingStorage": { "@id": "urn:solid-server:default:PodRoutingStorage" }, "resourcesGenerator": { "@id": "urn:solid-server:default:ResourcesGenerator" } diff --git a/config/pod/handler/http/manager.json b/config/pod/handler/http/manager.json deleted file mode 100644 index e5db2e1ff..000000000 --- a/config/pod/handler/http/manager.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld", - "@graph": [ - { - "comment": "The main entry point into pod provisioning.", - "@id": "urn:solid-server:default:PodManagerHandler", - "@type": "PodManagerHttpHandler", - "args_requestPath": "/pods", - "args_requestParser": { "@id": "urn:solid-server:default:RequestParser" }, - "args_podSettingsParser": { "@type": "PodSettingsJsonParser" }, - "args_manager": { "@id": "urn:solid-server:default:PodManager" }, - "args_responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" } - } - ] -} diff --git a/config/pod/handler/static.json b/config/pod/handler/static.json index d75c7bd57..d9a50f4e7 100644 --- a/config/pod/handler/static.json +++ b/config/pod/handler/static.json @@ -1,7 +1,6 @@ { "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^0.0.0/components/context.jsonld", "import": [ - "files-scs:config/pod/handler/http/manager.json", "files-scs:config/pod/handler/resource-generators/templated.json" ], "@graph": [ @@ -9,15 +8,8 @@ "comment": "Stores all new resources for a pod in the default resource store under the generated identifier.", "@id": "urn:solid-server:default:PodManager", "@type": "GeneratedPodManager", - "store": { - "@id": "urn:solid-server:default:ResourceStore" - }, - "idGenerator": { - "@id": "urn:solid-server:default:IdentifierGenerator" - }, - "resourcesGenerator": { - "@id": "urn:solid-server:default:ResourcesGenerator" - } + "store": { "@id": "urn:solid-server:default:ResourceStore" }, + "resourcesGenerator": { "@id": "urn:solid-server:default:ResourcesGenerator" } } ] } diff --git a/src/pods/PodManagerHttpHandler.ts b/src/pods/PodManagerHttpHandler.ts deleted file mode 100644 index 5780dfea7..000000000 --- a/src/pods/PodManagerHttpHandler.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { RequestParser } from '../ldp/http/RequestParser'; -import { CreatedResponseDescription } from '../ldp/http/response/CreatedResponseDescription'; -import type { ResponseWriter } from '../ldp/http/ResponseWriter'; -import { HttpHandler } from '../server/HttpHandler'; -import type { HttpRequest } from '../server/HttpRequest'; -import type { HttpResponse } from '../server/HttpResponse'; -import { BadRequestHttpError } from '../util/errors/BadRequestHttpError'; -import { isNativeError } from '../util/errors/ErrorUtil'; -import { InternalServerError } from '../util/errors/InternalServerError'; -import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError'; -import type { PodManager } from './PodManager'; -import type { PodSettingsParser } from './settings/PodSettingsParser'; - -export interface PodHttpHandlerArgs { - /** The path on which this handler should intercept requests. Should start with a slash. */ - requestPath: string; - /** Parses the incoming request. */ - requestParser: RequestParser; - /** Parses the data stream to PodSettings. */ - podSettingsParser: PodSettingsParser; - /** Handles the pod management. */ - manager: PodManager; - /** Writes the outgoing response. */ - responseWriter: ResponseWriter; -} - -/** - * An HTTP handler that listens to requests to a specific path for pod related requests. - * Handles everything related to pod management from input request to output response. - */ -export class PodManagerHttpHandler extends HttpHandler { - private readonly requestPath!: string; - private readonly requestParser!: RequestParser; - private readonly podSettingsParser!: PodSettingsParser; - private readonly manager!: PodManager; - private readonly responseWriter!: ResponseWriter; - - public constructor(args: PodHttpHandlerArgs) { - super(); - Object.assign(this, args); - } - - public async canHandle({ request }: { request: HttpRequest }): Promise { - if (request.url !== this.requestPath) { - throw new NotImplementedHttpError(`Only requests to ${this.requestPath} are accepted`); - } - } - - public async handle({ request, response }: { request: HttpRequest; response: HttpResponse }): Promise { - try { - if (request.method !== 'POST') { - throw new NotImplementedHttpError('Only POST requests are supported'); - } - const op = await this.requestParser.handleSafe(request); - if (!op.body) { - throw new BadRequestHttpError('A body is required to create a pod'); - } - const settings = await this.podSettingsParser.handleSafe(op.body); - const id = await this.manager.createPod(settings); - - await this.responseWriter.handleSafe({ response, result: new CreatedResponseDescription(id) }); - } catch (error: unknown) { - if (isNativeError(error)) { - await this.responseWriter.handleSafe({ response, result: error }); - } else { - await this.responseWriter.handleSafe({ response, result: new InternalServerError('Unexpected error') }); - } - } - } -} diff --git a/src/pods/settings/PodSettingsJsonParser.ts b/src/pods/settings/PodSettingsJsonParser.ts deleted file mode 100644 index 064b6d53c..000000000 --- a/src/pods/settings/PodSettingsJsonParser.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Representation } from '../../ldp/representation/Representation'; -import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError'; -import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError'; -import { readableToString } from '../../util/StreamUtil'; -import type { PodSettings } from './PodSettings'; -import { PodSettingsParser } from './PodSettingsParser'; -import Dict = NodeJS.Dict; - -const requiredKeys: (keyof PodSettings)[] = [ 'login', 'webId' ]; - -/** - * A parser that extracts PodSettings data from a JSON body. - */ -export class PodSettingsJsonParser extends PodSettingsParser { - public async canHandle(input: Representation): Promise { - if (!input.metadata.contentType || !this.isJSON(input.metadata.contentType)) { - throw new NotImplementedHttpError('Only JSON data is supported'); - } - } - - public async handle(input: Representation): Promise { - const result = JSON.parse(await readableToString(input.data)); - this.isValid(result); - return result; - } - - private isJSON(mediaType: string): boolean { - return mediaType === 'application/json' || mediaType.endsWith('+json'); - } - - /** - * Checks if all the required PodSettings keys are there. - */ - private isValid(data: Dict): asserts data is PodSettings { - for (const key of requiredKeys) { - if (!data[key]) { - throw new BadRequestHttpError(`Input data is missing key ${key}`); - } - } - } -} diff --git a/src/pods/settings/PodSettingsParser.ts b/src/pods/settings/PodSettingsParser.ts deleted file mode 100644 index 564452f09..000000000 --- a/src/pods/settings/PodSettingsParser.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Representation } from '../../ldp/representation/Representation'; -import { AsyncHandler } from '../../util/handlers/AsyncHandler'; -import type { PodSettings } from './PodSettings'; - -/** - * Parser that generates a {@link PodSettings} from the data in the given {@link Representation}. - */ -export abstract class PodSettingsParser extends AsyncHandler { } diff --git a/test/integration/PodCreation.test.ts b/test/integration/PodCreation.test.ts deleted file mode 100644 index 00f2e0e10..000000000 --- a/test/integration/PodCreation.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { Server } from 'http'; -import fetch from 'cross-fetch'; -import type { Initializer } from '../../src/init/Initializer'; -import type { HttpServerFactory } from '../../src/server/HttpServerFactory'; -import { joinFilePath } from '../../src/util/PathUtil'; -import { readableToString } from '../../src/util/StreamUtil'; -import { getPort } from '../util/Util'; -import { getTestConfigPath, instantiateFromConfig } from './Config'; - -const port = getPort('PodCreation'); -const baseUrl = `http://localhost:${port}/`; - -describe('A server with a pod handler', (): void => { - let initializer: Initializer; - let factory: HttpServerFactory; - let server: Server; - const settings = { login: 'alice', webId: 'http://test.com/#alice', name: 'Alice Bob' }; - - beforeAll(async(): Promise => { - const instances = await instantiateFromConfig( - 'urn:solid-server:test:Instances', - getTestConfigPath('server-without-auth.json'), - { - 'urn:solid-server:default:variable:port': port, - 'urn:solid-server:default:variable:baseUrl': baseUrl, - 'urn:solid-server:default:variable:podTemplateFolder': joinFilePath(__dirname, '../assets/templates'), - }, - ) as Record; - ({ factory, initializer } = instances); - await initializer.handleSafe(); - server = factory.startServer(port); - }); - - afterAll(async(): Promise => { - await new Promise((resolve, reject): void => { - server.close((error): void => error ? reject(error) : resolve()); - }); - }); - - it('creates a pod when posting PodSettings to /pods.', async(): Promise => { - const pod = `${baseUrl}${settings.login}/`; - - // Pod should not exist yet - let res = await fetch(pod); - expect(res.status).toBe(404); - - // Create pod call should return the address of the new pod - res = await fetch(`${baseUrl}pods`, { - method: 'POST', - headers: { - 'content-type': 'application/json', - }, - body: JSON.stringify(settings), - }); - expect(res.status).toBe(201); - expect(res.headers.get('location')).toBe(`${baseUrl}${settings.login}/`); - - // Check if all resources are created - res = await fetch(`${pod}.acl`); - expect(res.status).toBe(200); - let body = await readableToString(res.body as any); - expect(body).toContain(`acl:agent <${settings.webId}>`); - - res = await fetch(`${pod}profile/card`); - expect(res.status).toBe(200); - expect(res.headers.get('content-type')).toBe('text/turtle'); - body = await readableToString(res.body as any); - expect(body).toBe(`@prefix foaf: . - -<${settings.webId}> - a foaf:Person ; - foaf:name "${settings.name}". -`); - }); -}); diff --git a/test/unit/pods/PodManagerHttpHandler.test.ts b/test/unit/pods/PodManagerHttpHandler.test.ts deleted file mode 100644 index 97c1f3bba..000000000 --- a/test/unit/pods/PodManagerHttpHandler.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { RequestParser } from '../../../src/ldp/http/RequestParser'; -import { CreatedResponseDescription } from '../../../src/ldp/http/response/CreatedResponseDescription'; -import type { ResponseWriter } from '../../../src/ldp/http/ResponseWriter'; -import type { ResourceIdentifier } from '../../../src/ldp/representation/ResourceIdentifier'; -import type { PodManager } from '../../../src/pods/PodManager'; -import { PodManagerHttpHandler } from '../../../src/pods/PodManagerHttpHandler'; -import type { PodSettingsParser } from '../../../src/pods/settings/PodSettingsParser'; -import type { HttpRequest } from '../../../src/server/HttpRequest'; -import type { HttpResponse } from '../../../src/server/HttpResponse'; -import { BadRequestHttpError } from '../../../src/util/errors/BadRequestHttpError'; -import { InternalServerError } from '../../../src/util/errors/InternalServerError'; -import { NotImplementedHttpError } from '../../../src/util/errors/NotImplementedHttpError'; -import { StaticAsyncHandler } from '../../util/StaticAsyncHandler'; - -describe('A PodManagerHttpHandler', (): void => { - const requestPath = '/pods'; - let requestParser: RequestParser; - let podSettingsParser: PodSettingsParser; - let manager: PodManager; - let responseWriter: ResponseWriter; - let handler: PodManagerHttpHandler; - - beforeEach(async(): Promise => { - requestParser = { handleSafe: jest.fn((): any => 'requestParser') } as any; - podSettingsParser = new StaticAsyncHandler(true, 'podSettingsParser' as any); - manager = { - createPod: jest.fn(), - }; - responseWriter = { handleSafe: jest.fn((): any => 'response') } as any; - handler = new PodManagerHttpHandler({ requestPath, requestParser, podSettingsParser, manager, responseWriter }); - }); - - it('only supports requests to /pods.', async(): Promise => { - const call = handler.canHandle({ request: { url: '/notPods' } as HttpRequest }); - await expect(call).rejects.toThrow('Only requests to /pods are accepted'); - await expect(call).rejects.toThrow(NotImplementedHttpError); - await expect(handler.canHandle({ request: { url: '/pods' } as HttpRequest })).resolves.toBeUndefined(); - }); - - it('writes an error if the request was no POST.', async(): Promise => { - const response = {} as HttpResponse; - await expect(handler.handle({ request: { method: 'GET' } as HttpRequest, response })).resolves.toBeUndefined(); - expect(responseWriter.handleSafe).toHaveBeenCalledTimes(1); - const mockCall = (responseWriter.handleSafe as jest.Mock).mock.calls[0][0]; - expect(mockCall).toEqual({ response, result: expect.any(NotImplementedHttpError) }); - expect(mockCall.result.message).toBe('Only POST requests are supported'); - }); - - it('writes an error if there is no input body.', async(): Promise => { - const response = {} as HttpResponse; - await expect(handler.handle({ request: { method: 'POST' } as HttpRequest, response })).resolves.toBeUndefined(); - expect(responseWriter.handleSafe).toHaveBeenCalledTimes(1); - const mockCall = (responseWriter.handleSafe as jest.Mock).mock.calls[0][0]; - expect(mockCall).toEqual({ response, result: expect.any(BadRequestHttpError) }); - expect(mockCall.result.message).toBe('A body is required to create a pod'); - }); - - it('writes an internal error if a non-error was thrown.', async(): Promise => { - const response = {} as HttpResponse; - (requestParser.handleSafe as jest.Mock).mockImplementationOnce((): any => { - throw 'apple'; - }); - await expect(handler.handle({ request: { method: 'POST' } as HttpRequest, response })).resolves.toBeUndefined(); - expect(responseWriter.handleSafe).toHaveBeenCalledTimes(1); - const mockCall = (responseWriter.handleSafe as jest.Mock).mock.calls[0][0]; - expect(mockCall).toEqual({ response, result: expect.any(InternalServerError) }); - expect(mockCall.result.message).toBe('Unexpected error'); - }); - - it('returns the id of the created pod on success.', async(): Promise => { - const response = {} as HttpResponse; - (manager.createPod as jest.Mock).mockImplementationOnce((): ResourceIdentifier => ({ path: '/pad/to/pod/' })); - (requestParser.handleSafe as jest.Mock).mockImplementationOnce((): any => ({ body: 'data' })); - await expect(handler.handle({ request: { method: 'POST' } as HttpRequest, response })).resolves.toBeUndefined(); - expect(responseWriter.handleSafe).toHaveBeenCalledTimes(1); - expect(responseWriter.handleSafe).toHaveBeenLastCalledWith( - { response, result: new CreatedResponseDescription({ path: '/pad/to/pod/' }) }, - ); - }); -}); diff --git a/test/unit/pods/settings/PodSettingsJsonParser.test.ts b/test/unit/pods/settings/PodSettingsJsonParser.test.ts deleted file mode 100644 index b119a8b90..000000000 --- a/test/unit/pods/settings/PodSettingsJsonParser.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { BasicRepresentation } from '../../../../src/ldp/representation/BasicRepresentation'; -import type { Representation } from '../../../../src/ldp/representation/Representation'; -import { RepresentationMetadata } from '../../../../src/ldp/representation/RepresentationMetadata'; -import { PodSettingsJsonParser } from '../../../../src/pods/settings/PodSettingsJsonParser'; -import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError'; -import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError'; -import { guardedStreamFrom } from '../../../../src/util/StreamUtil'; - -describe('An PodSettingsJsonParser', (): void => { - let metadata: RepresentationMetadata; - let representation: Representation; - const parser = new PodSettingsJsonParser(); - - beforeEach(async(): Promise => { - metadata = new RepresentationMetadata('application/json'); - representation = new BasicRepresentation([], metadata); - }); - - it('only supports JSON data.', async(): Promise => { - metadata.contentType = undefined; - const result = parser.canHandle(representation); - await expect(result).rejects.toThrow(NotImplementedHttpError); - await expect(result).rejects.toThrow('Only JSON data is supported'); - metadata.contentType = 'application/json'; - await expect(parser.canHandle(representation)).resolves.toBeUndefined(); - metadata.contentType = 'application/ld+json'; - await expect(parser.canHandle(representation)).resolves.toBeUndefined(); - }); - - it('errors if required keys are missing.', async(): Promise => { - representation.data = guardedStreamFrom([ JSON.stringify({ login: 'login' }) ]); - const result = parser.handle(representation); - await expect(result).rejects.toThrow(BadRequestHttpError); - await expect(result).rejects.toThrow('Input data is missing key webId'); - }); - - it('generates a User object.', async(): Promise => { - representation.data = guardedStreamFrom([ JSON.stringify({ - login: 'login', - webId: 'webId', - name: 'name', - }) ]); - await expect(parser.handle(representation)).resolves - .toEqual({ - login: 'login', - webId: 'webId', - name: 'name', - }); - }); -});