openapi init source
The team ruleset for OpenAPI documents, run by Spectral. It turns the "OpenAPI" section of conventional/RESTfulAPI.md into a check, so a missing operationId, an unversioned path or a GET without a cache policy is found in the merge request, not by a client.
| File | What it is |
|---|---|
.spectral.yaml | The ruleset. error is a MUST of the standard and fails the job; warn is a SHOULD and only prints |
functions/versionedPath.js | Joins Swagger 2.0's basePath to each path before checking it, because swaggo puts /api/v1 there |
openapi.gitlab-ci.yml | A job for a repository whose aioz-ci-template version does not have validate:openapi yet |
testdata/ | A spec that must pass clean and one that must trip every rule; tests/openapi-ruleset/run.sh runs both |
Run it locally
From the repository root, after make swagger:
npx --yes @stoplight/spectral-cli@6.17.0 lint -r .spectral.yaml doc/swagger.jsonThe VS Code Spectral extension reads the same .spectral.yaml and marks problems as you edit annotations.
Adopting it
Copy
.spectral.yamlandfunctions/to the repository root.Add the CI job:
validate:openapifrom aioz-ci-template when the repository's template version has it, otherwiseopenapi.gitlab-ci.yml. SetOPENAPI_SPECif the spec is notdoc/swagger.json.Annotate every GET with its cache policy, beside its other swaggo annotations:
go// @x-cache-policy {"type":"revalidate"} // @x-cache-policy {"type":"public","max_age":300} // @x-cache-policy {"type":"no-store"}The type MUST match the
Cache(...)the route registers.Fix the errors, or record each one you cannot fix yet in an
overridesblock with the Jira key that will. The job warns until the repository setsallow_failure: false; do that once the error count is zero.
Baseline (2026-10-08)
The ruleset run against the specs in use today, so adoption subtasks know their size:
| Spec | Errors | Warnings | Biggest items |
|---|---|---|---|
aioz-template doc/swagger.json | 0 | 15 | 8 GETs without x-cache-policy, 3 camelCase fields (createdAt) |
aioz-stream docs/swagger.json | 134 | 86 | 89 paths with no version (basePath is /api/), 40 operations without operationId, 5 verb paths |
parser-service doc/swagger.json | 181 | 185 | 163 operations without operationId, 9 without a success response, 4 without an error response, 5 verb paths (/auth/remove-wallet) |
aioz-stream's unversioned paths are a breaking change to fix: they move under /api/v1/ beside the current paths, and the old ones are deprecated for the standard three months.
Where the rule is a heuristic
list-paginated treats a GET on a path that does not end in a {param} as a list. A singleton such as /v1/me or /v1/stats trips it; that is a warning, and an overrides entry with a one-line reason silences it.