feat(all-services): add openapi documentation files to arc-saas services#123
Conversation
add openapi documentation files to arc-saas services GH-122
remove node js 20 support BREAKING CHANGE: yes GH-122
SonarQube reviewer guide
|
| "preopenapi-spec": "npm run build", | ||
| "preopenapi-spec": "lb-tsc", | ||
| "openapi-spec": "node ./dist/openapi-spec", | ||
| "apidocs": "npx widdershins --language_tabs 'javascript:JavaScript:request' 'javascript--nodejs:Node.JS' --summary openapi.json -o openapi.md", |
There was a problem hiding this comment.
Two things on the doc generation here (same applies to subscription-service and tenant-management-service — identical scripts):
npx widdershinsresolves and downloads widdershins from the registry at build time — non-deterministic (picks up new releases), network-dependent (fails on offline/air-gapped CI or a registry blip), and slow on a clean environment. Sincebuildnow callsapidocs, a bad widdershins release would break every build of every service. Pin it as a devDependency and call the local bin:
"devDependencies": { "widdershins": "4.0.1" },
"scripts": {
"apidocs": "widdershins --language_tabs 'javascript:JavaScript:request' 'javascript--nodejs:Node.JS' --summary openapi.json -o openapi.md"
}build: lb-tsc && npm run openapi-spec && npm run apidocscouples doc generation into the standard build that Docker/deploy run, and sincepreopenapi-specislb-tsc, the compile runs twice per build. Consider keepingbuildas justlb-tsc, moving docs to a dedicated script, and adding a CI step that regenerates and fails if the committed spec drifted:
"build": "lb-tsc",
"docs": "npm run openapi-spec && npm run apidocs"
// CI: npm run docs && git diff --exit-code services/**/openapi.*Keeps deploy builds offline-safe and avoids the double compile while still guaranteeing the committed spec matches the code.
rohit-sourcefuse
left a comment
There was a problem hiding this comment.
Solid, low-risk PR — OpenAPI spec + markdown across all three services, Node 20 dropped consistently (CI matrix + engines), and the Trivy action is now pinned to a commit SHA. Nice incidental fixes too: the Audit Service → Tenant Management Service title correction across the three components, the openapi.json output-path fix, and preopenapi-spec: lb-tsc to avoid the build recursion. Checks are green.
One thing worth doing before merge: pin widdershins as a devDependency instead of npx so doc generation (now part of build) doesn't depend on a registry fetch at build time — details inline. Couple of non-blocking nits there too (decoupling docs from build, and the "22 || 24" engines being an exact allow-list rather than >=22).
Approving — the inline points are improvements, not blockers.
rohit-sourcefuse
left a comment
There was a problem hiding this comment.
Did a closer pass on the hand-written bits — a few things worth a look beyond the widdershins comment above. None are hard blockers, but the first two are worth deciding on before merge.
1. The orchestrator OpenAPI doc documents nothing real. services/orchestrator-service/openapi.json has only paths: /ping, / and components.schemas: [], and info.title is the package name @sourceloop/ctrl-plane-orchestrator-service. The orchestrator only has the boilerplate ping/home-page controllers — it's a component consumed by other apps, not a standalone REST API. For comparison subscription documents 45 paths and tenant 24. So we're adding a new application.ts + shipping an openapi.json/.md that advertise an essentially empty contract. Either orchestrator genuinely has no standalone API (then skip the doc for it, or clearly label it), or the real controllers/components aren't being registered in the new application.ts. Also worth fixing info.title to a human title like the tenant fix did.
2. npm test now boots the app and fetches widdershins over the network. pretest → rebuild → build, and build now runs openapi-spec (app.boot) + apidocs (npx widdershins). So a boot hang or a widdershins/registry failure now fails the test job (misleading signal), and Dockerfile RUN npm run build is gated on a network fetch. Pinning widdershins (comment above) fixes the network half; decoupling docs from build fixes the rest.
3. Orchestrator's exporter can hang the build silently. services/orchestrator-service/src/openapi-spec.ts uses .catch(()=>process.exit(1)) with no .then(()=>process.exit(0)), while subscription and tenant force process.exit(0) on success. If app.boot() leaves open handles (event-stream/message-bus listeners), the orchestrator step keeps the process alive with no error — a silent stall. Standardize all three on .then(()=>process.exit(0)).catch(err=>{console.error(...);process.exit(1)}), ideally with await app.stop() before exit.
Smaller / nits
- Stale
Copyright (c) 2023header on the brand-new 2026 files (application.ts,openapi-spec.ts). - The three
openapi-spec.tsare gratuitously divergent (license header only on orchestrator,PORTvsDEFAULT_PORT, different success handler,//NOSONARon only one) — worth normalizing them to byte-identical. orchestrator application.tsimportsnode:pathwhile the peers usepath; andexport {ApplicationConfig}is dead here (the exporter imports it from@loopback/core).subscription-serviceis missing theposttest: npm run lintthe other two have; tenant/subscriptiondocker:push:devuse a literal:undefinedtag (pre-existing, but broken).- Nice-to-have: a CI drift-guard — regenerate the spec and
git diff --exit-code services/**/openapi.*so the committed docs can't silently go stale.
Happy to hop on a quick call if it's easier to walk through the orchestrator-spec question.
rohit-sourcefuse
left a comment
There was a problem hiding this comment.
Quick clarification on ownership so the earlier comments are fair — splitting "this PR's changes" from "inherited / expected":
- The OpenAPI exporter pattern (
openapi-spec.ts→node ./dist/openapi-spec) already existed in subscription/tenant; this PR replicates it for orchestrator and adds markdown generation. So the exporter structure isn't a design choice to defend here. - What is new in this PR (all three services) is wiring docs into
buildand thenpx widdershinsstep. That part is in scope and is the one worth tightening. - The orchestrator "empty spec" is an expected consequence of orchestrator being a component (only
ping/home-pagecontrollers), not a bug — see below.
Concrete, working changes:
1. Pin widdershins, drop npx (all three services). This is the one clear win and it's small. widdershins isn't a declared dep, so npx fetches it from the registry on every build (non-deterministic + breaks offline/Docker). Pin to the version that generated these docs (Generator: Widdershins v4.0.1):
// services/<svc>/package.json
"devDependencies": {
"widdershins": "4.0.1"
},
"scripts": {
"apidocs": "widdershins --language_tabs 'javascript:JavaScript:request' 'javascript--nodejs:Node.JS' --summary openapi.json -o openapi.md"
}Then npm install to lock it. (No npx, no network at build time.)
2. Optional but recommended — keep docs out of build. Because pretest → rebuild → build, the doc step now runs during npm test and RUN npm run build in the Dockerfile. Move it to its own script + a CI drift-guard so docs stay fresh without coupling to compile/test:
"scripts": {
"build": "lb-tsc",
"openapi-spec": "node ./dist/openapi-spec",
"preopenapi-spec": "lb-tsc",
"apidocs": "widdershins --language_tabs 'javascript:JavaScript:request' 'javascript--nodejs:Node.JS' --summary openapi.json -o openapi.md",
"docs": "npm run openapi-spec && npm run apidocs"
}CI step (one place, catches stale specs):
- run: npm run docs --workspaces && git diff --exit-code services/**/openapi.{json,md}3. Normalize the orchestrator exporter so the build can't hang. orchestrator's openapi-spec.ts only does .catch(exit 1); subscription/tenant force exit(0). If app.boot() leaves an open handle, the orchestrator step stalls with no error. Match the other two and stop the app:
exportOpenApiSpec()
.then(() => process.exit(0))
.catch(err => {
console.error(`Failed to export OpenAPI spec to ${outFile}:`, err); //NOSONAR
process.exit(1);
});(and await app.stop() before returning, if boot starts any listeners.)
4. Orchestrator spec — just confirm intent. Since orchestrator only exposes ping/home-page, its openapi.json will always be near-empty — that's expected for a component, not something you did wrong. If a doc is still wanted for it (the "all three services" ask), set a human info.title (it currently shows the package name) the way tenant's component title was fixed. If it isn't actually consumed, it's fine to skip generating one for orchestrator.
Everything else I flagged (2023 headers, the three openapi-spec.ts being slightly different) is cosmetic — fine as follow-ups. #1 is the only thing I'd genuinely do before merge.



add openapi documentation files to arc-saas services
GH-122
Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
Checklist:
Build:
Test: