feat: Worked with matt to create all the things

This commit is contained in:
BluJ
2023-03-02 13:25:07 -07:00
parent de043ddd9e
commit 19504ef559
38 changed files with 990 additions and 1160 deletions

View File

@@ -0,0 +1,75 @@
import { Types } from "..";
import { object, string } from "ts-matches";
export type HealthCheck = (
effects: Types.Effects,
dateMs: number
) => Promise<HealthResult>;
export type HealthResult =
| { success: string }
| { failure: string }
| { disabled: null }
| { starting: null }
| { loading: string };
const hasMessage = object({ message: string }).test;
function safelyStringify(e: unknown) {
if (hasMessage(e)) return e.message;
if (string.test(e)) return e;
try {
return JSON.stringify(e);
} catch (e) {
return "unknown";
}
}
async function timeoutHealth(
effects: Types.Effects,
timeMs: number
): Promise<HealthResult> {
await effects.sleep(timeMs);
return { failure: "Timed out " };
}
export default function healthRunner(
name: string,
fn: HealthCheck,
{ defaultIntervalS = 60 } = {}
) {
return {
create(effects: Types.Effects, defaultIntervalCreatedS = defaultIntervalS) {
let running: any;
function startFn(intervalS: number, timeoutS: number, delayS: number) {
clearInterval(running);
setTimeout(() => {
running = setInterval(async () => {
const result = await Promise.race([
timeoutHealth(effects, timeoutS * 1000),
fn(effects, 123),
]).catch((e) => {
return { failure: safelyStringify(e) };
});
(effects as any).setHealthStatus({
name,
result,
});
}, intervalS * 1000);
}, delayS * 1000);
}
const self = {
stop() {
clearInterval(running);
return self;
},
start({
intervalS = defaultIntervalCreatedS,
timeoutS = 10,
delayS = 0,
} = {}) {
startFn(intervalS, timeoutS, delayS);
return self;
},
};
return self;
},
};
}

2
lib/health/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./util";
export { default as healthRunner } from "./healthRunner";

54
lib/health/util.ts Normal file
View File

@@ -0,0 +1,54 @@
import { Effects, ResultType } from "../types";
import { error, errorCode, isKnownError, ok } from "../util";
export const checkWebUrl: (
url: string
) => (
effects: Effects,
duration: number
) => Promise<ResultType<null | void>> = (url) => {
return async (effects, duration) => {
let errorValue;
if (
// deno-lint-ignore no-cond-assign
(errorValue = guardDurationAboveMinimum({ duration, minimumTime: 5000 }))
) {
return errorValue;
}
return await effects
.fetch(url)
.then((_) => ok)
.catch((e) => {
effects.warn(`Error while fetching URL: ${url}`);
effects.error(JSON.stringify(e));
effects.error(e.toString());
return error(`Error while fetching URL: ${url}`);
});
};
};
export const runHealthScript =
({ command, args }: { command: string; args: string[] }) =>
async (
effects: Effects,
_duration: number
): Promise<ResultType<null | void>> => {
const res = await effects.runCommand({ command, args });
if ("result" in res) {
return { result: null };
} else {
return res;
}
};
// Ensure the starting duration is pass a minimum
export const guardDurationAboveMinimum = (input: {
duration: number;
minimumTime: number;
}) => (input.duration <= input.minimumTime ? errorCode(60, "Starting") : null);
export const catchError = (effects: Effects) => (e: unknown) => {
if (isKnownError(e)) return e;
effects.error(`Health check failed: ${e}`);
return error("Error while running health check");
};