OpenAPI document with Swagger UI (task 60) #27

Merged
rob merged 2 commits from feat/openapi-docs into main 2026-08-03 21:36:43 +00:00
Owner

Document at /openapi/v1.json, Swagger UI at /swagger, in every environment except Production — verified by running under ASPNETCORE_ENVIRONMENT=Production and confirming the document, page and UI assets all 404 while /health still answers 200. The document declares a bearer scheme and no security requirement, ready for task 52. Prose comes from XML doc comments, with /health as the worked example. Choice of Swagger UI over Scalar is in ADR-0029.

The "all endpoints documented" criterion is only partly satisfiable today — the API has /health plus the test-only endpoints from ADR-0022 and ADR-0025. This establishes the convention feature endpoints will follow rather than inventing endpoints to document.

Two things this imposes on everything that follows:

  • PlaceMark.Contracts now sets GenerateDocumentationFile, so an undocumented public member there is a build error. Anyone adding wire types must give them doc comments.
  • PlaceMark.Api.csproj carries a direct Microsoft.OpenApi 2.11.0 reference solely to escape GHSA-v5pm-xwqc-g5wc in the 2.0.0 that Microsoft.AspNetCore.OpenApi 10.0.10 resolves. It is a pin that holds the library back — delete it when a framework release resolves a patched version.

One unrelated fix was needed to build with documentation files on: an ambiguous cref in RequestValidator.cs.

docs/adr/README.md will conflict with the PRs in flight — keep the rows in numeric order.

Document at `/openapi/v1.json`, Swagger UI at `/swagger`, in every environment except Production — verified by running under `ASPNETCORE_ENVIRONMENT=Production` and confirming the document, page and UI assets all 404 while `/health` still answers 200. The document declares a bearer scheme and no security requirement, ready for task 52. Prose comes from XML doc comments, with `/health` as the worked example. Choice of Swagger UI over Scalar is in ADR-0029. **The "all endpoints documented" criterion is only partly satisfiable today** — the API has `/health` plus the test-only endpoints from ADR-0022 and ADR-0025. This establishes the convention feature endpoints will follow rather than inventing endpoints to document. Two things this imposes on everything that follows: - **`PlaceMark.Contracts` now sets `GenerateDocumentationFile`, so an undocumented public member there is a build error.** Anyone adding wire types must give them doc comments. - **`PlaceMark.Api.csproj` carries a direct `Microsoft.OpenApi` 2.11.0 reference** solely to escape GHSA-v5pm-xwqc-g5wc in the 2.0.0 that `Microsoft.AspNetCore.OpenApi` 10.0.10 resolves. It is a pin that holds the library back — delete it when a framework release resolves a patched version. One unrelated fix was needed to build with documentation files on: an ambiguous `cref` in `RequestValidator.cs`. `docs/adr/README.md` will conflict with the PRs in flight — keep the rows in numeric order.
Describe the API with OpenAPI, and read it with Swagger UI
All checks were successful
CI / build (pull_request) Successful in 54s
ed31432e12
The document is generated by Microsoft.AspNetCore.OpenApi and served at
/openapi/v1.json, with Swagger UI over it at /swagger. Both are registered
in every environment except Production, through one condition asked twice —
once for services, once for routes — so the two cannot drift into a document
nothing serves or a route with nothing behind it. Started under Production,
the application answers 404 for the document, the page and the UI's assets
while /health still answers 200.

The document declares a bearer scheme and no security requirement, on the
document or on any operation, because nothing is protected yet: a requirement
is what a generated client reads to decide it must obtain a token, and a false
one is not harmless. The scheme is what puts the UI's Authorize control on the
page — with the transformer removed and nothing else changed, the document
carries no securitySchemes and the rendered page carries no button.

Endpoint prose comes from XML doc comments, which is what fixes the convention
the feature endpoints will follow: a named handler that is at least internal, a
tag on the group, implementation notes outside the doc comment because <remarks>
is published as the operation's description, and request and response types
documented in PlaceMark.Contracts. Both projects generate a documentation file,
which the generator requires and which nothing reports the absence of. The
acceptance criterion about documenting every endpoint is only partly satisfiable
today — the API has one endpoint and no contracts — so what lands here is the
mechanism, one worked example and tests that fail when either stops working.

Microsoft.OpenApi is referenced only to raise it above the 2.0.0 that the
framework package resolves, which carries GHSA-v5pm-xwqc-g5wc and so fails the
restore under NU1903-as-error. Delete that line when a framework release
resolves a patched version by itself.

ADR-0029 records the choice of UI over Scalar, and the measurement behind each
claim above.
Author
Owner

BearerSecuritySchemeTransformer.SchemeName is used both as the securitySchemes dictionary key and as Scheme (the HTTP auth scheme, RFC 6750) — renaming the key to anything else would silently emit an invalid scheme and make the UI send Authorization: <newkey> <token>; split into two constants.

PlaceMark.Api.xml and PlaceMark.Contracts.xml land in the publish output but are read only at build time — deleting both from a published Release output leaves the summaries and schema descriptions intact in the served document, so add <PublishDocumentationFile>false</PublishDocumentationFile> beside each GenerateDocumentationFile and keep the internal prose out of the deployed artefact.

Verdict: mergeable

`BearerSecuritySchemeTransformer.SchemeName` is used both as the `securitySchemes` dictionary key and as `Scheme` (the HTTP auth scheme, RFC 6750) — renaming the key to anything else would silently emit an invalid `scheme` and make the UI send `Authorization: <newkey> <token>`; split into two constants. `PlaceMark.Api.xml` and `PlaceMark.Contracts.xml` land in the publish output but are read only at build time — deleting both from a published Release output leaves the summaries and schema descriptions intact in the served document, so add `<PublishDocumentationFile>false</PublishDocumentationFile>` beside each `GenerateDocumentationFile` and keep the internal prose out of the deployed artefact. Verdict: mergeable
Action review on the OpenAPI document
All checks were successful
CI / build (pull_request) Successful in 46s
69d4490f86
The dictionary key and the HTTP authentication scheme were one constant. They
read the same but only one of them is ours: `bearer` in `scheme` is RFC 6750's
registered name, while the key is a name this API picked and a security
requirement under #52 will have to match. Sharing the constant meant renaming
the key silently emitted an authentication scheme no server has heard of, and
the UI would send `Authorization: <whatever-the-key-now-is> <token>`.

Documentation files no longer reach the published output. They are read at
build time and never at run time — the published output with both deleted
serves a document carrying every summary, status code description and schema
description, because the prose is in the assembly by then. The property is the
plural `PublishDocumentationFiles`, on PlaceMark.Api: the singular
`PublishDocumentationFile` drops that project's own file and leaves
PlaceMark.Contracts.xml, which is the one describing types a caller can see.
Published both ways to check. Nothing is needed on PlaceMark.Contracts itself,
which is never published on its own, and the WebUI publish carries no XML from
it either.
Author
Owner

Both actioned in 69d4490 — key and RFC 6750 scheme split into separate constants; documentation files kept out of the publish output by PublishDocumentationFiles (plural) on PlaceMark.Api, since the singular property leaves PlaceMark.Contracts.xml behind.

Both actioned in 69d4490 — key and RFC 6750 scheme split into separate constants; documentation files kept out of the publish output by `PublishDocumentationFiles` (plural) on PlaceMark.Api, since the singular property leaves `PlaceMark.Contracts.xml` behind.
Author
Owner

Paused. The Verdict: mergeable above is against ed31432 and both its findings were actioned in 69d4490, so it is stale by one commit — this needs a confirming look at ed31432..69d4490 before it merges, not a fresh review.

Paused. The `Verdict: mergeable` above is against `ed31432` and both its findings were actioned in `69d4490`, so it is stale by one commit — this needs a confirming look at `ed31432..69d4490` before it merges, not a fresh review.
Author
Owner

Both fixes hold, including the PublishDocumentationFile singular/plural claim. Nothing to action.

Verdict: mergeable

Both fixes hold, including the `PublishDocumentationFile` singular/plural claim. Nothing to action. Verdict: mergeable
rob merged commit 75cf44e8e6 into main 2026-08-03 21:36:43 +00:00
rob referenced this pull request from a commit 2026-08-03 21:36:45 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/PlaceMark!27
No description provided.