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,6 +1,7 @@
|
||||
import type { TLSSocket } from 'tls';
|
||||
import type { HttpRequest } from '../../../server/HttpRequest';
|
||||
import { BadRequestHttpError } from '../../../util/errors/BadRequestHttpError';
|
||||
import { errorTermsToMetadata } from '../../../util/errors/HttpErrorUtil';
|
||||
import { InternalServerError } from '../../../util/errors/InternalServerError';
|
||||
import { parseForwarded } from '../../../util/HeaderUtil';
|
||||
import type { IdentifierStrategy } from '../../../util/identifiers/IdentifierStrategy';
|
||||
@@ -73,7 +74,7 @@ export class OriginalUrlExtractor extends TargetExtractor {
|
||||
// Check if the configured IdentifierStrategy supports the identifier
|
||||
if (!this.identifierStrategy.supportsIdentifier(identifier)) {
|
||||
throw new InternalServerError(`The identifier ${identifier.path} is outside the configured identifier space.`,
|
||||
{ errorCode: 'E0001', details: { path: identifier.path }});
|
||||
{ errorCode: 'E0001', metadata: errorTermsToMetadata({ path: identifier.path }) });
|
||||
}
|
||||
|
||||
return identifier;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { HttpRequest } from '../../../server/HttpRequest';
|
||||
import { parseContentType } from '../../../util/HeaderUtil';
|
||||
import type { RepresentationMetadata } from '../../representation/RepresentationMetadata';
|
||||
import { MetadataParser } from './MetadataParser';
|
||||
|
||||
@@ -9,7 +10,7 @@ export class ContentTypeParser extends MetadataParser {
|
||||
public async handle(input: { request: HttpRequest; metadata: RepresentationMetadata }): Promise<void> {
|
||||
const contentType = input.request.headers['content-type'];
|
||||
if (contentType) {
|
||||
input.metadata.contentType = contentType;
|
||||
input.metadata.contentTypeObject = parseContentType(contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { HttpRequest } from '../../../server/HttpRequest';
|
||||
import type { AcceptHeader } from '../../../util/HeaderUtil';
|
||||
import type { AcceptHeader } from '../../../util/Header';
|
||||
import {
|
||||
parseAccept,
|
||||
parseAcceptCharset,
|
||||
|
||||
@@ -3,13 +3,9 @@ import type {
|
||||
RepresentationConverterArgs,
|
||||
} from '../../../storage/conversion/RepresentationConverter';
|
||||
import { INTERNAL_ERROR } from '../../../util/ContentTypes';
|
||||
import { getStatusCode } from '../../../util/errors/HttpErrorUtil';
|
||||
import { toLiteral } from '../../../util/TermUtil';
|
||||
import { HTTP, XSD } from '../../../util/Vocabularies';
|
||||
import type { PreferenceParser } from '../../input/preferences/PreferenceParser';
|
||||
import { BasicRepresentation } from '../../representation/BasicRepresentation';
|
||||
import type { Representation } from '../../representation/Representation';
|
||||
import { RepresentationMetadata } from '../../representation/RepresentationMetadata';
|
||||
import type { ResponseDescription } from '../response/ResponseDescription';
|
||||
import type { ErrorHandlerArgs } from './ErrorHandler';
|
||||
import { ErrorHandler } from './ErrorHandler';
|
||||
@@ -64,11 +60,13 @@ export class ConvertingErrorHandler extends ErrorHandler {
|
||||
* Prepares the arguments used by all functions.
|
||||
*/
|
||||
private async extractErrorDetails({ error, request }: ErrorHandlerArgs): Promise<PreparedArguments> {
|
||||
const statusCode = getStatusCode(error);
|
||||
const representation = this.toRepresentation(error, statusCode);
|
||||
if (!this.showStackTrace) {
|
||||
delete error.stack;
|
||||
}
|
||||
const representation = new BasicRepresentation([ error ], error.metadata, INTERNAL_ERROR, false);
|
||||
const identifier = { path: representation.metadata.identifier.value };
|
||||
const preferences = await this.preferenceParser.handle({ request });
|
||||
return { statusCode, conversionArgs: { identifier, representation, preferences }};
|
||||
return { statusCode: error.statusCode, conversionArgs: { identifier, representation, preferences }};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,20 +79,4 @@ export class ConvertingErrorHandler extends ErrorHandler {
|
||||
data: converted.data,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Representation based on the given error.
|
||||
* Content type will be internal/error.
|
||||
* The status code is used for metadata.
|
||||
*/
|
||||
private toRepresentation(error: Error, statusCode: number): Representation {
|
||||
const metadata = new RepresentationMetadata(INTERNAL_ERROR);
|
||||
metadata.add(HTTP.terms.statusCodeNumber, toLiteral(statusCode, XSD.terms.integer));
|
||||
|
||||
if (!this.showStackTrace) {
|
||||
delete error.stack;
|
||||
}
|
||||
|
||||
return new BasicRepresentation([ error ], metadata, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { HttpRequest } from '../../../server/HttpRequest';
|
||||
import type { HttpError } from '../../../util/errors/HttpError';
|
||||
import { AsyncHandler } from '../../../util/handlers/AsyncHandler';
|
||||
import type { ResponseDescription } from '../response/ResponseDescription';
|
||||
|
||||
export interface ErrorHandlerArgs {
|
||||
error: Error;
|
||||
error: HttpError;
|
||||
request: HttpRequest;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { getLoggerFor } from '../../../logging/LogUtil';
|
||||
import { createErrorMessage } from '../../../util/errors/ErrorUtil';
|
||||
import { getStatusCode } from '../../../util/errors/HttpErrorUtil';
|
||||
import { guardedStreamFrom } from '../../../util/StreamUtil';
|
||||
import { toLiteral } from '../../../util/TermUtil';
|
||||
import { HTTP, XSD } from '../../../util/Vocabularies';
|
||||
import { RepresentationMetadata } from '../../representation/RepresentationMetadata';
|
||||
import type { ResponseDescription } from '../response/ResponseDescription';
|
||||
import type { ErrorHandlerArgs } from './ErrorHandler';
|
||||
import { ErrorHandler } from './ErrorHandler';
|
||||
@@ -32,17 +28,15 @@ export class SafeErrorHandler extends ErrorHandler {
|
||||
this.logger.debug(`Recovering from error handler failure: ${createErrorMessage(error)}`);
|
||||
}
|
||||
const { error } = input;
|
||||
const statusCode = getStatusCode(error);
|
||||
const metadata = new RepresentationMetadata('text/plain');
|
||||
metadata.add(HTTP.terms.statusCodeNumber, toLiteral(statusCode, XSD.terms.integer));
|
||||
error.metadata.contentType = 'text/plain';
|
||||
|
||||
const text = typeof error.stack === 'string' && this.showStackTrace ?
|
||||
`${error.stack}\n` :
|
||||
`${error.name}: ${error.message}\n`;
|
||||
|
||||
return {
|
||||
statusCode,
|
||||
metadata,
|
||||
statusCode: error.statusCode,
|
||||
metadata: error.metadata,
|
||||
data: guardedStreamFrom(text),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { DataFactory } from 'n3';
|
||||
import type { RedirectHttpError } from '../../../util/errors/RedirectHttpError';
|
||||
import { SOLID_HTTP } from '../../../util/Vocabularies';
|
||||
import { RepresentationMetadata } from '../../representation/RepresentationMetadata';
|
||||
import { ResponseDescription } from './ResponseDescription';
|
||||
|
||||
/**
|
||||
@@ -9,7 +8,7 @@ import { ResponseDescription } from './ResponseDescription';
|
||||
*/
|
||||
export class RedirectResponseDescription extends ResponseDescription {
|
||||
public constructor(error: RedirectHttpError) {
|
||||
const metadata = new RepresentationMetadata({ [SOLID_HTTP.location]: DataFactory.namedNode(error.location) });
|
||||
super(error.statusCode, metadata);
|
||||
error.metadata.set(SOLID_HTTP.terms.location, DataFactory.namedNode(error.location));
|
||||
super(error.statusCode, error.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { DataFactory, Store } from 'n3';
|
||||
import type { BlankNode, DefaultGraph, Literal, NamedNode, Quad, Term } from 'rdf-js';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { InternalServerError } from '../../util/errors/InternalServerError';
|
||||
import { ContentType, parseContentType } from '../../util/HeaderUtil';
|
||||
import { ContentType, SIMPLE_MEDIA_RANGE } from '../../util/Header';
|
||||
import { toNamedTerm, toObjectTerm, isTerm, toLiteral } from '../../util/TermUtil';
|
||||
import { CONTENT_TYPE_TERM, CONTENT_LENGTH_TERM, XSD, SOLID_META, RDFS } from '../../util/Vocabularies';
|
||||
import type { ResourceIdentifier } from './ResourceIdentifier';
|
||||
@@ -67,18 +66,18 @@ export class RepresentationMetadata {
|
||||
* @param identifier - Identifier of the resource relevant to this metadata.
|
||||
* @param contentType - Override for the content type of the representation.
|
||||
*/
|
||||
public constructor(identifier?: MetadataIdentifier, contentType?: string);
|
||||
public constructor(identifier?: MetadataIdentifier, contentType?: string | ContentType);
|
||||
|
||||
/**
|
||||
* @param metadata - Starts as a copy of the input metadata.
|
||||
* @param contentType - Override for the content type of the representation.
|
||||
*/
|
||||
public constructor(metadata?: RepresentationMetadata, contentType?: string);
|
||||
public constructor(metadata?: RepresentationMetadata, contentType?: string | ContentType);
|
||||
|
||||
/**
|
||||
* @param contentType - The content type of the representation.
|
||||
*/
|
||||
public constructor(contentType?: string);
|
||||
public constructor(contentType?: string | ContentType);
|
||||
|
||||
/**
|
||||
* @param metadata - Metadata values (defaulting to content type if a string)
|
||||
@@ -86,8 +85,8 @@ export class RepresentationMetadata {
|
||||
public constructor(metadata?: RepresentationMetadata | MetadataRecord | string);
|
||||
|
||||
public constructor(
|
||||
input?: MetadataIdentifier | RepresentationMetadata | MetadataRecord | string,
|
||||
overrides?: MetadataRecord | string,
|
||||
input?: MetadataIdentifier | RepresentationMetadata | MetadataRecord | ContentType | string,
|
||||
overrides?: MetadataRecord | string | ContentType,
|
||||
) {
|
||||
this.store = new Store();
|
||||
if (isResourceIdentifier(input)) {
|
||||
@@ -105,6 +104,8 @@ export class RepresentationMetadata {
|
||||
if (overrides) {
|
||||
if (typeof overrides === 'string') {
|
||||
this.contentType = overrides;
|
||||
} else if (overrides instanceof ContentType) {
|
||||
this.contentTypeObject = overrides;
|
||||
} else {
|
||||
this.setOverrides(overrides);
|
||||
}
|
||||
@@ -313,7 +314,8 @@ export class RepresentationMetadata {
|
||||
}
|
||||
if (terms.length > 1) {
|
||||
this.logger.error(`Multiple results for ${predicate.value}`);
|
||||
throw new InternalServerError(
|
||||
// We can not use an `InternalServerError` here as otherwise errors and metadata files would depend on each other
|
||||
throw new Error(
|
||||
`Multiple results for ${predicate.value}`,
|
||||
);
|
||||
}
|
||||
@@ -344,7 +346,16 @@ export class RepresentationMetadata {
|
||||
}
|
||||
|
||||
if (typeof input === 'string') {
|
||||
input = parseContentType(input);
|
||||
// Simple check to estimate if this is a simple content type.
|
||||
// If not, mention that the `contentTypeObject` should be used instead.
|
||||
// Not calling `parseContentType` here as that would cause a dependency loop with `HttpError`.
|
||||
if (!SIMPLE_MEDIA_RANGE.test(input)) {
|
||||
// Not using an HttpError as HttpError depends on metadata
|
||||
throw new Error(
|
||||
'Only simple content types can be set by string. Use the `contentTypeObject` function for complexer types.',
|
||||
);
|
||||
}
|
||||
input = new ContentType(input);
|
||||
}
|
||||
|
||||
for (const [ key, value ] of Object.entries(input.parameters)) {
|
||||
|
||||
Reference in New Issue
Block a user