feat: Add metadata to errors

This commit is contained in:
Joachim Van Herwegen
2023-07-25 14:10:46 +02:00
parent a333412e19
commit f373dff1d7
42 changed files with 455 additions and 419 deletions

View File

@@ -1,7 +1,8 @@
import { BasicRepresentation } from '../../http/representation/BasicRepresentation';
import type { Representation } from '../../http/representation/Representation';
import { APPLICATION_JSON, INTERNAL_ERROR } from '../../util/ContentTypes';
import { HttpError } from '../../util/errors/HttpError';
import type { HttpError } from '../../util/errors/HttpError';
import { extractErrorTerms } from '../../util/errors/HttpErrorUtil';
import { OAuthHttpError } from '../../util/errors/OAuthHttpError';
import { getSingleItem } from '../../util/StreamUtil';
import { BaseTypedRepresentationConverter } from './BaseTypedRepresentationConverter';
@@ -16,11 +17,14 @@ export class ErrorToJsonConverter extends BaseTypedRepresentationConverter {
}
public async handle({ representation }: RepresentationConverterArgs): Promise<Representation> {
const error = await getSingleItem(representation.data) as Error;
const error = await getSingleItem(representation.data) as HttpError;
const result: Record<string, any> = {
name: error.name,
message: error.message,
statusCode: error.statusCode,
errorCode: error.errorCode,
details: extractErrorTerms(error.metadata),
};
// OAuth errors responses require additional fields
@@ -28,22 +32,6 @@ export class ErrorToJsonConverter extends BaseTypedRepresentationConverter {
Object.assign(result, error.mandatoryFields);
}
if (HttpError.isInstance(error)) {
result.statusCode = error.statusCode;
result.errorCode = error.errorCode;
if (error.details) {
try {
// The details might not be serializable
JSON.stringify(error.details);
result.details = error.details;
} catch {
// Do not store the details
}
}
} else {
result.statusCode = 500;
}
if (error.stack) {
result.stack = error.stack;
}

View File

@@ -2,8 +2,9 @@ import { BasicRepresentation } from '../../http/representation/BasicRepresentati
import type { Representation } from '../../http/representation/Representation';
import { RepresentationMetadata } from '../../http/representation/RepresentationMetadata';
import { INTERNAL_ERROR, INTERNAL_QUADS } from '../../util/ContentTypes';
import type { HttpError } from '../../util/errors/HttpError';
import { getSingleItem } from '../../util/StreamUtil';
import { DC, SOLID_ERROR } from '../../util/Vocabularies';
import { DC, SOLID_ERROR, SOLID_ERROR_TERM } from '../../util/Vocabularies';
import { BaseTypedRepresentationConverter } from './BaseTypedRepresentationConverter';
import type { RepresentationConverterArgs } from './RepresentationConverter';
@@ -16,7 +17,7 @@ export class ErrorToQuadConverter extends BaseTypedRepresentationConverter {
}
public async handle({ identifier, representation }: RepresentationConverterArgs): Promise<Representation> {
const error = await getSingleItem(representation.data) as Error;
const error = await getSingleItem(representation.data) as HttpError;
// A metadata object makes it easier to add triples due to the utility functions
const data = new RepresentationMetadata(identifier);
@@ -25,6 +26,9 @@ export class ErrorToQuadConverter extends BaseTypedRepresentationConverter {
if (error.stack) {
data.add(SOLID_ERROR.terms.stack, error.stack);
}
// Add all the error terms from the metadata
data.addQuads(representation.metadata.quads()
.filter((quad): boolean => quad.predicate.value.startsWith(SOLID_ERROR_TERM.namespace)));
// Update the content-type to quads
return new BasicRepresentation(data.quads(), representation.metadata, INTERNAL_QUADS, false);

View File

@@ -2,7 +2,8 @@ import assert from 'assert';
import { BasicRepresentation } from '../../http/representation/BasicRepresentation';
import type { Representation } from '../../http/representation/Representation';
import { INTERNAL_ERROR } from '../../util/ContentTypes';
import { HttpError } from '../../util/errors/HttpError';
import type { HttpError } from '../../util/errors/HttpError';
import { extractErrorTerms } from '../../util/errors/HttpErrorUtil';
import { resolveModulePath } from '../../util/PathUtil';
import { getSingleItem } from '../../util/StreamUtil';
import { isValidFileName } from '../../util/StringUtil';
@@ -57,19 +58,20 @@ export class ErrorToTemplateConverter extends BaseTypedRepresentationConverter {
}
public async handle({ representation }: RepresentationConverterArgs): Promise<Representation> {
const error = await getSingleItem(representation.data) as Error;
const error = await getSingleItem(representation.data) as HttpError;
// Render the error description using an error-specific template
let description: string | undefined;
if (HttpError.isInstance(error)) {
try {
const templateFile = `${error.errorCode}${this.extension}`;
assert(isValidFileName(templateFile), 'Invalid error template name');
description = await this.templateEngine.handleSafe({ contents: error.details ?? {},
template: { templateFile, templatePath: this.codeTemplatesPath }});
} catch {
// In case no template is found, or rendering errors, we still want to convert
}
try {
const templateFile = `${error.errorCode}${this.extension}`;
assert(isValidFileName(templateFile), 'Invalid error template name');
// Filter out the error terms to pass to the template
description = await this.templateEngine.handleSafe({
contents: extractErrorTerms(error.metadata),
template: { templateFile, templatePath: this.codeTemplatesPath },
});
} catch {
// In case no template is found, or rendering errors, we still want to convert
}
// Render the main template, embedding the rendered error description