diff --git a/src/ldp/operations/SimpleDeleteOperationHandler.ts b/src/ldp/operations/SimpleDeleteOperationHandler.ts new file mode 100644 index 000000000..fd36f5323 --- /dev/null +++ b/src/ldp/operations/SimpleDeleteOperationHandler.ts @@ -0,0 +1,25 @@ +import { Operation } from './Operation'; +import { OperationHandler } from './OperationHandler'; +import { ResourceStore } from '../../storage/ResourceStore'; +import { ResponseDescription } from './ResponseDescription'; +import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError'; + +export class SimpleDeleteOperationHandler extends OperationHandler { + private readonly store: ResourceStore; + + public constructor(store: ResourceStore) { + super(); + this.store = store; + } + + public async canHandle(input: Operation): Promise { + if (input.method !== 'DELETE') { + throw new UnsupportedHttpError('This handler only supports DELETE operations.'); + } + } + + public async handle(input: Operation): Promise { + await this.store.deleteResource(input.target); + return { identifier: input.target }; + } +} diff --git a/src/ldp/operations/SimpleGetOperationHandler.ts b/src/ldp/operations/SimpleGetOperationHandler.ts new file mode 100644 index 000000000..1b6abdd50 --- /dev/null +++ b/src/ldp/operations/SimpleGetOperationHandler.ts @@ -0,0 +1,25 @@ +import { Operation } from './Operation'; +import { OperationHandler } from './OperationHandler'; +import { ResourceStore } from '../../storage/ResourceStore'; +import { ResponseDescription } from './ResponseDescription'; +import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError'; + +export class SimpleGetOperationHandler extends OperationHandler { + private readonly store: ResourceStore; + + public constructor(store: ResourceStore) { + super(); + this.store = store; + } + + public async canHandle(input: Operation): Promise { + if (input.method !== 'GET') { + throw new UnsupportedHttpError('This handler only supports GET operations.'); + } + } + + public async handle(input: Operation): Promise { + const body = await this.store.getRepresentation(input.target, input.preferences); + return { identifier: input.target, body }; + } +} diff --git a/src/ldp/operations/SimplePostOperationHandler.ts b/src/ldp/operations/SimplePostOperationHandler.ts new file mode 100644 index 000000000..f375a77d1 --- /dev/null +++ b/src/ldp/operations/SimplePostOperationHandler.ts @@ -0,0 +1,28 @@ +import { Operation } from './Operation'; +import { OperationHandler } from './OperationHandler'; +import { ResourceStore } from '../../storage/ResourceStore'; +import { ResponseDescription } from './ResponseDescription'; +import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError'; + +export class SimplePostOperationHandler extends OperationHandler { + private readonly store: ResourceStore; + + public constructor(store: ResourceStore) { + super(); + this.store = store; + } + + public async canHandle(input: Operation): Promise { + if (input.method !== 'POST') { + throw new UnsupportedHttpError('This handler only supports POST operations.'); + } + if (!input.body) { + throw new UnsupportedHttpError('POST operations require a body.'); + } + } + + public async handle(input: Operation): Promise { + const identifier = await this.store.addResource(input.target, input.body); + return { identifier }; + } +} diff --git a/test/unit/ldp/operations/SimpleDeleteOperationHandler.test.ts b/test/unit/ldp/operations/SimpleDeleteOperationHandler.test.ts new file mode 100644 index 000000000..9d9ebe7b2 --- /dev/null +++ b/test/unit/ldp/operations/SimpleDeleteOperationHandler.test.ts @@ -0,0 +1,24 @@ +import { Operation } from '../../../../src/ldp/operations/Operation'; +import { ResourceStore } from '../../../../src/storage/ResourceStore'; +import { SimpleDeleteOperationHandler } from '../../../../src/ldp/operations/SimpleDeleteOperationHandler'; +import { UnsupportedHttpError } from '../../../../src/util/errors/UnsupportedHttpError'; + +describe('A SimpleDeleteOperationHandler', (): void => { + const store = {} as unknown as ResourceStore; + const handler = new SimpleDeleteOperationHandler(store); + beforeEach(async(): Promise => { + // eslint-disable-next-line @typescript-eslint/no-empty-function + store.deleteResource = jest.fn(async(): Promise => {}); + }); + + it('only supports GET operations.', async(): Promise => { + await expect(handler.canHandle({ method: 'DELETE' } as Operation)).resolves.toBeUndefined(); + await expect(handler.canHandle({ method: 'GET' } as Operation)).rejects.toThrow(UnsupportedHttpError); + }); + + it('deletes the resource from the store and returns its identifier.', async(): Promise => { + await expect(handler.handle({ target: { path: 'url' }} as Operation)).resolves.toEqual({ identifier: { path: 'url' }}); + expect(store.deleteResource).toHaveBeenCalledTimes(1); + expect(store.deleteResource).toHaveBeenLastCalledWith({ path: 'url' }); + }); +}); diff --git a/test/unit/ldp/operations/SimpleGetOperationHandler.test.ts b/test/unit/ldp/operations/SimpleGetOperationHandler.test.ts new file mode 100644 index 000000000..ec334032e --- /dev/null +++ b/test/unit/ldp/operations/SimpleGetOperationHandler.test.ts @@ -0,0 +1,23 @@ +import { Operation } from '../../../../src/ldp/operations/Operation'; +import { Representation } from '../../../../src/ldp/representation/Representation'; +import { ResourceStore } from '../../../../src/storage/ResourceStore'; +import { SimpleGetOperationHandler } from '../../../../src/ldp/operations/SimpleGetOperationHandler'; +import { UnsupportedHttpError } from '../../../../src/util/errors/UnsupportedHttpError'; + +describe('A SimpleGetOperationHandler', (): void => { + const store = { + getRepresentation: async(): Promise => ({ dataType: 'quad' } as Representation), + } as unknown as ResourceStore; + const handler = new SimpleGetOperationHandler(store); + + it('only supports GET operations.', async(): Promise => { + await expect(handler.canHandle({ method: 'GET' } as Operation)).resolves.toBeUndefined(); + await expect(handler.canHandle({ method: 'POST' } as Operation)).rejects.toThrow(UnsupportedHttpError); + }); + + it('returns the representation from the store with the input identifier.', async(): Promise => { + await expect(handler.handle({ target: { path: 'url' }} as Operation)).resolves.toEqual( + { identifier: { path: 'url' }, body: { dataType: 'quad' }}, + ); + }); +}); diff --git a/test/unit/ldp/operations/SimplePostOperationHandler.test.ts b/test/unit/ldp/operations/SimplePostOperationHandler.test.ts new file mode 100644 index 000000000..733bd024f --- /dev/null +++ b/test/unit/ldp/operations/SimplePostOperationHandler.test.ts @@ -0,0 +1,22 @@ +import { Operation } from '../../../../src/ldp/operations/Operation'; +import { ResourceIdentifier } from '../../../../src/ldp/representation/ResourceIdentifier'; +import { ResourceStore } from '../../../../src/storage/ResourceStore'; +import { SimplePostOperationHandler } from '../../../../src/ldp/operations/SimplePostOperationHandler'; +import { UnsupportedHttpError } from '../../../../src/util/errors/UnsupportedHttpError'; + +describe('A SimplePostOperationHandler', (): void => { + const store = { + addResource: async(): Promise => ({ path: 'newPath' } as ResourceIdentifier), + } as unknown as ResourceStore; + const handler = new SimplePostOperationHandler(store); + + it('only supports POST operations with a body.', async(): Promise => { + await expect(handler.canHandle({ method: 'POST', body: { dataType: 'test' }} as Operation)).resolves.toBeUndefined(); + await expect(handler.canHandle({ method: 'GET', body: { dataType: 'test' }} as Operation)).rejects.toThrow(UnsupportedHttpError); + await expect(handler.canHandle({ method: 'POST' } as Operation)).rejects.toThrow(UnsupportedHttpError); + }); + + it('adds the given representation to the store and returns the new identifier.', async(): Promise => { + await expect(handler.handle({ method: 'POST', body: { dataType: 'test' }} as Operation)).resolves.toEqual({ identifier: { path: 'newPath' }}); + }); +});