Skip to content

[O2B-1623] Add HTTP API documentation (OpenAPI spec, generated reference and /api-docs page) - #2261

Merged
graduta merged 9 commits into
mainfrom
feature/O2B-1623/http-api-documentation
Oct 20, 2026
Merged

graduta merged 9 commits into
mainfrom
feature/O2B-1623/http-api-documentation

Conversation

@graduta

@graduta graduta commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

I have a JIRA ticket

  • branch and/or PR name(s) include(s) JIRA ID
  • issue has "Fix version" assigned
  • issue "Status" is set to "In review"
  • PR labels are selected

Notable changes for users:

  • The HTTP API is now documented at /api-docs (styled page with collapsible endpoints and curl examples). Raw versions are available at /api-docs/API.md (Markdown, suited for LLMs) and /api-docs/openapi.yaml (OpenAPI 3.1 spec, suited for tools and MCP servers).
  • Pilot: only the tags endpoints are documented so far.
  • The pages are served without authentication; calling the API from them still needs a token.
  • GET /api/tags/:tagId/runs and GET /api/tags/:tagId/log are removed: they only ever answered 501 Not implemented (or were never bound). They now answer 404 like any unknown route. The GUI and the tests did not use them.

Notable changes for developers:

  • lib/server/api-docs/openapi.yaml is the only file to edit by hand. Run npm run docs:api afterwards to regenerate API.md and index.html (generated, committed, never edited by hand). The generator is scripts/build-api-docs.js and the page styles are in scripts/api-docs.css.
  • Every non-GET operation must declare x-llm-exclude: true|false (write operations are not meant for LLM / MCP tool use); the generator fails otherwise. The spec also gives LLMs usage rules (always paginate, 100 items per request at most).
  • Security of the generator: spec content is treated as untrusted. Raw HTML and unsafe link schemes are neutralised, and values put in curl examples are shell-quoted. Checked with hostile input (quote break-out, $(...), HTML in title and description, javascript: links).
  • New devDependencies: js-yaml 4.3.2 (already in the lockfile through eslint) and marked 18.0.14 (MIT, no dependencies, no install scripts, released more than two weeks ago). Neither is shipped to production. npm audit reports nothing for them (it still reports the existing vulnerable js-yaml 3.x nested in nyc).
  • WebUiServer serves the three files from lib/server/api-docs/ (inside lib/ so they are part of the Docker image and the dev bind mount).
  • Tags router: removed the duplicated /runs route whose controller (TagsController.patchRun) does not exist, the two 501 stub routes and their stub controllers (getRuns, patchLog).
  • The route binder (lib/server/routers/index.js) now throws, naming the route, when a route declares a controller that is not a function. Before, such routes were silently skipped, which hid the missing patchRun. Grouping nodes without a controller key (/statistics, /dpl-process) are unaffected. Unit tests added in test/lib/server/routers.
  • Not included yet: a test checking the spec against the routers, and a CI check that the generated files are up to date.

Changes made to the database:

  • None

🤖 Generated with Claude Code

@graduta
graduta requested a review from isaachilly as a code owner October 20, 2026 12:36
@graduta graduta added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file backend labels Oct 20, 2026
@graduta graduta self-assigned this Oct 20, 2026
Comment thread scripts/build-api-docs.js Fixed
Describe the tags endpoints (list, create, get by id or name, update, delete, logs of a tag) with parameters, schemas, errors, required roles and
examples. Operations that change data are flagged `x-llm-exclude: true`, and the spec carries instructions for LLMs (always paginate, 100 items max).
`npm run docs:api` builds `API.md` (Markdown with curl examples) and `index.html` (styled page, collapsible endpoints) from the OpenAPI spec.

- Fails when a non-GET operation does not declare `x-llm-exclude`
- Spec content is treated as untrusted when rendering: raw HTML is escaped, unsafe link schemes are dropped, and values put in curl examples are
  shell-quoted
- New devDependencies `js-yaml` (already in the lockfile through eslint) and `marked`, pinned to a release older than two weeks
Serve the generated HTML page (`/api-docs`), the Markdown version for LLMs (`/api-docs/API.md`) and the spec (`/api-docs/openapi.yaml`). The files
live under `lib/server` so that they are part of the Docker image.
@graduta
graduta force-pushed the feature/O2B-1623/http-api-documentation branch from a104117 to 04dda18 Compare October 20, 2026 12:38
@graduta graduta removed their assignment Oct 20, 2026
@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 46.46%. Comparing base (eca7057) to head (99dbed6).

Files with missing lines Patch % Lines
lib/server/WebUiServer.js 57.14% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2261      +/-   ##
==========================================
+ Coverage   46.42%   46.46%   +0.03%     
==========================================
  Files        1053     1053              
  Lines       17353    17359       +6     
  Branches     3161     3162       +1     
==========================================
+ Hits         8056     8065       +9     
+ Misses       9297     9294       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- `GET /tags/:tagId/runs` was declared twice: once with a controller answering 501 and once with `TagsController.patchRun`, which does not exist,
  so the binder silently skipped it
- `GET /tags/:tagId/log` answered 501 as well

None of them is used by the GUI or covered by tests, and none did anything but advertise endpoints that do not exist. They now answer 404 like any
unknown route. The matching `getRuns` and `patchLog` stub controllers are removed.
… not a function

The route binder silently skipped routes whose controller was undefined (for example a typo or a missing export, as with the removed
`GET /tags/:tagId/runs`). It now throws, naming the route. Nodes that only group children, such as `/statistics` and `/dpl-process`, are not affected.

Unit tests check that every declared route binds, that grouping nodes are ignored, and that undefined controllers (alone or inside a list) are rejected.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Documentation generation, schema accuracy, and text contrast need correction before approval.

4 open issues
What changed in this PR

Adds a tags-focused HTTP API documentation pilot to Bookkeeping, served publicly at /api-docs while API calls remain authenticated.

Changes:

  • Adds an OpenAPI specification and generated Markdown/HTML references with curl examples.
  • Adds documentation generation tooling and page styling.
  • Removes unused tag routes and validates route controllers, with unit tests.
File Description
test/​lib/​server/​routers/​routers.test.js Tests controller validation and route binding.
test/​lib/​server/​routers/​index.js Registers router tests.
test/​lib/​server/​index.js Includes the router suite.
scripts/​build-api-docs.js Generates API references from OpenAPI.
scripts/​api-docs.css Styles the documentation page.
package.json Adds generation command and dependencies.
package-lock.json Locks documentation dependencies.
lib/​server/​WebUiServer.js Serves documentation files.
lib/​server/​routers/​tags.router.js Removes unimplemented routes.
lib/​server/​routers/​index.js Rejects invalid route controllers.
lib/​server/​controllers/​tags.controller.js Removes stub controllers.
lib/​server/​api-docs/​openapi.yaml Defines the tags API specification.
lib/​server/​api-docs/​index.html Provides the generated HTML reference.
lib/​server/​api-docs/​API.md Provides the generated Markdown reference.

Review effort: Balanced

Comment thread lib/server/api-docs/openapi.yaml Outdated
Comment thread scripts/api-docs.css Outdated
Comment thread scripts/build-api-docs.js Outdated
Comment thread scripts/build-api-docs.js Outdated
graduta and others added 2 commits October 20, 2026 17:03
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Allow epoch milliseconds for archivedAt in UpdateTag
- URL-encode query values in the curl examples
- Darken method badge and link colours to reach a 4.5:1 contrast

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@graduta
graduta requested a lite review from Copilot October 20, 2026 15:09
Comment thread scripts/build-api-docs.js Dismissed
…e cells

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Six unresolved moderate findings affect API accuracy, generated shell examples, and untrusted-content sanitization.

No open issues 🎉

3 previously missed issues

In code that hasn't changed since last review

Medium severity Missing required validation for query.name

lib/​server/​api-docs/​openapi.yaml:131

GetTagByNameDto does not mark query.name as required, so a request without this parameter passes DTO validation and decodeURIComponent(undefined) in the controller path can throw instead of returning the documented validation response. Either make the DTO field required (with a regression test) or change this contract to match the current behavior before publishing it as the API reference.

Medium severity Tag pattern incorrectly documents and enforces allowed characters

lib/​server/​api-docs/​openapi.yaml:320

The description says tag text permits only words, digits, -, and +, but the pattern includes | in its character class and is not anchored; it therefore documents stricter validation than the current CreateTagDto actually enforces (for example, UNIX:... already passes the server validation). Anchor/fix the DTO and schema together, or describe the real accepted values here.

Medium severity POSIX shell escaping corrupts exclamation marks in URLs

scripts/​build-api-docs.js:72

This shell escaping incorrectly prefixes ! with a backslash inside a double-quoted POSIX shell string. POSIX shells preserve that backslash ("a\\!b" becomes a\\!b), while encodeURIComponent leaves ! unescaped, so a spec value containing ! produces a different URL in the generated curl example. Do not escape ! for POSIX double quotes, or quote the complete URL with a method that handles all values correctly.

4 resolved since last review

Review effort: Lite

@graduta
graduta merged commit 4437a89 into main Oct 20, 2026
24 of 25 checks passed
@graduta
graduta deleted the feature/O2B-1623/http-api-documentation branch October 20, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation

Development

Successfully merging this pull request may close these issues.

4 participants