Skip to content

fix: handle circular OpenAPI schemas during conversion#454

Merged
bzp2010 merged 4 commits into
mainfrom
fix-openapi-circular-453
Jun 16, 2026
Merged

fix: handle circular OpenAPI schemas during conversion#454
bzp2010 merged 4 commits into
mainfrom
fix-openapi-circular-453

Conversation

@jarvis9443

@jarvis9443 jarvis9443 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #453.

adc convert openapi only needs routing metadata, but the converter was dereferencing the whole OpenAPI document before conversion. For OpenAPI 3.0 documents with circular component schemas, that can make unrelated schema graphs fail before route conversion.

This changes the converter to upgrade the raw spec first, build a conversion-only document containing info, servers, paths, reusable path items, and x-adc-* fields, then dereference that smaller document. Request and response schema trees are left out because ADC config output does not consume them.

Route, service, upstream, label, and plugin metadata conversion behavior is preserved. A regression fixture covers the self-referencing schema from the issue.

Summary by CodeRabbit

Release Notes

  • Improvements

    • Enhanced OpenAPI conversion by pruning irrelevant or unsupported parts of the spec before dereferencing, improving reliability for complex documents.
    • Improved route generation by inlining each service’s path prefix directly into route URIs.
    • Strengthened schema parsing and dereferencing to produce more consistent output.
  • Tests

    • Added a new OpenAPI fixture and Jest coverage for recursive/self-referencing component schemas (e.g., nested children structures).

@jarvis9443
jarvis9443 requested a review from bzp2010 as a code owner June 11, 2026 08:33
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8628c954-6471-460c-a94c-dc836a1c77d9

📥 Commits

Reviewing files that changed from the base of the PR and between 6736c6b and 68dcb8c.

📒 Files selected for processing (1)
  • libs/converter-openapi/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/converter-openapi/src/index.ts

📝 Walkthrough

Walkthrough

This PR fixes a stack overflow bug that occurred when converting OpenAPI 3.0.2 documents with self-referencing component schemas. The fix introduces document pruning helpers, restructures the parseOAS pipeline to apply pruning before dereferencing, updates route handling to inline path prefixes, and adds a test case validating the fix works with recursive schema structures.

Changes

OpenAPI Self-Referencing Schema Stack Overflow Fix

Layer / File(s) Summary
Document pruning helpers
libs/converter-openapi/src/index.ts
New pruneConversionDocument function and type utilities mutate an upgraded OpenAPI specification by unsetting top-level keys (tags, externalDocs, security, webhooks), removing selected components schema fields, and stripping parameters and operation-level fields from path items. RxJS defer import added to support the new async pipeline.
parseOAS pipeline refactor
libs/converter-openapi/src/index.ts
Restructured parseOAS to call upgrade(content) first via defer, validate the upgraded specification exists, wrap it through pruneConversionDocument for pruning, then dereference the pruned result. Dereference mapping now returns res.schema cast as OpenAPIV3_1.Document.
Route typing and path prefix inlining
libs/converter-openapi/src/index.ts
Explicitly type mapped route as ADCSDK.Route and inline service.path_prefix into each route.uris entry, removing path_prefix from the service object.
Test case with self-referencing schema
libs/converter-openapi/test/assets/basic-8.yaml, libs/converter-openapi/test/basic.spec.ts
Added OpenAPI 3.0.2 test asset containing self-referencing SectorNode component schema (the reproduction case from issue #453), and Jest test case validating successful conversion to service with GET route and upstream configuration.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning Test is unit-level only, not E2E. It tests converter logic in isolation without covering real CLI conversion path mentioned in PR as where regression is covered. Add E2E test that executes adc convert openapi CLI with basic-8.yaml to verify actual CLI path functions. Current test only verifies internal converter.toADC() succeeds, not CLI integration.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: handle circular OpenAPI schemas during conversion' directly describes the main change in the pull request, which adds pruning to avoid dereferencing unnecessary OpenAPI schema graphs and prevents RangeError failures.
Linked Issues check ✅ Passed The pull request successfully addresses all objectives from issue #453: it enables conversion of OpenAPI documents with circular schemas by pruning unnecessary schema fields before dereferencing, and includes a regression test covering the self-referencing SectorNode scenario.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing circular schema handling during OpenAPI conversion: pruning logic in the main converter, a test asset demonstrating the circular schema scenario, and a corresponding test case.
Security Check ✅ Passed PR introduces OpenAPI converter pruning to handle circular schemas. Analysis found: (1) console.log statements only expose non-sensitive HTTP method/path metadata; (2) error messages show validatio...

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-openapi-circular-453

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/converter-openapi/src/index.ts`:
- Around line 241-246: The parseOAS implementation eagerly calls
upgrade(content) inside of(...), causing synchronous throws; change it to a lazy
Observable so upgrade(content) runs only on subscription (use RxJS defer or a
deferred factory returning the upgrade result) so errors become Observable
errors; update the pipeline starting at the current
of(upgrade(content).specification) to a deferred Observable that invokes
upgrade(content).specification when subscribed, keeping the downstream
map(dereference(...)) and error path intact (refer to parseOAS and the toADC
call sites to ensure behavior stays consistent).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 01f7a933-781c-4a95-85c3-ed206230ec3f

📥 Commits

Reviewing files that changed from the base of the PR and between d6ab29a and e311dd2.

📒 Files selected for processing (3)
  • libs/converter-openapi/src/index.ts
  • libs/converter-openapi/test/assets/basic-8.yaml
  • libs/converter-openapi/test/basic.spec.ts

Comment thread libs/converter-openapi/src/index.ts Outdated
@jarvis9443

Copy link
Copy Markdown
Contributor Author

The actionable inline issue from CodeRabbit was fixed in 4294efb and the thread is resolved. I am not adding an API-server E2E for this case because adc convert openapi is a local converter path; the regression is covered by the converter fixture and the CLI conversion path.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
libs/converter-openapi/src/index.ts (1)

187-199: 💤 Low value

Consider using forEach instead of map for mutation-only side effects.

The outer services.map() creates and discards an array since only the in-place mutations matter. Using forEach would be more semantically appropriate for side-effect-only iteration.

♻️ Suggested refactor
          tap((services) =>
-           services.map((service) => {
+           services.forEach((service) => {
              if (!service.path_prefix) return service;
              service.routes = service.routes!.map((route: ADCSDK.Route) => {
                route.uris = route.uris.map(
                  (uri: string) => `${service.path_prefix}${uri}`,
                );
                return route;
              });
              unset(service, 'path_prefix');
-             return service;
            }),
          ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/converter-openapi/src/index.ts` around lines 187 - 199, Replace the
outer services.map() with services.forEach() since the callback only performs
mutations (modifying route uris and unsetting path_prefix) and doesn't need to
return the array. Update the conditional return statement from return service to
just return when path_prefix doesn't exist, and remove the final return service
statement at the end of the forEach callback since forEach doesn't use return
values for iteration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@libs/converter-openapi/src/index.ts`:
- Around line 187-199: Replace the outer services.map() with services.forEach()
since the callback only performs mutations (modifying route uris and unsetting
path_prefix) and doesn't need to return the array. Update the conditional return
statement from return service to just return when path_prefix doesn't exist, and
remove the final return service statement at the end of the forEach callback
since forEach doesn't use return values for iteration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 78e9429d-e964-41d7-8eca-693547f27ccf

📥 Commits

Reviewing files that changed from the base of the PR and between e311dd2 and 6736c6b.

📒 Files selected for processing (1)
  • libs/converter-openapi/src/index.ts

@bzp2010
bzp2010 merged commit eb0d6b0 into main Jun 16, 2026
29 checks passed
@bzp2010
bzp2010 deleted the fix-openapi-circular-453 branch June 16, 2026 05:58
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.

Bug: Maximum call stack size exceeded when converting OpenAPI 3.0.2 with self-referencing schema

2 participants