From a7253411e96e2dad6ee562a763e66273915469cc Mon Sep 17 00:00:00 2001 From: Den Williams Date: Mon, 15 Jan 2024 17:24:26 +1100 Subject: [PATCH] print extra keys when stringifying --- lib/client.ts | 44 +++++++++++++++++++++++++++++++-- test/rpc-response-error.test.ts | 36 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 test/rpc-response-error.test.ts diff --git a/lib/client.ts b/lib/client.ts index f5724e6..6dd01b7 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -110,7 +110,45 @@ function mapError(serviceName: string, methodName: string, errResult: any) { throw new RpcResponseError(source, errResult); } -class RpcResponseError { +const EXCLUDED_META_KEYS = [ + "type", + "code", + "expose", + "message", + "namespace", + "instance", + "source", +]; + +function logfmt(data: Record) { + // taken from https://github.com/csquared/node-logfmt/blob/master/lib/stringify.js + var line = ""; + + for (var key in data) { + if (EXCLUDED_META_KEYS.includes(key)) continue; + + var value = data[key]; + var is_null = false; + if (value == null) { + is_null = true; + value = ""; + } else value = value.toString(); + + var needs_quoting = value.indexOf(" ") > -1 || value.indexOf("=") > -1; + var needs_escaping = value.indexOf('"') > -1 || value.indexOf("\\") > -1; + + if (needs_escaping) value = value.replace(/["\\]/g, "\\$&"); + if (needs_quoting || needs_escaping) value = '"' + value + '"'; + if (value === "" && !is_null) value = '""'; + + line += key + "=" + value + " "; + } + + // trim trailing space + return line.substring(0, line.length - 1); +} + +export class RpcResponseError { source?: string[]; constructor(source: string, responseBody: any) { @@ -143,7 +181,9 @@ class RpcResponseError { toString() { // defineProperty not recognized by typescript, nor is the result of assign recognizable const { name, message, instance } = this as any; - return `${name}: ${message} [${instance}]`; + const meta = logfmt(this); + + return `${name}: ${message} [${instance}]${meta ? " " + meta : ""}`; } } diff --git a/test/rpc-response-error.test.ts b/test/rpc-response-error.test.ts new file mode 100644 index 0000000..a48664c --- /dev/null +++ b/test/rpc-response-error.test.ts @@ -0,0 +1,36 @@ +import test from "ava"; + +import { RpcResponseError } from "../lib/client"; + +test("RpcResponseError", (t) => { + const err: any = new RpcResponseError("my_source", { + type: "FooError", + code: 123, + expose: true, + message: "foo", + namespace: "foo", + instance: "bar", + source: ["foo", "bar"], + upstreamCode: 456, + upstreamMessage: "a bad thing happened", + }); + + t.is( + err.toString(), + 'RpcResponseError: foo [bar] upstreamCode=456 upstreamMessage="a bad thing happened"' + ); + t.is(err.name, "RpcResponseError"); + t.is(err.type, "FooError"); + t.is(err.code, 123); + t.is(err.expose, true); + t.is(err.message, "foo"); + t.is(err.namespace, "foo"); + t.is(err.instance, "bar"); + t.deepEqual(err.source, ["my_source", "foo", "bar"]); + t.is( + err.stack, + 'RpcResponseError: foo [bar] upstreamCode=456 upstreamMessage="a bad thing happened"\n via bar\n via foo\n via my_source' + ); + t.is(err.upstreamCode, 456); + t.is(err.upstreamMessage, "a bad thing happened"); +});