From 95cd4a63939f022a8802944587eeb8c1a43d1032 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Tue, 22 Feb 2022 11:22:55 +0100 Subject: [PATCH 1/2] implement RFC7807 --- index.d.ts | 39 +++++++++++++++++++++++++++++++++++++++ index.js | 15 ++++++++++++++- index.test-d.ts | 8 ++++++-- test.js | 30 ++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index a068054..15d75d9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -12,6 +12,45 @@ declare namespace createError { code: string; name: string; statusCode?: number; + + toRFC7807: (instance?: RFC7807Error['instance'], details?: RFC7807Error['details']) => RFC7807Error; + } + + interface RFC7807Error { + /** + * A URI reference [RFC3986] that identifies the + * problem type. This specification encourages that, when + * dereferenced, it provide human-readable documentation for the + * problem type (e.g., using HTML [W3C.REC-html5-20141028]). + * When this member is not present, its value is assumed to be + * "about:blank". + */ + type: string; + /** + * A short, human-readable summary of the problem + * type. It SHOULD NOT change from occurrence to occurrence of the + * problem, except for purposes of localization (e.g., using + * proactive content negotiation; see [RFC7231], Section 3.4). + */ + title: string; + /** + * The HTTP status code ([RFC7231], Section 6) + * generated by the origin server for this occurrence of the problem. + */ + status: number; + /** + * A human-readable explanation specific to this + * occurrence of the problem. + */ + detail: string; + /** + * A URI reference that identifies the specific + * occurrence of the problem. It may or may not yield further + * information if dereferenced. + */ + instance: string; + code: string; + details: { [key: string]: any }[]; } interface FastifyErrorConstructor { diff --git a/index.js b/index.js index d77e486..1782827 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ const { inherits, format } = require('util') -function createError (code, message, statusCode = 500, Base = Error) { +function createError (code, message, statusCode = 500, Base = Error, uriReference) { if (!code) throw new Error('Fastify error code must not be empty') if (!message) throw new Error('Fastify error message must not be empty') @@ -28,6 +28,7 @@ function createError (code, message, statusCode = 500, Base = Error) { } this.statusCode = statusCode || undefined + this.uriReference = uriReference || 'about:blank' } FastifyError.prototype[Symbol.toStringTag] = 'Error' @@ -35,6 +36,18 @@ function createError (code, message, statusCode = 500, Base = Error) { return `${this.name} [${this.code}]: ${this.message}` } + FastifyError.prototype.toRFC7807 = function (instance, details) { + return { + type: this.uriReference, + title: this.name, + status: this.statusCode, + detail: this.message, + instance: instance || '', + code: this.code, + details: details || [] + } + } + inherits(FastifyError, Base) return FastifyError diff --git a/index.test-d.ts b/index.test-d.ts index fce08fc..3b1c24b 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,4 +1,4 @@ -import createError, { FastifyError, FastifyErrorConstructor } from './' +import createError, { FastifyError, FastifyErrorConstructor, RFC7807Error } from './' import { expectType } from 'tsd' const CustomError = createError('ERROR_CODE', 'message') @@ -7,4 +7,8 @@ const err = new CustomError() expectType(err) expectType(err.code) expectType(err.message) -expectType(err.statusCode!) \ No newline at end of file +expectType(err.statusCode!) + +expectType(err.toRFC7807()); +expectType(err.toRFC7807('/dev/null')); +expectType(err.toRFC7807(undefined, [{ description: 'missing value for key'}])); diff --git a/test.js b/test.js index 7936976..e7d33ae 100644 --- a/test.js +++ b/test.js @@ -104,3 +104,33 @@ test('Create the error without the new keyword', t => { t.is(err.statusCode, 500) t.truthy(err.stack) }) + +test('FastifyError.toRFC7807 returns RFC7807 conform Object', t => { + const NewError = createError('CODE', 'foo') + const err = new NewError() + t.is(typeof err.toRFC7807(), 'object') + t.is(err.toRFC7807().code, 'CODE') + t.is(err.toRFC7807().detail, 'foo') + t.is(err.toRFC7807().title, 'FastifyError') + t.is(err.toRFC7807().type, 'about:blank') + t.deepEqual(err.toRFC7807().details, []) + t.is(err.toRFC7807().instance, '') +}) + +test('FastifyError.toRFC7807 accepts instance', t => { + const NewError = createError('CODE', 'foo') + const err = new NewError() + t.is(err.toRFC7807('/dev/null').instance, '/dev/null') +}) + +test('FastifyError.toRFC7807 accepts details', t => { + const NewError = createError('CODE', 'foo') + const err = new NewError() + t.deepEqual(err.toRFC7807(undefined, [{ max: 'not a valid maximum value for key' }]).details, [{ max: 'not a valid maximum value for key' }]) +}) + +test('FastifyError accepts an uriReference which is later used for toRFC7807 type attribute', t => { + const NewError = createError('CODE', 'foo', undefined, undefined, 'https://www.fastify.io/docs/latest/Reference/Errors/#fst_err_bad_url') + const err = new NewError() + t.is(err.toRFC7807().type, 'https://www.fastify.io/docs/latest/Reference/Errors/#fst_err_bad_url') +}) From 433f8e4ebc52b563eec481e451500c594290d03c Mon Sep 17 00:00:00 2001 From: uzlopak Date: Tue, 22 Feb 2022 12:18:02 +0100 Subject: [PATCH 2/2] use object for details --- index.d.ts | 2 +- index.js | 6 +++--- index.test-d.ts | 2 +- test.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index 15d75d9..3a2121c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -50,7 +50,7 @@ declare namespace createError { */ instance: string; code: string; - details: { [key: string]: any }[]; + details: { [key: string]: any }; } interface FastifyErrorConstructor { diff --git a/index.js b/index.js index 1782827..6606d62 100644 --- a/index.js +++ b/index.js @@ -28,7 +28,7 @@ function createError (code, message, statusCode = 500, Base = Error, uriReferenc } this.statusCode = statusCode || undefined - this.uriReference = uriReference || 'about:blank' + this.uriReference = uriReference || undefined } FastifyError.prototype[Symbol.toStringTag] = 'Error' @@ -38,13 +38,13 @@ function createError (code, message, statusCode = 500, Base = Error, uriReferenc FastifyError.prototype.toRFC7807 = function (instance, details) { return { - type: this.uriReference, + type: this.uriReference || 'about:blank', title: this.name, status: this.statusCode, detail: this.message, instance: instance || '', code: this.code, - details: details || [] + details: details || {} } } diff --git a/index.test-d.ts b/index.test-d.ts index 3b1c24b..77bcfa8 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -11,4 +11,4 @@ expectType(err.statusCode!) expectType(err.toRFC7807()); expectType(err.toRFC7807('/dev/null')); -expectType(err.toRFC7807(undefined, [{ description: 'missing value for key'}])); +expectType(err.toRFC7807(undefined, { description: 'missing value for key'})); diff --git a/test.js b/test.js index e7d33ae..7f03484 100644 --- a/test.js +++ b/test.js @@ -113,7 +113,7 @@ test('FastifyError.toRFC7807 returns RFC7807 conform Object', t => { t.is(err.toRFC7807().detail, 'foo') t.is(err.toRFC7807().title, 'FastifyError') t.is(err.toRFC7807().type, 'about:blank') - t.deepEqual(err.toRFC7807().details, []) + t.deepEqual(err.toRFC7807().details, {}) t.is(err.toRFC7807().instance, '') }) @@ -126,7 +126,7 @@ test('FastifyError.toRFC7807 accepts instance', t => { test('FastifyError.toRFC7807 accepts details', t => { const NewError = createError('CODE', 'foo') const err = new NewError() - t.deepEqual(err.toRFC7807(undefined, [{ max: 'not a valid maximum value for key' }]).details, [{ max: 'not a valid maximum value for key' }]) + t.deepEqual(err.toRFC7807(undefined, { max: 'not a valid maximum value for key' }).details, { max: 'not a valid maximum value for key' }) }) test('FastifyError accepts an uriReference which is later used for toRFC7807 type attribute', t => {