1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Introduce "call-result" to formalize non-failing yet non-fatal behaviour

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

Co-authored-by: Gabriel <gaccettola@mirantis.com>
This commit is contained in:
Iku-turso 2023-03-23 17:16:00 +02:00
parent 39a5c71aef
commit b847bb71f4
2 changed files with 107 additions and 0 deletions

View File

@ -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<string> = 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<string> = 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();
});
});

View File

@ -0,0 +1,52 @@
import { isString } from "lodash/fp";
export type CallFailure = {
callWasSuccessful: false;
error: { message: string | undefined; cause: unknown };
};
export type CallSuccess<Response> = Response extends void
? { callWasSuccessful: true; response?: undefined }
: { callWasSuccessful: true; response: Response };
export type CallResult<Response> = CallSuccess<Response> | CallFailure;
export type AsyncCallResult<Response> = Promise<CallResult<Response>>;
export type AsyncCallSuccess<Response> = Promise<CallSuccess<Response>>;
export const getSuccess = <T>(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 = <Result>(
callResult: CallResult<Result>
): callResult is CallSuccess<Result> => callResult.callWasSuccessful;
export const callWasFailure = <Result>(
callResult: CallResult<Result>
): callResult is CallFailure => !callResult.callWasSuccessful;