mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Add metadata to errors
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { HttpResponse } from '../../../src/server/HttpResponse';
|
||||
import { BadRequestHttpError } from '../../../src/util/errors/BadRequestHttpError';
|
||||
import { ContentType,
|
||||
addHeader,
|
||||
import { ContentType } from '../../../src/util/Header';
|
||||
import { addHeader,
|
||||
hasScheme,
|
||||
matchesAuthorizationScheme,
|
||||
parseAccept,
|
||||
|
||||
@@ -2,6 +2,8 @@ import { promises as fsPromises } from 'fs';
|
||||
import type { TargetExtractor } from '../../../src/http/input/identifier/TargetExtractor';
|
||||
import type { ResourceIdentifier } from '../../../src/http/representation/ResourceIdentifier';
|
||||
import type { HttpRequest } from '../../../src/server/HttpRequest';
|
||||
import { BadRequestHttpError } from '../../../src/util/errors/BadRequestHttpError';
|
||||
import { extractErrorTerms } from '../../../src/util/errors/HttpErrorUtil';
|
||||
import {
|
||||
absoluteFilePath,
|
||||
createSubdomainRegexp,
|
||||
@@ -218,8 +220,15 @@ describe('PathUtil', (): void => {
|
||||
|
||||
it('errors if the target is outside of the server scope.', async(): Promise<void> => {
|
||||
targetExtractor.handleSafe.mockResolvedValueOnce({ path: 'http://somewhere.else/resource' });
|
||||
await expect(getRelativeUrl(baseUrl, request, targetExtractor)).rejects
|
||||
.toThrow(expect.objectContaining({ errorCode: 'E0001', details: { path: 'http://somewhere.else/resource' }}));
|
||||
let error: unknown;
|
||||
try {
|
||||
await getRelativeUrl(baseUrl, request, targetExtractor);
|
||||
} catch (err: unknown) {
|
||||
error = err;
|
||||
}
|
||||
expect(error).toEqual(expect.objectContaining({ errorCode: 'E0001' }));
|
||||
expect(BadRequestHttpError.isInstance(error)).toBe(true);
|
||||
expect(extractErrorTerms((error as BadRequestHttpError).metadata)).toEqual({ path: 'http://somewhere.else/resource' });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { assertError, createErrorMessage, isError } from '../../../../src/util/errors/ErrorUtil';
|
||||
import { createErrorMessage, isError } from '../../../../src/util/errors/ErrorUtil';
|
||||
|
||||
describe('ErrorUtil', (): void => {
|
||||
describe('#isError', (): void => {
|
||||
@@ -19,16 +19,6 @@ describe('ErrorUtil', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#assertError', (): void => {
|
||||
it('returns undefined on native errors.', async(): Promise<void> => {
|
||||
expect(assertError(new Error('error'))).toBeUndefined();
|
||||
});
|
||||
|
||||
it('throws on other values.', async(): Promise<void> => {
|
||||
expect((): void => assertError('apple')).toThrow('apple');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createErrorMessage', (): void => {
|
||||
it('returns the given message for normal Errors.', async(): Promise<void> => {
|
||||
expect(createErrorMessage(new Error('error msg'))).toBe('error msg');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'jest-rdf';
|
||||
import { DataFactory } from 'n3';
|
||||
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
||||
import { BadRequestHttpError } from '../../../../src/util/errors/BadRequestHttpError';
|
||||
import { ConflictHttpError } from '../../../../src/util/errors/ConflictHttpError';
|
||||
import { ForbiddenHttpError } from '../../../../src/util/errors/ForbiddenHttpError';
|
||||
@@ -15,9 +15,7 @@ import { PreconditionFailedHttpError } from '../../../../src/util/errors/Precond
|
||||
import { UnauthorizedHttpError } from '../../../../src/util/errors/UnauthorizedHttpError';
|
||||
import { UnprocessableEntityHttpError } from '../../../../src/util/errors/UnprocessableEntityHttpError';
|
||||
import { UnsupportedMediaTypeHttpError } from '../../../../src/util/errors/UnsupportedMediaTypeHttpError';
|
||||
import { SOLID_ERROR } from '../../../../src/util/Vocabularies';
|
||||
|
||||
const { literal, namedNode, quad } = DataFactory;
|
||||
import { HTTP, SOLID_ERROR } from '../../../../src/util/Vocabularies';
|
||||
|
||||
describe('HttpError', (): void => {
|
||||
const errors: [string, number, HttpErrorClass][] = [
|
||||
@@ -39,7 +37,7 @@ describe('HttpError', (): void => {
|
||||
const options = {
|
||||
cause: new Error('cause'),
|
||||
errorCode: 'E1234',
|
||||
details: {},
|
||||
metadata: new RepresentationMetadata(),
|
||||
};
|
||||
const instance = new constructor('my message', options);
|
||||
|
||||
@@ -75,15 +73,11 @@ describe('HttpError', (): void => {
|
||||
expect(new constructor().errorCode).toBe(`H${statusCode}`);
|
||||
});
|
||||
|
||||
it('sets the details.', (): void => {
|
||||
expect(instance.details).toBe(options.details);
|
||||
});
|
||||
|
||||
it('generates metadata.', (): void => {
|
||||
const subject = namedNode('subject');
|
||||
expect(instance.generateMetadata(subject)).toBeRdfIsomorphic([
|
||||
quad(subject, SOLID_ERROR.terms.errorResponse, constructor.uri),
|
||||
]);
|
||||
it('sets the metadata.', (): void => {
|
||||
expect(instance.metadata).toBe(options.metadata);
|
||||
expect(instance.metadata.get(SOLID_ERROR.terms.errorResponse)?.value)
|
||||
.toBe(`${SOLID_ERROR.namespace}H${statusCode}`);
|
||||
expect(instance.metadata.get(HTTP.terms.statusCodeNumber)?.value).toBe(`${statusCode}`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -92,7 +86,6 @@ describe('HttpError', (): void => {
|
||||
const options = {
|
||||
cause: new Error('cause'),
|
||||
errorCode: 'E1234',
|
||||
details: { some: 'detail' },
|
||||
};
|
||||
const instance = new MethodNotAllowedHttpError([ 'GET' ], 'my message', options);
|
||||
|
||||
@@ -107,11 +100,10 @@ describe('HttpError', (): void => {
|
||||
expect(instance.errorCode).toBe(options.errorCode);
|
||||
expect(new MethodNotAllowedHttpError([ 'GET' ]).errorCode).toBe(`H${405}`);
|
||||
|
||||
const subject = namedNode('subject');
|
||||
expect(instance.generateMetadata(subject)).toBeRdfIsomorphic([
|
||||
quad(subject, SOLID_ERROR.terms.errorResponse, MethodNotAllowedHttpError.uri),
|
||||
quad(subject, SOLID_ERROR.terms.disallowedMethod, literal('GET')),
|
||||
]);
|
||||
expect(instance.metadata.get(SOLID_ERROR.terms.errorResponse)?.value)
|
||||
.toBe(`${SOLID_ERROR.namespace}H405`);
|
||||
expect(instance.metadata.get(HTTP.terms.statusCodeNumber)?.value).toBe('405');
|
||||
expect(instance.metadata.get(SOLID_ERROR.terms.disallowedMethod)?.value).toBe('GET');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,53 @@
|
||||
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
||||
import { HttpError } from '../../../../src/util/errors/HttpError';
|
||||
import { createAggregateError, getStatusCode } from '../../../../src/util/errors/HttpErrorUtil';
|
||||
import { NotFoundHttpError } from '../../../../src/util/errors/NotFoundHttpError';
|
||||
import {
|
||||
createAggregateError,
|
||||
errorTermsToMetadata,
|
||||
extractErrorTerms,
|
||||
} from '../../../../src/util/errors/HttpErrorUtil';
|
||||
import { toPredicateTerm } from '../../../../src/util/TermUtil';
|
||||
|
||||
describe('HttpErrorUtil', (): void => {
|
||||
describe('#errorTermsToMetadata', (): void => {
|
||||
it('creates a metadata object with the necessary triples.', async(): Promise<void> => {
|
||||
const metadata = errorTermsToMetadata({
|
||||
test: 'apple',
|
||||
test2: 'pear',
|
||||
not: undefined,
|
||||
});
|
||||
expect(metadata.quads()).toHaveLength(2);
|
||||
expect(metadata.get(toPredicateTerm('urn:npm:solid:community-server:error-term:test'))?.value).toBe('apple');
|
||||
expect(metadata.get(toPredicateTerm('urn:npm:solid:community-server:error-term:test2'))?.value).toBe('pear');
|
||||
});
|
||||
|
||||
it('can add the necessary triples to existing metadata.', async(): Promise<void> => {
|
||||
const metadata = new RepresentationMetadata();
|
||||
const response = errorTermsToMetadata({
|
||||
test: 'apple',
|
||||
test2: 'pear',
|
||||
not: undefined,
|
||||
}, metadata);
|
||||
expect(response).toBe(metadata);
|
||||
expect(metadata.quads()).toHaveLength(2);
|
||||
expect(metadata.get(toPredicateTerm('urn:npm:solid:community-server:error-term:test'))?.value).toBe('apple');
|
||||
expect(metadata.get(toPredicateTerm('urn:npm:solid:community-server:error-term:test2'))?.value).toBe('pear');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#extractErrorTerms', (): void => {
|
||||
it('returns an object describing the terms.', async(): Promise<void> => {
|
||||
const metadata = new RepresentationMetadata({
|
||||
'urn:npm:solid:community-server:error-term:test': 'apple',
|
||||
'urn:npm:solid:community-server:error-term:test2': 'pear',
|
||||
'urn:npm:solid:community-server:other:test3': 'mango',
|
||||
});
|
||||
expect(extractErrorTerms(metadata)).toEqual({
|
||||
test: 'apple',
|
||||
test2: 'pear',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ErrorUtil', (): void => {
|
||||
describe('#createAggregateError', (): void => {
|
||||
const error401 = new HttpError(401, 'UnauthorizedHttpError');
|
||||
const error415 = new HttpError(415, 'UnsupportedMediaTypeHttpError');
|
||||
@@ -50,14 +95,4 @@ describe('ErrorUtil', (): void => {
|
||||
.toBe('Multiple handler errors: noStatusCode, noStatusCode');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getStatusCode', (): void => {
|
||||
it('returns the corresponding status code for HttpErrors.', async(): Promise<void> => {
|
||||
expect(getStatusCode(new NotFoundHttpError())).toBe(404);
|
||||
});
|
||||
|
||||
it('returns 500 for other errors.', async(): Promise<void> => {
|
||||
expect(getStatusCode(new Error('404'))).toBe(500);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import { RedirectHttpError } from '../../../../src/util/errors/RedirectHttpError
|
||||
import type { RedirectHttpErrorClass } from '../../../../src/util/errors/RedirectHttpError';
|
||||
import { SeeOtherHttpError } from '../../../../src/util/errors/SeeOtherHttpError';
|
||||
import { TemporaryRedirectHttpError } from '../../../../src/util/errors/TemporaryRedirectHttpError';
|
||||
import { HTTP, SOLID_ERROR, SOLID_HTTP } from '../../../../src/util/Vocabularies';
|
||||
|
||||
// Used to make sure the RedirectHttpError constructor also gets called in a test.
|
||||
class FixedRedirectHttpError extends RedirectHttpError {
|
||||
@@ -70,7 +71,10 @@ describe('RedirectHttpError', (): void => {
|
||||
});
|
||||
|
||||
it('sets the details.', (): void => {
|
||||
expect(instance.details).toBe(options.details);
|
||||
expect(instance.metadata.get(SOLID_ERROR.terms.errorResponse)?.value)
|
||||
.toBe(`${SOLID_ERROR.namespace}H${statusCode}`);
|
||||
expect(instance.metadata.get(HTTP.terms.statusCodeNumber)?.value).toBe(`${statusCode}`);
|
||||
expect(instance.metadata.get(SOLID_HTTP.terms.location)?.value).toBe(location);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier';
|
||||
import { extractErrorTerms } from '../../../../src/util/errors/HttpErrorUtil';
|
||||
import { InternalServerError } from '../../../../src/util/errors/InternalServerError';
|
||||
import { BaseIdentifierStrategy } from '../../../../src/util/identifiers/BaseIdentifierStrategy';
|
||||
|
||||
class DummyStrategy extends BaseIdentifierStrategy {
|
||||
@@ -21,10 +23,16 @@ describe('A BaseIdentifierStrategy', (): void => {
|
||||
});
|
||||
|
||||
it('errors when attempting to get the parent of an unsupported identifier.', async(): Promise<void> => {
|
||||
expect((): any => strategy.getParentContainer({ path: '/unsupported' }))
|
||||
.toThrow('The identifier /unsupported is outside the configured identifier space.');
|
||||
expect((): any => strategy.getParentContainer({ path: '/unsupported' }))
|
||||
.toThrow(expect.objectContaining({ errorCode: 'E0001', details: { path: '/unsupported' }}));
|
||||
let error: unknown;
|
||||
try {
|
||||
strategy.getParentContainer({ path: '/unsupported' });
|
||||
} catch (err: unknown) {
|
||||
error = err;
|
||||
}
|
||||
expect(error).toEqual(expect.objectContaining({ errorCode: 'E0001',
|
||||
message: 'The identifier /unsupported is outside the configured identifier space.' }));
|
||||
expect(InternalServerError.isInstance(error)).toBe(true);
|
||||
expect(extractErrorTerms((error as InternalServerError).metadata)).toEqual({ path: '/unsupported' });
|
||||
});
|
||||
|
||||
it('errors when attempting to get the parent of a root container.', async(): Promise<void> => {
|
||||
|
||||
Reference in New Issue
Block a user