From d7a127328d0dd92c0b7a3ddbcdbb7588e57cc2b6 Mon Sep 17 00:00:00 2001 From: Harrison Ifeanyichukwu Date: Sun, 11 Feb 2024 18:49:09 +0100 Subject: [PATCH] fix: fix response handling methods: --- src/@types/index.ts | 4 +- src/modules/Response.ts | 108 +++++++++++++++++++++------------------- 2 files changed, 60 insertions(+), 52 deletions(-) diff --git a/src/@types/index.ts b/src/@types/index.ts index bf60050..0da1246 100644 --- a/src/@types/index.ts +++ b/src/@types/index.ts @@ -245,7 +245,7 @@ export interface RouteResponse { headers?: IncomingHttpHeaders; } -export interface APIExecutor { +export interface APIExecutor { (arg: { /** * request body, should be a combination of parsed post body and url search params @@ -261,7 +261,7 @@ export interface APIExecutor { * request path parameters, as contained in the routing path */ pathParams: PathParameters; - }): Promise | null>; + }): Promise | null>; /** * assigned name of the handler diff --git a/src/modules/Response.ts b/src/modules/Response.ts index 6056961..810e3ac 100644 --- a/src/modules/Response.ts +++ b/src/modules/Response.ts @@ -7,6 +7,18 @@ import { ErrorCallback, RouteResponse } from '../@types'; import { handleError } from './Utils'; import { ServerRequest } from './Request'; +export interface AfterResponseCallbacksOpt { + /** + * handler callback after a successful response has been sent to the client + */ + onSuccess?: (response: RouteResponse) => void; + + /** + * callback is called after an error response is sent back to the client + */ + onError?: (response: RouteResponse) => void; +} + export type ServerResponse< T extends typeof Http2ServerResponse | typeof Http1ServerResponse = | typeof Http2ServerResponse @@ -99,6 +111,7 @@ export type ServerResponse< path: string, status?: number ): Promise; + /** * sends a file download attachment to the client * @param filePath - relative or absolute file path @@ -113,17 +126,19 @@ export type ServerResponse< /** * sends json error data back to the client */ - jsonError( + jsonError( this: ServerResponse, - response?: RouteResponse + response?: RouteResponse, + options?: AfterResponseCallbacksOpt ): Promise; /** * sends json success data back to the client */ - jsonSuccess( + jsonSuccess( this: ServerResponse, - response?: RouteResponse + response?: RouteResponse, + options?: AfterResponseCallbacksOpt ): Promise; /** @@ -139,11 +154,13 @@ export type ServerResponse< */ processRouteResponse( this: ServerResponse, + + /** + * the response promise + */ responsePromise: Promise>, - options?: { - onSuccess?: (response: RouteResponse) => void; - onError?: (response: RouteResponse) => void; - } + + options?: AfterResponseCallbacksOpt ): Promise; }; @@ -223,68 +240,59 @@ const createResponseClass = < return this; }; - ResponseClass.prototype.removeHeaders = function (...names: string[]) { + ResponseClass.prototype.removeHeaders = function (...names) { names.forEach((name) => { this.removeHeader(name); }); return this; }; - ResponseClass.prototype.status = function (code: number) { + ResponseClass.prototype.status = function (code) { this.statusCode = code; return this; }; - ResponseClass.prototype.redirect = function ( - path: string, - status = 302 - ): Promise { + ResponseClass.prototype.redirect = function (path, status = 302) { return this.status(status).setHeader('Location', path).end(); }; - ResponseClass.prototype.download = function ( - filePath: string, - filename?: string - ): Promise { + ResponseClass.prototype.download = function (filePath, filename) { return this.fileServer.serveDownload(filePath, this, filename); }; - ResponseClass.prototype.json = function ( - data?: object | string - ): Promise { + ResponseClass.prototype.json = function (data) { const resolvedData = typeof data === 'string' ? data : JSON.stringify(data || ''); return this.setHeader('Content-Type', 'application/json').end(resolvedData); }; - ResponseClass.prototype.jsonError = function ( - response?: RouteResponse - ): Promise { - const { - statusCode = 400, - headers, - message, - errors = null, - } = response || {}; - - if (statusCode < 300) { - return this.jsonSuccess(response); - } + ResponseClass.prototype.jsonError = function (response, opts) { + const { headers, message, errors = null, statusCode } = response || {}; - return this.status(statusCode) + return this.status(statusCode || 400) .setHeaders(headers) .json({ message: message || 'Request failed', errors, + }) + .finally(() => { + if (opts?.onError) { + opts.onError(response); + } }); }; - ResponseClass.prototype.jsonSuccess = function ( - response?: RouteResponse - ): Promise { - const { statusCode = 200, headers, message, data = null } = response || {}; - if (statusCode >= 300) { - return this.jsonError(response); + ResponseClass.prototype.jsonSuccess = function (response, opts) { + const { + statusCode = 200, + headers, + message, + data = null, + errors, + } = response || {}; + + if (statusCode >= 300 || errors) { + return this.jsonError(response, opts); } return this.status(statusCode) @@ -292,6 +300,11 @@ const createResponseClass = < .json({ message: message || 'Request successful', data, + }) + .finally(() => { + if (opts?.onSuccess) { + opts.onSuccess(response); + } }); }; @@ -305,23 +318,18 @@ const createResponseClass = < ResponseClass.prototype.processRouteResponse = function ( responsePromise, - options + opts ) { return responsePromise .then((response) => { - return this.jsonSuccess(response).then(() => { - options?.onSuccess?.(response); - return true; - }); + return this.jsonSuccess(response, opts); }) + .catch((err) => { if (err instanceof Error) { return handleError(err, this); } - return this.jsonError(err).then(() => { - options?.onError?.(err); - return true; - }); + return this.jsonError(err, opts); }); };