mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
change: Refactor AllVoidCompositeHandler into SequenceHandler.
This commit is contained in:
parent
7cae14acf7
commit
ba47ce7951
@ -20,8 +20,8 @@
|
||||
},
|
||||
{
|
||||
"@id": "urn:solid-server:default:HttpHandler",
|
||||
"@type": "AllVoidCompositeHandler",
|
||||
"AllVoidCompositeHandler:_handlers": [
|
||||
"@type": "SequenceHandler",
|
||||
"SequenceHandler:_handlers": [
|
||||
{
|
||||
"@id": "urn:solid-server:default:Middleware"
|
||||
},
|
||||
|
@ -3,8 +3,8 @@
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "urn:solid-server:default:Initializer",
|
||||
"@type": "AllVoidCompositeHandler",
|
||||
"AllVoidCompositeHandler:_handlers": [
|
||||
"@type": "SequenceHandler",
|
||||
"SequenceHandler:_handlers": [
|
||||
{
|
||||
"@type": "LoggerInitializer",
|
||||
"LoggerInitializer:_loggerFactory": {
|
||||
|
@ -3,8 +3,8 @@
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "urn:solid-server:default:MetadataSerializer",
|
||||
"@type": "AllVoidCompositeHandler",
|
||||
"AllVoidCompositeHandler:_handlers": [
|
||||
"@type": "SequenceHandler",
|
||||
"SequenceHandler:_handlers": [
|
||||
{
|
||||
"@type": "MappedMetadataWriter",
|
||||
"MappedMetadataWriter:_headerMap": [
|
||||
|
@ -3,8 +3,8 @@
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "urn:solid-server:default:Middleware",
|
||||
"@type": "AllVoidCompositeHandler",
|
||||
"AllVoidCompositeHandler:_handlers": [
|
||||
"@type": "SequenceHandler",
|
||||
"SequenceHandler:_handlers": [
|
||||
{
|
||||
"@type": "CorsHandler",
|
||||
"CorsHandler:_options_methods": [
|
||||
|
@ -181,10 +181,10 @@ export * from './util/locking/SingleThreadedResourceLocker';
|
||||
export * from './util/locking/WrappedExpiringResourceLocker';
|
||||
|
||||
// Util
|
||||
export * from './util/AllVoidCompositeHandler';
|
||||
export * from './util/AsyncHandler';
|
||||
export * from './util/FirstCompositeHandler';
|
||||
export * from './util/HeaderUtil';
|
||||
export * from './util/PathUtil';
|
||||
export * from './util/QuadUtil';
|
||||
export * from './util/SequenceHandler';
|
||||
export * from './util/StreamUtil';
|
||||
|
@ -1,24 +0,0 @@
|
||||
import { AsyncHandler } from './AsyncHandler';
|
||||
|
||||
/**
|
||||
* A composite handler that runs all of its handlers independent of their result.
|
||||
* The `canHandle` check of this handler will always succeed.
|
||||
*/
|
||||
export class AllVoidCompositeHandler<TIn> extends AsyncHandler<TIn> {
|
||||
private readonly handlers: AsyncHandler<TIn>[];
|
||||
|
||||
public constructor(handlers: AsyncHandler<TIn>[]) {
|
||||
super();
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
public async handle(input: TIn): Promise<void> {
|
||||
for (const handler of this.handlers) {
|
||||
try {
|
||||
await handler.handleSafe(input);
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Simple interface for classes that can potentially handle a specific kind of data asynchronously.
|
||||
*/
|
||||
export abstract class AsyncHandler<TInput = void, TOutput = void> {
|
||||
export abstract class AsyncHandler<TIn = void, TOut = void> {
|
||||
/**
|
||||
* Checks if the input data can be handled by this class.
|
||||
* Throws an error if it can't handle the data.
|
||||
@ -10,7 +10,7 @@ export abstract class AsyncHandler<TInput = void, TOutput = void> {
|
||||
* @returns A promise resolving if this input can be handled, rejecting with an Error if not.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public async canHandle(input: TInput): Promise<void> {
|
||||
public async canHandle(input: TIn): Promise<void> {
|
||||
// Support any input by default
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ export abstract class AsyncHandler<TInput = void, TOutput = void> {
|
||||
*
|
||||
* @returns A promise resolving when the handling is finished. Return value depends on the given type.
|
||||
*/
|
||||
public abstract handle(input: TInput): Promise<TOutput>;
|
||||
public abstract handle(input: TIn): Promise<TOut>;
|
||||
|
||||
/**
|
||||
* Helper function that first runs the canHandle function followed by the handle function.
|
||||
@ -30,7 +30,7 @@ export abstract class AsyncHandler<TInput = void, TOutput = void> {
|
||||
*
|
||||
* @returns The result of the handle function of the handler.
|
||||
*/
|
||||
public async handleSafe(data: TInput): Promise<TOutput> {
|
||||
public async handleSafe(data: TIn): Promise<TOut> {
|
||||
await this.canHandle(data);
|
||||
|
||||
return this.handle(data);
|
||||
|
32
src/util/SequenceHandler.ts
Normal file
32
src/util/SequenceHandler.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { AsyncHandler } from './AsyncHandler';
|
||||
|
||||
/**
|
||||
* A composite handler that will try to run all supporting handlers sequentially
|
||||
* and return the value of the last supported handler.
|
||||
* The `canHandle` check of this handler will always succeed.
|
||||
*/
|
||||
export class SequenceHandler<TIn = void, TOut = void> extends AsyncHandler<TIn, TOut | undefined> {
|
||||
private readonly handlers: AsyncHandler<TIn, TOut>[];
|
||||
|
||||
public constructor(handlers: AsyncHandler<TIn, TOut>[]) {
|
||||
super();
|
||||
this.handlers = [ ...handlers ];
|
||||
}
|
||||
|
||||
public async handle(input: TIn): Promise<TOut | undefined> {
|
||||
let result: TOut | undefined;
|
||||
for (const handler of this.handlers) {
|
||||
let supported: boolean;
|
||||
try {
|
||||
await handler.canHandle(input);
|
||||
supported = true;
|
||||
} catch {
|
||||
supported = false;
|
||||
}
|
||||
if (supported) {
|
||||
result = await handler.handle(input);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ import type {
|
||||
OperationHandler,
|
||||
} from '../../src/index';
|
||||
import {
|
||||
AcceptPreferenceParser, AllVoidCompositeHandler,
|
||||
AcceptPreferenceParser,
|
||||
BasicMetadataExtractor,
|
||||
BasicRequestParser,
|
||||
BasicResponseWriter,
|
||||
@ -35,6 +35,7 @@ import {
|
||||
PutOperationHandler,
|
||||
RawBodyParser,
|
||||
RepresentationConvertingStore,
|
||||
SequenceHandler,
|
||||
SingleThreadedResourceLocker,
|
||||
SlugParser,
|
||||
SparqlUpdatePatchHandler,
|
||||
@ -117,7 +118,7 @@ export const getOperationHandler = (store: ResourceStore): OperationHandler => {
|
||||
};
|
||||
|
||||
export const getResponseWriter = (): ResponseWriter => {
|
||||
const serializer = new AllVoidCompositeHandler([
|
||||
const serializer = new SequenceHandler([
|
||||
new MappedMetadataWriter({
|
||||
[CONTENT_TYPE]: 'content-type',
|
||||
[HTTP.location]: 'location',
|
||||
|
@ -1,30 +0,0 @@
|
||||
import { AllVoidCompositeHandler } from '../../../src/util/AllVoidCompositeHandler';
|
||||
import type { AsyncHandler } from '../../../src/util/AsyncHandler';
|
||||
|
||||
describe('An AllVoidCompositeHandler', (): void => {
|
||||
let handler1: AsyncHandler<string>;
|
||||
let handler2: AsyncHandler<string>;
|
||||
let composite: AllVoidCompositeHandler<string>;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
handler1 = { handleSafe: jest.fn() } as any;
|
||||
handler2 = { handleSafe: jest.fn() } as any;
|
||||
|
||||
composite = new AllVoidCompositeHandler<string>([ handler1, handler2 ]);
|
||||
});
|
||||
|
||||
it('can handle all input.', async(): Promise<void> => {
|
||||
await expect(composite.canHandle('test')).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('runs all handlers without caring about their result.', async(): Promise<void> => {
|
||||
handler1.handleSafe = jest.fn(async(): Promise<void> => {
|
||||
throw new Error('error');
|
||||
});
|
||||
await expect(composite.handleSafe('test')).resolves.toBeUndefined();
|
||||
expect(handler1.handleSafe).toHaveBeenCalledTimes(1);
|
||||
expect(handler1.handleSafe).toHaveBeenLastCalledWith('test');
|
||||
expect(handler2.handleSafe).toHaveBeenCalledTimes(1);
|
||||
expect(handler2.handleSafe).toHaveBeenLastCalledWith('test');
|
||||
});
|
||||
});
|
64
test/unit/util/SequenceHandler.test.ts
Normal file
64
test/unit/util/SequenceHandler.test.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import type { AsyncHandler } from '../../../src/util/AsyncHandler';
|
||||
import { SequenceHandler } from '../../../src/util/SequenceHandler';
|
||||
|
||||
describe('A SequenceHandler', (): void => {
|
||||
const handlers: jest.Mocked<AsyncHandler<string, string>>[] = [
|
||||
{
|
||||
canHandle: jest.fn(),
|
||||
handle: jest.fn().mockResolvedValue('0'),
|
||||
} as any,
|
||||
{
|
||||
canHandle: jest.fn().mockRejectedValue(new Error('not supported')),
|
||||
handle: jest.fn().mockRejectedValue(new Error('should not be called')),
|
||||
} as any,
|
||||
{
|
||||
canHandle: jest.fn(),
|
||||
handle: jest.fn().mockResolvedValue('2'),
|
||||
} as any,
|
||||
];
|
||||
let composite: SequenceHandler<string, string>;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
composite = new SequenceHandler<string, string>(handlers);
|
||||
});
|
||||
|
||||
it('can handle all input.', async(): Promise<void> => {
|
||||
await expect(composite.canHandle('test')).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('runs all supported handlers.', async(): Promise<void> => {
|
||||
await composite.handleSafe('test');
|
||||
|
||||
expect(handlers[0].canHandle).toHaveBeenCalledTimes(1);
|
||||
expect(handlers[0].canHandle).toHaveBeenLastCalledWith('test');
|
||||
expect(handlers[0].handle).toHaveBeenCalledTimes(1);
|
||||
expect(handlers[0].handle).toHaveBeenLastCalledWith('test');
|
||||
|
||||
expect(handlers[1].canHandle).toHaveBeenCalledTimes(1);
|
||||
expect(handlers[1].canHandle).toHaveBeenLastCalledWith('test');
|
||||
expect(handlers[1].handle).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(handlers[2].canHandle).toHaveBeenCalledTimes(1);
|
||||
expect(handlers[2].canHandle).toHaveBeenLastCalledWith('test');
|
||||
expect(handlers[2].handle).toHaveBeenCalledTimes(1);
|
||||
expect(handlers[2].handle).toHaveBeenLastCalledWith('test');
|
||||
});
|
||||
|
||||
it('returns the result of the last supported handler.', async(): Promise<void> => {
|
||||
await expect(composite.handleSafe('test')).resolves.toBe('2');
|
||||
|
||||
handlers[2].canHandle.mockRejectedValueOnce(new Error('not supported'));
|
||||
await expect(composite.handleSafe('test')).resolves.toBe('0');
|
||||
});
|
||||
|
||||
it('returns undefined if no handler is supported.', async(): Promise<void> => {
|
||||
handlers[0].canHandle.mockRejectedValueOnce(new Error('not supported'));
|
||||
handlers[2].canHandle.mockRejectedValueOnce(new Error('not supported'));
|
||||
await expect(composite.handleSafe('test')).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('errors if a handler errors.', async(): Promise<void> => {
|
||||
handlers[2].handle.mockRejectedValueOnce(new Error('failure'));
|
||||
await expect(composite.handleSafe('test')).rejects.toThrow('failure');
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user