summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2024-03-01 17:07:30 -0300
committerEuAndreh <eu@euandre.org>2024-03-01 17:07:30 -0300
commit8cdab6f4184e332ca1fbb65562b98f2977a74d92 (patch)
tree071841ae385407eb2b9888ff57f975ce04347bf6
parentMakefile: Let the application recreate its pipes and sockets (diff)
downloadpapod-8cdab6f4184e332ca1fbb65562b98f2977a74d92.tar.gz
papod-8cdab6f4184e332ca1fbb65562b98f2977a74d92.tar.xz
src/hero.mjs: Add actionsFn() and lineHandlerFn()
-rw-r--r--src/hero.mjs76
-rw-r--r--tests/js/hero.mjs114
2 files changed, 189 insertions, 1 deletions
diff --git a/src/hero.mjs b/src/hero.mjs
index c249d4f..b623943 100644
--- a/src/hero.mjs
+++ b/src/hero.mjs
@@ -148,6 +148,7 @@ export const makeRequestListener = table => async (req, res) => {
export const loggerDefaults = {
pid: process.pid,
ppid: process.ppid,
+ tool: "hero",
};
export let loggerGlobals = {};
@@ -170,6 +171,7 @@ export const makeLogger = (writerFn = console.error) => ({
warn: u.partial(logit, writerFn, "WARN"),
error: u.partial(logit, writerFn, "ERROR"),
});
+
export const log = makeLogger();
export const interceptorsFn = ({
@@ -250,6 +252,80 @@ export const wrapHandler = (fn, arr) =>
fn :
chainInterceptors(arr.concat([ (req, _next) => fn(req) ]));
+export const actionsFn = ({
+ logger,
+} = {
+ logger: log,
+}) => ({
+ "toggle-debug-env": action => {
+ const before = process.env.DEBUG;
+ if (process.env.DEBUG) {
+ delete process.env.DEBUG;
+ } else {
+ process.env.DEBUG = "1";
+ }
+ const after = process.env.DEBUG;
+
+ logger.info({
+ type: "action-response",
+ action,
+ message: "toggle process.env.DEBUG",
+ before: u.undefinedAsNull(before),
+ after: u.undefinedAsNull(after),
+ });
+ },
+ "config-dump": action => logger.info({
+ type: "action-response",
+ action,
+ data: {
+ ...loggerDefaults,
+ ...loggerGlobals,
+ },
+ }),
+});
+
+export const actions = actionsFn();
+
+export const lineHandlerFn = ({
+ logger,
+ actionsMap,
+} = {
+ logger: log,
+ actionsMap: actions,
+}) => line => {
+ let cmd = null;
+ try {
+ cmd = JSON.parse(line);
+ } catch (e) {
+ logger.info({
+ type: "invalid-cmd-input",
+ message: e.message,
+ });
+ return;
+ }
+
+ if (typeof cmd?.action !== "string") {
+ logger.info({
+ type: "missing-key-action",
+ message: `missing the "action" key from the given object`,
+ });
+ return;
+ }
+
+ const fn = actionsMap[cmd.action];
+ if (!fn) {
+ logger.info({
+ type: "unsupported-action",
+ message: `can't run given action: ${cmd.action}`,
+ });
+ return;
+ }
+
+ return fn(cmd.action, ...(cmd?.args || []));
+};
+
+export const lineHandler = lineHandlerFn();
+
export const buildRoutes = (routes, globalInterceptors = []) =>
routes.reduce(
(acc, [methods, path, handlerFn, interceptors = []]) =>
diff --git a/tests/js/hero.mjs b/tests/js/hero.mjs
index 1e1668d..51fa84a 100644
--- a/tests/js/hero.mjs
+++ b/tests/js/hero.mjs
@@ -26,6 +26,8 @@ import {
defaultInterceptors,
chainInterceptors,
wrapHandler,
+ actionsFn,
+ lineHandlerFn,
buildRoutes,
promisifyServer,
buildServer,
@@ -758,9 +760,10 @@ const test_makeLogger = t => {
const log = makeLogger(writerFn);
log.debug({ x: "ignored" });
- process.env.DEBUG = 1;
+ process.env.DEBUG = "1";
log.debug({ x: "seen" });
delete process.env.DEBUG;
+ // call the function that toggles
log.debug({ x: "ignored" });
assert.deepEqual(contents.map(JSON.parse), [{
@@ -1106,6 +1109,113 @@ const test_wrapHandler = t => {
});
};
+const test_actionsFn = t => {
+ {
+ t.start(`actionsFn()["toggle-debug-env"()]`);
+
+ t.test("we can toggle back and forth", () => {
+ const contents = [];
+ const logger = { info: x => contents.push(x) };
+ const actions = actionsFn({logger});
+
+ assert.equal(process.env.DEBUG, undefined);
+ actions["toggle-debug-env"]("action-text-1");
+ assert.equal(process.env.DEBUG, "1");
+ actions["toggle-debug-env"]("action-text-2");
+ assert.equal(process.env.DEBUG, undefined);
+
+ assert.deepEqual(contents, [
+ {
+ type: "action-response",
+ action: "action-text-1",
+ message: "toggle process.env.DEBUG",
+ before: null,
+ after: "1",
+ },
+ {
+ type: "action-response",
+ action: "action-text-2",
+ message: "toggle process.env.DEBUG",
+ before: "1",
+ after: null,
+ },
+ ]);
+ });
+ }
+
+ {
+ t.start(`actionsFn()["config-dump"]()`);
+
+ t.test("we just dump data as a log entry", () => {
+ const contents = [];
+ const logger = { info: x => contents.push(x) };
+ const actions = actionsFn({logger});
+
+ configLogger({});
+ actions["config-dump"]("first-call");
+ configLogger({ some: "thing", });
+ actions["config-dump"]("second-call");
+ configLogger({});
+
+ assert.deepEqual(contents, [
+ {
+ type: "action-response",
+ action: "first-call",
+ data: {
+ pid: process.pid,
+ ppid: process.ppid,
+ tool: "hero",
+ },
+ },
+ {
+ type: "action-response",
+ action: "second-call",
+ data: {
+ pid: process.pid,
+ ppid: process.ppid,
+ tool: "hero",
+ some: "thing",
+ },
+ },
+
+ ]);
+ });
+ }
+};
+
+const test_lineHandlerFn = t => {
+ t.start("lineHandlerFn()");
+
+ t.test("empty values", () => {
+ const contents = [];
+ const logger = { info: x => contents.push(x) };
+ const lineHandler = lineHandlerFn({logger, actionsMap: {}});
+
+ lineHandler("");
+ lineHandler("{}");
+ lineHandler(`{"action": "this-action-does-not-exist"}`);
+
+ assert.deepEqual(contents.map(x => x.type), [
+ "invalid-cmd-input",
+ "missing-key-action",
+ "unsupported-action",
+ ]);
+ });
+
+ t.test("calling an action", () => {
+ const contents = [];
+ const logger = { info: x => contents.push(x) };
+ const lineHandler = lineHandlerFn({ logger: null, actionsMap: {
+ "an-action": (arg1, arg2, arg3) => [arg1, arg2, arg3],
+ }});
+
+ const ret1 = lineHandler(`{"action": "an-action"}`);
+ const ret2 = lineHandler(`{"action": "an-action", "args": [1, "text", 2]}`);
+ assert.deepEqual(ret1, ["an-action", undefined, undefined]);
+ assert.deepEqual(ret2, ["an-action", 1, "text"]);
+ });
+};
+
const test_buildRoutes = t => {
t.start("buildRoutes()");
@@ -1334,6 +1444,8 @@ await runner.runTests([
test_interceptorsFn,
test_chainInterceptors,
test_wrapHandler,
+ test_actionsFn,
+ test_lineHandlerFn,
test_buildRoutes,
test_promisifyServer,
test_buildServer,