UNPKG

counterfact

Version:

a library for building a fake REST API for testing

110 lines (109 loc) 3.98 kB
import createDebugger from "debug"; import { ModuleTree } from "./module-tree.js"; const debug = createDebugger("counterfact:server:registry"); function castParameter(value, type) { if (typeof value !== "string") { return value; } if (type === "integer") { return Number.parseInt(value); } if (type === "number") { return Number.parseFloat(value); } if (type === "boolean") { return value === "true"; } return value; } function castParameters(parameters = {}, parameterTypes = {}) { const copy = {}; Object.entries(parameters).forEach(([key, value]) => { copy[key] = castParameter(value, parameterTypes?.[key] ?? "string"); }); return copy; } export class Registry { moduleTree = new ModuleTree(); middlewares = new Map(); constructor() { this.middlewares.set("/", ($, respondTo) => respondTo($)); } get routes() { return this.moduleTree.routes; } add(url, module) { this.moduleTree.add(url, module); } addMiddleware(url, callback) { this.middlewares.set(url, callback); } remove(url) { this.moduleTree.remove(url); } exists(method, url) { return Boolean(this.handler(url, method).module?.[method]); } handler(url, method) { const match = this.moduleTree.match(url, method); return { matchedPath: match?.matchedPath ?? "", module: match?.module, path: match?.pathVariables ?? {}, }; } endpoint(httpRequestMethod, url, parameterTypes = {}) { const handler = this.handler(url, httpRequestMethod); debug("handler for %s: %o", url, handler); const execute = handler.module?.[httpRequestMethod]; if (!execute) { return () => ({ body: `Could not find a ${httpRequestMethod} method matching ${url}\n`, contentType: "text/plain", headers: {}, status: 404, }); } return async ({ ...requestData }) => { const operationArgument = { ...requestData, headers: castParameters(requestData.headers, parameterTypes.header), matchedPath: handler.matchedPath, path: castParameters(handler.path, parameterTypes.path), query: castParameters(requestData.query, parameterTypes.query), }; operationArgument.x = operationArgument; const executeAndNormalizeResponse = async (requestData) => { const result = await execute(requestData); if (typeof result === "string") { return { headers: {}, status: 200, body: result, contentType: "text/plain", }; } if (typeof result === "undefined") { return { headers: {}, body: `The ${httpRequestMethod} function did not return anything. Did you forget a return statement?`, status: 500, }; } return result; }; const middlewares = this.middlewares; function recurse(path, respondTo) { if (path === null) return respondTo; const nextPath = path === "" ? null : path.slice(0, path.lastIndexOf("/")); const middleware = middlewares.get(path); if (middleware !== undefined) { return recurse(nextPath, ($) => middleware($, respondTo)); } return recurse(nextPath, respondTo); } return recurse(operationArgument.matchedPath ?? "/", executeAndNormalizeResponse)(operationArgument); }; } }