From b847bb71f47e514d3f8cc004c7426b248c881f73 Mon Sep 17 00:00:00 2001 From: Iku-turso Date: Thu, 23 Mar 2023 17:16:00 +0200 Subject: [PATCH] Introduce "call-result" to formalize non-failing yet non-fatal behaviour Signed-off-by: Iku-turso Co-authored-by: Gabriel --- .../renderer/call-result/call-result.test.ts | 55 +++++++++++++++++++ .../renderer/call-result/call-result.ts | 52 ++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 packages/core/src/features/telemetry/renderer/call-result/call-result.test.ts create mode 100644 packages/core/src/features/telemetry/renderer/call-result/call-result.ts diff --git a/packages/core/src/features/telemetry/renderer/call-result/call-result.test.ts b/packages/core/src/features/telemetry/renderer/call-result/call-result.test.ts new file mode 100644 index 0000000000..b52badf186 --- /dev/null +++ b/packages/core/src/features/telemetry/renderer/call-result/call-result.test.ts @@ -0,0 +1,55 @@ +import { + CallResult, + callWasFailure, + callWasSuccessful, + getFailure, + getSuccess, +} from "./call-result"; + +describe("call-result", () => { + it("given successful call, narrows type of response", () => { + const someSuccess: CallResult = getSuccess("some-success"); + + if (callWasSuccessful(someSuccess)) { + expect(someSuccess.response).toBe("some-success"); + } + + expect.assertions(1); + }); + + it("given successful call, call is not failure", () => { + const actual = callWasFailure(getSuccess("some-success")); + + expect(actual).toBe(false); + }); + + it("given unsuccessful call, narrows type of error", () => { + const someFailure: CallResult = getFailure( + "some-cause", + ); + + // Todo: find out why this this type narrowing isn't working anymore. + if (callWasFailure(someFailure)) { + expect(someFailure.error).toEqual({ + cause: "some-cause", + message: "some-cause", + }); + } + + expect.assertions(1); + }); + + it("given unsuccessful call, call is not successful", () => { + const actual = callWasSuccessful( + getFailure("some-cause") + ); + + expect(actual).toBe(false); + }); + + it('when a call fails by throwing a thing with no known message, fails with no message', () => { + const failure = getFailure({ noKnownMessage: 'irrelevant' }); + + expect(failure.error.message).toBeUndefined(); + }); +}); diff --git a/packages/core/src/features/telemetry/renderer/call-result/call-result.ts b/packages/core/src/features/telemetry/renderer/call-result/call-result.ts new file mode 100644 index 0000000000..f925ffe0eb --- /dev/null +++ b/packages/core/src/features/telemetry/renderer/call-result/call-result.ts @@ -0,0 +1,52 @@ +import { isString } from "lodash/fp"; + +export type CallFailure = { + callWasSuccessful: false; + error: { message: string | undefined; cause: unknown }; +}; + +export type CallSuccess = Response extends void + ? { callWasSuccessful: true; response?: undefined } + : { callWasSuccessful: true; response: Response }; + +export type CallResult = CallSuccess | CallFailure; + +export type AsyncCallResult = Promise>; + +export type AsyncCallSuccess = Promise>; + +export const getSuccess = (response: T) => ({ + callWasSuccessful: true as const, + response, +}); + +const getErrorMessage = (cause: unknown) => { + if (isString(cause)) { + return cause; + } + + const causeObject = cause as any; + + if (causeObject.message) { + return causeObject.message; + } + + return undefined; +}; + +export const getFailure = (errorCause: unknown) => ({ + callWasSuccessful: false as const, + + error: { + cause: errorCause, + message: getErrorMessage(errorCause), + }, +}); + +export const callWasSuccessful = ( + callResult: CallResult +): callResult is CallSuccess => callResult.callWasSuccessful; + +export const callWasFailure = ( + callResult: CallResult +): callResult is CallFailure => !callResult.callWasSuccessful;