Skip to content

feat: add OpenAPI Overlays support#2059

Merged
pmcelhaney merged 11 commits into
mainfrom
copilot/add-openapi-overlays-support
May 22, 2026
Merged

feat: add OpenAPI Overlays support#2059
pmcelhaney merged 11 commits into
mainfrom
copilot/add-openapi-overlays-support

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 11, 2026

Summary

Adds support for OpenAPI Overlays v1.0.0 — a mechanism to apply targeted patches to an OpenAPI document without modifying the original file.

New CLI flag (repeatable, applied in order):

counterfact openapi.yaml ./out --overlay base.yaml --overlay env-override.yaml

Overlay file format (my-overlay.yaml):

overlay: 1.0.0
info:
  title: My Overlay
  version: 1.0.0
actions:
  - target: $.info
    update:
      description: Injected by overlay
  - target: $.paths['/internal']
    remove: true

Per-spec overlays in counterfact.yaml:

spec:
  - source: openapi.yaml
    group: ""
    overlays:
      - base-overlay.yaml
      - env-overlay.yaml

Overlays are applied in the code-generation pipeline (Specification.fromFile), the runtime server pipeline (OpenApiDocument.load), and the /counterfact/openapi endpoint (the raw spec served to Swagger UI). deepMerge is guarded against prototype pollution.

Original Prompt

Add support for OpenAPI Overlays v1.0.0 — a mechanism to apply targeted patches to an OpenAPI document without modifying the original file. Expose a repeatable --overlay <path> CLI flag and support per-spec overlays entries in counterfact.yaml.

Manual acceptance tests

  • counterfact openapi.yaml ./out --overlay overlay.yaml applies the overlay's update action (e.g. changes info.title) before generating route stubs
  • An overlay remove action suppresses route stub generation for the removed path
  • Overlays specified under overlays: in a counterfact.yaml spec entry are applied on startup
  • Repeating --overlay multiple times applies each file in the order given
  • A missing overlay file exits with a clear error message rather than a silent crash
  • GET /counterfact/openapi returns the overlay-patched spec (e.g. updated info.title and removed paths are absent)

Tasks

  • Added src/util/apply-overlay.ts: loadOverlay, applyOverlayActions (JSONPath via jsonpath-plus), applyOverlays; deepMerge with __proto__/constructor/prototype guard
  • Extended Specification.fromFile()/load() and OpenApiDocument constructor to accept overlays?: readonly string[]
  • Propagated overlays through Config, SpecConfig, ApiRunner, CodeGenerator, and loadOpenApiDocument
  • Added repeatable --overlay <path> Commander option to run.ts; per-spec overlays in multi-spec config entries; single-spec CLI flag falls back to config.overlays
  • Fixed openapiMiddleware to apply overlays after bundle() so that GET /counterfact/openapi (Swagger UI source) reflects overlay changes; added overlays?: readonly string[] to OpenApiDocumentConfig
  • Exposed public readonly overlays on ApiRunner; create-koa-app.ts passes runner.overlays to openapiMiddleware
  • Fixed Windows test failure: replaced new URL(...).pathname with fileURLToPath(new URL(..., import.meta.url)) in test/util/apply-overlay.test.ts to avoid doubled drive-letter paths (D:\D:\a\...) on Windows
  • Added 20 unit tests in test/util/apply-overlay.test.ts, including fixture-based integration tests
  • Added three tests in test/server/web-server/openapi-middleware.test.ts locking in overlay behavior at the /counterfact/openapi endpoint (update visible, remove suppresses path, no-overlay case unchanged)
  • Added three example overlay fixtures under test/fixtures/openapi/overlays/:
    • update-info.yaml — patches the API title, description, and contact block
    • remove-deprecated.yaml — removes the deprecated /legacy/items endpoint
    • add-extensions.yaml — adds x-internal: true to targeted operations
  • Added two black box tests in test-black-box/test_counterfact.py:
    • test_overlay_removes_path_from_generated_routes — runs --generate --overlay with a remove action and asserts the removed path produces no route file
    • test_overlay_update_changes_served_response — starts a live server with --serve --generate --overlay and verifies the overlay-modified example value is returned at runtime
  • Updated docs/reference.md with full Overlays section and CLI flag entry
  • Added minor changeset
  • Merged latest origin/main; resolved yarn.lock conflict (preserved jsonify and jsonpath-plus entries)

Copilot AI linked an issue May 11, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add support for OpenAPI overlays feat: add OpenAPI Overlays support May 11, 2026
Copilot AI requested a review from pmcelhaney May 11, 2026 22:28
Copy link
Copy Markdown
Collaborator

@pmcelhaney pmcelhaney left a comment

Choose a reason for hiding this comment

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

Add some example overlays to fixtures.

Copy link
Copy Markdown
Collaborator

@pmcelhaney pmcelhaney left a comment

Choose a reason for hiding this comment

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

Add a black box test

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 18, 2026

Add some example overlays to fixtures.

Done in 5532344. Added three overlay fixtures under test/fixtures/openapi/overlays/:

  • update-info.yaml – patches the API title, description, and adds a contact block
  • remove-deprecated.yaml – removes the deprecated /legacy/items path
  • add-extensions.yaml – adds x-internal: true to the /count and /ping operations

Each fixture is exercised by a new test in test/util/apply-overlay.test.ts that loads it against example.yaml via Specification.fromFile().

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 18, 2026

Add a black box test

Done in 0ca5b36. Added two black box tests to test-black-box/test_counterfact.py:

  • test_overlay_removes_path_from_generated_routes — runs counterfact --generate --overlay remove-beta.yaml against a spec with /alpha and /beta; asserts routes/alpha.ts is generated and routes/beta.ts is absent (removed by the overlay).
  • test_overlay_update_changes_served_response — starts a live server with --serve --generate --overlay change-greeting.yaml; the overlay updates the /greeting example from hello to hello-from-overlay, and the test asserts GET /greeting returns hello-from-overlay.

@pmcelhaney
Copy link
Copy Markdown
Collaborator

@copilot resolve the merge conflicts in this pull request

Co-authored-by: pmcelhaney <51504+pmcelhaney@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 22, 2026

@copilot resolve the merge conflicts in this pull request

Done in 7ac5b26. The only conflict was in yarn.lock — the jsonify and jsonpath-plus entries added by this branch weren't in main. Kept both and merged cleanly.

@pmcelhaney pmcelhaney marked this pull request as ready for review May 22, 2026 21:14
@pmcelhaney
Copy link
Copy Markdown
Collaborator

@copilot http://localhost/counterfact/openapi does not show the overlay changes applied. Make sure the overlay changes are applied everywhere. Add tests to lock in the changes.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 22, 2026

@copilot http://localhost/counterfact/openapi does not show the overlay changes applied. Make sure the overlay changes are applied everywhere. Add tests to lock in the changes.

Fixed in 44b2547. The root cause was that openapiMiddleware called bundle() directly on the raw spec file without applying overlays.

Changes:

  • openapiMiddleware now accepts overlays?: readonly string[] in its config and calls applyOverlays() on the bundled document before serving it — so /counterfact/openapi (and thus the Swagger UI) now reflects all overlay changes
  • ApiRunner exposes a new public readonly overlays property
  • create-koa-app.ts passes runner.overlays to openapiMiddleware
  • Added 3 tests to test/server/web-server/openapi-middleware.test.ts: overlay update action is visible in served document, overlay remove action removes the path, and no-overlay case is unchanged

Copilot AI requested a review from pmcelhaney May 22, 2026 21:31
@pmcelhaney pmcelhaney enabled auto-merge May 22, 2026 21:43
@pmcelhaney
Copy link
Copy Markdown
Collaborator

@copilot the unit tests are failing on Windows

auto-merge was automatically disabled May 22, 2026 22:05

Head branch was pushed to by a user without write access

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 22, 2026

@copilot the unit tests are failing on Windows

Fixed in 2e8bd27. The root cause was new URL(...).pathname returning /D:/a/... on Windows (a POSIX-style path with a leading slash). When passed to the ref-parser, it prepended the drive root again, producing D:\D:\a\....

Changed to fileURLToPath(new URL(..., import.meta.url)) in test/util/apply-overlay.test.ts, which returns the correct native OS path on all platforms.

Copilot AI requested a review from pmcelhaney May 22, 2026 22:06
@pmcelhaney pmcelhaney enabled auto-merge May 22, 2026 22:08
@pmcelhaney pmcelhaney added this pull request to the merge queue May 22, 2026
Merged via the queue into main with commit 8a2d6bd May 22, 2026
8 of 11 checks passed
@pmcelhaney pmcelhaney deleted the copilot/add-openapi-overlays-support branch May 22, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Overlays

2 participants