mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Store original target in error metadata
This commit is contained in:
parent
486241f3d4
commit
419312ee5f
@ -7,20 +7,26 @@
|
|||||||
"@type": "SafeErrorHandler",
|
"@type": "SafeErrorHandler",
|
||||||
"showStackTrace": { "@id": "urn:solid-server:default:variable:showStackTrace" },
|
"showStackTrace": { "@id": "urn:solid-server:default:variable:showStackTrace" },
|
||||||
"errorHandler": {
|
"errorHandler": {
|
||||||
"@type": "WaterfallHandler",
|
"@id": "urn:solid-server:default:TargetExtractorErrorHandler",
|
||||||
"handlers": [
|
"@type": "TargetExtractorErrorHandler",
|
||||||
{
|
"targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" },
|
||||||
"comment": "Internally redirects are created by throwing a specific error, this handler converts them to the correct response.",
|
"errorHandler": {
|
||||||
"@type": "RedirectingErrorHandler"
|
"@id": "urn:solid-server:default:WaterfallErrorHandler",
|
||||||
},
|
"@type": "WaterfallHandler",
|
||||||
{
|
"handlers": [
|
||||||
"comment": "Converts an Error object into a representation for an HTTP response.",
|
{
|
||||||
"@type": "ConvertingErrorHandler",
|
"comment": "Internally redirects are created by throwing a specific error, this handler converts them to the correct response.",
|
||||||
"converter": { "@id": "urn:solid-server:default:UiEnabledConverter" },
|
"@type": "RedirectingErrorHandler"
|
||||||
"preferenceParser": { "@id": "urn:solid-server:default:PreferenceParser" },
|
},
|
||||||
"showStackTrace": { "@id": "urn:solid-server:default:variable:showStackTrace" }
|
{
|
||||||
}
|
"comment": "Converts an Error object into a representation for an HTTP response.",
|
||||||
]
|
"@type": "ConvertingErrorHandler",
|
||||||
|
"converter": { "@id": "urn:solid-server:default:UiEnabledConverter" },
|
||||||
|
"preferenceParser": { "@id": "urn:solid-server:default:PreferenceParser" },
|
||||||
|
"showStackTrace": { "@id": "urn:solid-server:default:variable:showStackTrace" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
30
src/http/output/error/TargetExtractorErrorHandler.ts
Normal file
30
src/http/output/error/TargetExtractorErrorHandler.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { DataFactory } from 'n3';
|
||||||
|
import { SOLID_ERROR } from '../../../util/Vocabularies';
|
||||||
|
import type { TargetExtractor } from '../../input/identifier/TargetExtractor';
|
||||||
|
import type { ResponseDescription } from '../response/ResponseDescription';
|
||||||
|
import type { ErrorHandlerArgs } from './ErrorHandler';
|
||||||
|
import { ErrorHandler } from './ErrorHandler';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds metadata to an error to indicate what the identifier was of the resource originally being targeted.
|
||||||
|
*/
|
||||||
|
export class TargetExtractorErrorHandler extends ErrorHandler {
|
||||||
|
protected readonly errorHandler: ErrorHandler;
|
||||||
|
protected readonly targetExtractor: TargetExtractor;
|
||||||
|
|
||||||
|
public constructor(errorHandler: ErrorHandler, targetExtractor: TargetExtractor) {
|
||||||
|
super();
|
||||||
|
this.errorHandler = errorHandler;
|
||||||
|
this.targetExtractor = targetExtractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async canHandle(input: ErrorHandlerArgs): Promise<void> {
|
||||||
|
return this.errorHandler.canHandle(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async handle(input: ErrorHandlerArgs): Promise<ResponseDescription> {
|
||||||
|
const target = await this.targetExtractor.handleSafe(input);
|
||||||
|
input.error.metadata.add(SOLID_ERROR.terms.target, DataFactory.namedNode(target.path));
|
||||||
|
return this.errorHandler.handle(input);
|
||||||
|
}
|
||||||
|
}
|
@ -100,6 +100,7 @@ export * from './http/output/error/ConvertingErrorHandler';
|
|||||||
export * from './http/output/error/ErrorHandler';
|
export * from './http/output/error/ErrorHandler';
|
||||||
export * from './http/output/error/RedirectingErrorHandler';
|
export * from './http/output/error/RedirectingErrorHandler';
|
||||||
export * from './http/output/error/SafeErrorHandler';
|
export * from './http/output/error/SafeErrorHandler';
|
||||||
|
export * from './http/output/error/TargetExtractorErrorHandler';
|
||||||
|
|
||||||
// HTTP/Output/Metadata
|
// HTTP/Output/Metadata
|
||||||
export * from './http/output/metadata/AllowAcceptHeaderWriter';
|
export * from './http/output/metadata/AllowAcceptHeaderWriter';
|
||||||
|
@ -283,6 +283,7 @@ export const SOLID_ERROR = createVocabulary(
|
|||||||
'errorCode',
|
'errorCode',
|
||||||
'errorResponse',
|
'errorResponse',
|
||||||
'stack',
|
'stack',
|
||||||
|
'target',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Used to pass parameters to error templates
|
// Used to pass parameters to error templates
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
import type { TargetExtractor } from '../../../../../src/http/input/identifier/TargetExtractor';
|
||||||
|
import type { ErrorHandler, ErrorHandlerArgs } from '../../../../../src/http/output/error/ErrorHandler';
|
||||||
|
import { TargetExtractorErrorHandler } from '../../../../../src/http/output/error/TargetExtractorErrorHandler';
|
||||||
|
import type { ResourceIdentifier } from '../../../../../src/http/representation/ResourceIdentifier';
|
||||||
|
import { NotFoundHttpError } from '../../../../../src/util/errors/NotFoundHttpError';
|
||||||
|
import { SOLID_ERROR } from '../../../../../src/util/Vocabularies';
|
||||||
|
|
||||||
|
describe('A TargetExtractorErrorHandler', (): void => {
|
||||||
|
const identifier: ResourceIdentifier = { path: 'http://example.com/foo' };
|
||||||
|
let input: ErrorHandlerArgs;
|
||||||
|
let source: jest.Mocked<ErrorHandler>;
|
||||||
|
let targetExtractor: jest.Mocked<TargetExtractor>;
|
||||||
|
let handler: TargetExtractorErrorHandler;
|
||||||
|
|
||||||
|
beforeEach(async(): Promise<void> => {
|
||||||
|
input = {
|
||||||
|
request: 'request' as any,
|
||||||
|
error: new NotFoundHttpError(),
|
||||||
|
};
|
||||||
|
|
||||||
|
source = {
|
||||||
|
canHandle: jest.fn(),
|
||||||
|
handle: jest.fn().mockResolvedValue('response'),
|
||||||
|
} satisfies Partial<ErrorHandler> as any;
|
||||||
|
|
||||||
|
targetExtractor = {
|
||||||
|
handleSafe: jest.fn().mockResolvedValue(identifier),
|
||||||
|
} satisfies Partial<TargetExtractor> as any;
|
||||||
|
|
||||||
|
handler = new TargetExtractorErrorHandler(source, targetExtractor);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can handle input its source can handle.', async(): Promise<void> => {
|
||||||
|
await expect(handler.canHandle(input)).resolves.toBeUndefined();
|
||||||
|
expect(source.canHandle).toHaveBeenLastCalledWith(input);
|
||||||
|
|
||||||
|
const error = new Error('bad data');
|
||||||
|
source.canHandle.mockRejectedValueOnce(error);
|
||||||
|
await expect(handler.canHandle(input)).rejects.toThrow(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds the identifier as metadata.', async(): Promise<void> => {
|
||||||
|
await expect(handler.handle(input)).resolves.toBe('response');
|
||||||
|
expect(input.error.metadata.get(SOLID_ERROR.terms.target)?.value).toEqual(identifier.path);
|
||||||
|
expect(targetExtractor.handleSafe).toHaveBeenLastCalledWith(input);
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user