Skip to content

feat(page): OpenAPI bridge — ingest OpenAPI 3.x operations into the kit#46

Merged
mhenrixon merged 1 commit into
mainfrom
issue-40-openapi-bridge
Jul 3, 2026
Merged

feat(page): OpenAPI bridge — ingest OpenAPI 3.x operations into the kit#46
mhenrixon merged 1 commit into
mainfrom
issue-40-openapi-bridge

Conversation

@mhenrixon

Copy link
Copy Markdown
Owner

Summary

Closes #40. Point c.openapi at an OpenAPI 3.x spec and render a whole endpoint with one operation "createInvoice" call — zero hand-restatement. This closes the last big restatement source the DX overhaul (#8) deferred until the #14/#15 render targets shipped.

One operation call expands to a full DocsUI::Section:

Spec source Renders as
operationId + summary + method/path Section title + a DocsUI::Endpoint badge
description Markdown prose
parameters (query/path) a DocsUI::FieldTable
requestBody schema ($ref, allOf, nested) a FieldTable (nested names dotted: customer.id)
4xx/5xx responses a DocsUI::ErrorTable (error type from a response example)
x-codeSamples / x-code-samples DocsUI::Example tabs (a lone sample → a plain Code)
no code samples a generated DocsUI::RequestExample
first 2xx example a DocsUI::JsonResponse

Because it's composed entirely from the existing kit, the .md twin, llms.txt, search, and MCP surfaces all derive from it for free — verified on the dogfood page.

Design

  • Stdlib parse into a narrow, gem-owned model (DocsKit::OpenApi::Document + Operation + Schema) — no runtime parser dependency (the gemspec is deliberately minimal). The model exposes only what the render targets consume (field rows, error rows, example bodies, code samples).
  • Rejected: adding an openapi3_parser dependency (every site pays for an opt-in feature; its validation can reject real-world specs), and build-time codegen (regenerated pages drift from the spec — runtime derivation is what makes it zero-restatement).
  • Loud on lookup, graceful on rendering: an unknown operationId raises DocsKit::OpenApi::OperationNotFound naming the available ids; an external/remote $ref raises DocsKit::OpenApi::UnsupportedRef. Missing examples/blocks degrade (no body table, no response block).
  • Local $ref only, resolved cycle-safely and depth-capped; allOf shallow-merged; oneOf/anyOf rendered as a one of: A | B type label.
  • ErrorTable#type is now optional — OpenAPI has no canonical error-type field. A type-less row gets the em-dash; rows passing type: render exactly as before (backwards compatible).

What's new

  • DocsKit::OpenApi.load + the object model (lib/docs_kit/open_api.rb, open_api/{document,operation,schema}.rb).
  • c.openapi config knob (String/Pathname/Hash, default nil → bridge off). #openapi_document memoizes and reloads on the file's mtime change.
  • DocsUI::OpenApiOperation component + the lowercase operation page helper.

Install path & docs

  • The generator initializer documents the commented c.openapi knob; the operation helper is added to the AGENTS.md + write-docs-page skill contracts. docs-kit new inherits both via docs_kit:install.
  • README gains an "OpenAPI bridge" subsection with the spec → component mapping table.
  • Dogfood: docs/openapi.yaml + c.openapi set + a live "OpenAPI bridge" page (/docs/openapi) that documents the bridge and renders three real operations.

Test plan

  • spec/docs_kit/open_api/{document,operation,schema}_spec.rb — loading (YAML/JSON/Hash/Pathname), lookup by id and by verb+path, local $ref/cycle/allOf, parameter/body row derivation (nested dotting, enum labels, array-of), error rows + optional type, example precedence, x-codeSamples (both spellings).
  • spec/docs_ui/open_api_operation_spec.rb — each part renders through the kit; both-tables labelling; x-codeSamples override (1 → Code, ≥2 → Example); clients: passthrough; block passthrough.
  • spec/docs_ui/page_helpers_spec.rb — the operation helper (lookup, clients:, block, raise).
  • spec/docs_kit/configuration_spec.rb — the knob (default, types, memoize, mtime reload, raise-when-unset).
  • spec/docs_ui/error_table_spec.rb — optional type.
  • spec/generators/install_generator_spec.rb — the commented knob lands in the generated initializer.

Verification

  • bundle exec rake (rspec + rubocop) — 620 examples, 0 failures, 95.9% line coverage, RuboCop clean (113 files)
  • Dogfood page renders end-to-end through a real Rails view context; its .md twin derives (FieldTable/ErrorTable/code fences) with no export code
  • Backwards compatible — a site without c.openapi is byte-identical; ErrorTable rows passing type: unchanged
  • No new emitted Tailwind classes (the component composes existing kit pieces; the one h3 uses utilities already in prose.rb/header.rb/shell.rb)

https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX

Point `c.openapi` at an OpenAPI 3.x spec and render a whole endpoint with one
`operation "createInvoice"` call — no hand-restatement. The operation's
method/path/summary become a DocsUI::Endpoint badge; its parameters and
request body become FieldTables; its 4xx/5xx responses an ErrorTable; its
x-codeSamples (or a generated RequestExample) the client tabs; and its first
2xx example a JsonResponse. Because it's composed from the existing kit, the
.md twin, llms.txt, search, and MCP surfaces derive from it for free.

Closes the last big restatement source the DX overhaul (#8) deferred until the
#14/#15 render targets shipped.

## What's new

- `DocsKit::OpenApi.load` → a narrow, gem-owned object model
  (`Document` + `Operation` + `Schema`). Stdlib YAML/JSON — no parser
  dependency. Local $ref resolution (cycle-safe, depth-capped), allOf merge,
  enum/oneOf/anyOf type labels, example synthesis, x-codeSamples (both
  spellings). Loud on lookup (`OperationNotFound` names the ids;
  `UnsupportedRef` on an external $ref), graceful on missing examples/blocks.
- `c.openapi` config knob (String/Pathname/Hash, default nil → bridge off,
  fully backwards compatible). `#openapi_document` memoizes and reloads on the
  file's mtime change.
- `DocsUI::OpenApiOperation` component + the lowercase `operation` page helper.
- `DocsUI::ErrorTable#type` is now optional (OpenAPI has no canonical
  error-type field) — a type-less row gets the em-dash; rows with `type:`
  render exactly as before.

## Install path & docs

- Generator initializer documents the commented `c.openapi` knob; the
  `operation` helper is added to the AGENTS.md + write-docs-page skill
  contracts. `docs-kit new` inherits both via `docs_kit:install`.
- README gains an "OpenAPI bridge" subsection (spec → component mapping table).
- Dogfood: `docs/openapi.yaml` + `c.openapi` + a live "OpenAPI bridge" page.

## Test Coverage

- `spec/docs_kit/open_api/{document,operation,schema}_spec.rb` — the model:
  loading, lookup, $ref/cycle/allOf, row derivation, example precedence,
  code samples.
- `spec/docs_ui/open_api_operation_spec.rb` — the component renders each part
  through the kit; x-codeSamples override; block passthrough.
- `spec/docs_ui/page_helpers_spec.rb` — the `operation` helper.
- `spec/docs_kit/configuration_spec.rb` — the knob (default, types, memoize,
  mtime reload, raise-when-unset).
- `spec/docs_ui/error_table_spec.rb` — optional type.
- `spec/generators/install_generator_spec.rb` — the commented knob lands.

## Verification

- [x] bundle exec rake (rspec + rubocop) — 620 examples, 0 failures; 95.9%
      line coverage; RuboCop clean
- [x] Dogfood page renders end-to-end through a real Rails view context, and
      its .md twin derives (FieldTable/ErrorTable/code fences) with no export
      code
- [x] Backwards compatible — a site without c.openapi is byte-identical

Closes #40

Claude-Session: https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX
@mhenrixon mhenrixon self-assigned this Jul 3, 2026
@mhenrixon mhenrixon added the enhancement New feature or request label Jul 3, 2026
@mhenrixon mhenrixon merged commit 8838a3a into main Jul 3, 2026
4 checks passed
@mhenrixon mhenrixon deleted the issue-40-openapi-bridge branch July 4, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[EPIC] OpenAPI bridge — ingest OpenAPI operations into the Endpoint/RequestExample/FieldTable kit

1 participant