Parallel APIs second pass: add specs array to counterfact.yaml#1845
Parallel APIs second pass: add specs array to counterfact.yaml#1845
specs array to counterfact.yaml#1845Conversation
specs array to counterfact.yaml
pmcelhaney
left a comment
There was a problem hiding this comment.
Add more tests. Make sure the code is generated correctly. Make sure the REPL works.
Also add black box tests.
Added in commits Unit tests (
Black-box tests (
|
pmcelhaney
left a comment
There was a problem hiding this comment.
Instead of a multispecmiddleware, pass an array of middleware to createKoaApp() and use() each one in succession.
Done in commit |
pmcelhaney
left a comment
There was a problem hiding this comment.
When the config specifies a single spec, translate that to a config that has a specs property with one item.
After that there should be no divergence for single vs. multiple specs.
Done in commit |
pmcelhaney
left a comment
There was a problem hiding this comment.
adminApiMiddleware should be built in app.js and passed in to createKoaApp()
| const primaryBundle = specBundles[0] as SpecBundle; | ||
| const primarySpec = specs[0] as (typeof specs)[number]; | ||
|
|
||
| // Build one per-spec middleware. For the normalised single-spec entry |
There was a problem hiding this comment.
Updated in commit ec465e0 — the single-spec test now asserts Array.isArray(result.koaMiddleware) is true and result.koaMiddleware has length 1, reflecting that the unified code path always returns an array.
…(); assert koaMiddleware is always array Agent-Logs-Url: https://github.com/counterfact/api-simulator/sessions/87b4e1c7-6a5a-4e3e-a77b-39ea614ec85f
Done in commit |
pmcelhaney
left a comment
There was a problem hiding this comment.
There should only be one transpiler, not one per bundle.
Done in |
Summary
Adds a
specsarray tocounterfact.yamlthat mounts multiple OpenAPI documents at distinct URL base paths from a single server instance. Whenspecsis present it takes precedence overspec.Each spec is fully isolated: its own
Registry,Dispatcher,CodeGenerator, andModuleLoader. One Koa middleware is created per spec (each pre-configured with the spec's route prefix); they are passed as an array tocreateKoaApp(), which registers them withapp.use()in succession — no existing class needed to be made multi-API-aware, and no composite fan-out middleware is required.Single-spec and multi-spec configurations share a single code path: a plain
openApiPathconfig is normalised at startup tospecs = [{ source, base: "" }], so there is no divergence between the two modes.koaMiddlewarein the returned object is always an array (length 1 for single-spec, N for N specs).adminApiMiddlewareis constructed inapp.tsand passed intocreateKoaApp()as an optional parameter, socreateKoaApp()no longer needs to import or build it internally and no longer requiresregistryorcontextRegistryin its signature.A single shared
Transpilerinstance covers all specs.Transpiler(rootPath, moduleKind)watchesrootPathrecursively and applies the convention that any.tsfile under aroutes/subdirectory is compiled to the sibling.cache/directory (e.g.{base}/routes/foo.ts→{base}/.cache/foo.cjs). Inapp.ts, onenew Transpiler(config.basePath, "commonjs")covers all specs automatically — no per-spec instances, no array of path mappings.Generated files land under
{destination}/{base}/routes/and{destination}/{base}/types/. For single-spec mode (normalised withbase: ""), files continue to land directly under{destination}/routes/and{destination}/types/as before.Original Prompt
Add a
specsarray tocounterfact.yamlthat mounts multiple OpenAPI documents at distinct URL base paths from a single Counterfact server instance.Manual acceptance tests
counterfact.yamlwith aspecsarray starts the server; requests to/billing/...and/identity/...are served by their respective specsspecstakes precedence over aspeckey in the same config file — the singlespecvalue is ignored/billing/invoicesis validated againstbilling.yaml; request to/identity/usersis validated againstidentity.yamlbaseprefix (e.g./gamma/...) falls through to a 404 — not silently matched by another specspec:or positionalopenapi.yamlargument) is unaffected_(no spec) still works unchangedTasks
SpecEntryinterface (source,base) andspecs?: SpecEntry[]toConfigcreateSpecBundle()— creates the full per-spec service set (Registry, Dispatcher, CodeGenerator, ModuleLoader); handlessource === "_"(no document loaded) and optionally wires scenarios for the primary spec; no transpiler is created herecreateKoaApp()now acceptsKoa.Middleware | Koa.Middleware[]and registers each withapp.use()in order (removedbuildMultiSpecMiddleware());registryandcontextRegistryparameters removed —createKoaApp()now accepts an optional pre-builtadminMiddleware?: Koa.MiddlewareinsteadadminApiMiddlewareis constructed inapp.ts(conditionally whenconfig.startAdminApi) and passed tocreateKoaApp()— keeping middleware construction co-located with configuration logicTranspilerrefactored toTranspiler(rootPath, moduleKind): watchesrootPathrecursively; any.tsfile under aroutes/subdirectory is compiled to the sibling.cache/directory; one shared instance per server covers all specscounterfact()normalises a plain single-spec config tospecs = [{ source: config.openApiPath, base: "" }]at startup, then runs a single unified loop — no separate single-spec branch;koaMiddlewarein the returned object is always an array; one sharedTranspileris created withconfig.basePathas the rootbase === ""preserveconfig.routePrefix; named specs use"/" + baseas their route prefixbin/counterfact.js: readsspecsfrom config file; setsopenApiPath = "_"whenspecsis active; passesspecsinto the config objectspecsYAML parsing inload-config-file.test.ts; multi-spec shape and per-spec middleware prefix routing inapp.test.ts; single-spec test updated to assertkoaMiddlewareis always an array with length 1rootPath+routes/→.cache/convention; two new tests added: one verifying files outsideroutes/are ignored, one verifying multi-spec compilation under the same roottest/app.multispec.test.ts): code generation verifies route files appear under{base}/routes/for each spec without cross-contamination, type files generated under{base}/types/, andstartRepl()is wired to the primary spec's registrytest-black-box/test_parallel_apis.py) with dedicated OpenAPI fixtures (alpha.yaml,beta.yaml): distinct responses per prefix, file isolation between specs, 404 for unmatched prefix, andspecsprecedence overspeccounterfact.yamland parallel-APIs section todocs/reference.md