From b5b8f96cc94e3a3c0ee7d989819197ab5df393cd Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 12 Mar 2020 19:25:38 +0530 Subject: improved error reporting / towards a working recoup --- src/operations/errors.ts | 104 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 16 deletions(-) (limited to 'src/operations/errors.ts') diff --git a/src/operations/errors.ts b/src/operations/errors.ts index 7e97fdb3c..751a57111 100644 --- a/src/operations/errors.ts +++ b/src/operations/errors.ts @@ -1,8 +1,6 @@ -import { OperationError } from "../types/walletTypes"; - /* This file is part of GNU Taler - (C) 2019 GNUnet e.V. + (C) 2019-2020 Taler Systems SA GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -16,13 +14,26 @@ import { OperationError } from "../types/walletTypes"; GNU Taler; see the file COPYING. If not, see */ +/** + * Classes and helpers for error handling specific to wallet operations. + * + * @author Florian Dold + */ + +/** + * Imports. + */ +import { OperationError } from "../types/walletTypes"; +import { HttpResponse } from "../util/http"; +import { Codec } from "../util/codec"; + /** * This exception is there to let the caller know that an error happened, * but the error has already been reported by writing it to the database. */ export class OperationFailedAndReportedError extends Error { - constructor(message: string) { - super(message); + constructor(public operationError: OperationError) { + super(operationError.message); // Set the prototype explicitly. Object.setPrototypeOf(this, OperationFailedAndReportedError.prototype); @@ -34,14 +45,73 @@ export class OperationFailedAndReportedError extends Error { * responsible for recording the failure in the database. */ export class OperationFailedError extends Error { - constructor(message: string, public err: OperationError) { - super(message); + constructor(public operationError: OperationError) { + super(operationError.message); // Set the prototype explicitly. Object.setPrototypeOf(this, OperationFailedError.prototype); } } +/** + * Process an HTTP response that we expect to contain Taler-specific JSON. + * + * Depending on the status code, we throw an exception. This function + * will try to extract Taler-specific error information from the HTTP response + * if possible. + */ +export async function scrutinizeTalerJsonResponse( + resp: HttpResponse, + codec: Codec, +): Promise { + + // FIXME: We should distinguish between different types of error status + // to react differently (throttle, report permanent failure) + + // FIXME: Make sure that when we receive an error message, + // it looks like a Taler error message + + if (resp.status !== 200) { + let exc: OperationFailedError | undefined = undefined; + try { + const errorJson = await resp.json(); + const m = `received error response (status ${resp.status})`; + exc = new OperationFailedError({ + type: "protocol", + message: m, + details: { + httpStatusCode: resp.status, + errorResponse: errorJson, + } + }); + } catch (e) { + const m = "could not parse response JSON"; + exc = new OperationFailedError({ + type: "network", + message: m, + details: { + status: resp.status, + } + }); + } + throw exc; + } + let json: any; + try { + json = await resp.json(); + } catch (e) { + const m = "could not parse response JSON"; + throw new OperationFailedError({ + type: "network", + message: m, + details: { + status: resp.status, + } + }); + } + return codec.decode(json); +} + /** * Run an operation and call the onOpError callback * when there was an exception or operation error that must be reported. @@ -59,26 +129,28 @@ export async function guardOperationException( throw e; } if (e instanceof OperationFailedError) { - await onOpError(e.err); - throw new OperationFailedAndReportedError(e.message); + await onOpError(e.operationError); + throw new OperationFailedAndReportedError(e.operationError); } if (e instanceof Error) { console.log("guard: caught Error"); - await onOpError({ + const opErr = { type: "exception", message: e.message, details: {}, - }); - throw new OperationFailedAndReportedError(e.message); + } + await onOpError(opErr); + throw new OperationFailedAndReportedError(opErr); } console.log("guard: caught something else"); - await onOpError({ + const opErr = { type: "exception", message: "non-error exception thrown", details: { value: e.toString(), }, - }); - throw new OperationFailedAndReportedError(e.message); + }; + await onOpError(opErr); + throw new OperationFailedAndReportedError(opErr); } -} \ No newline at end of file +} -- cgit v1.2.3