Skip to content

fix(openAPI):multifile openAPI specs can be imported - #8946

Open
adwait-bruno wants to merge 2 commits into
usebruno:mainfrom
adwait-bruno:fix/multifile-imports
Open

fix(openAPI):multifile openAPI specs can be imported#8946
adwait-bruno wants to merge 2 commits into
usebruno:mainfrom
adwait-bruno:fix/multifile-imports

Conversation

@adwait-bruno

@adwait-bruno adwait-bruno commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Description

REF:BRU-3806

OpenApi Specs having external references ($ref) to different files can now be resolved and previewed.

Problem

Previously OpenAPI specs having external file references could not be resolved

Fix

Since Bruno runs locally, passing the Swagger URL directly to the renderer meant that external files could not be resolved. This PR introduces the external library apidevtools/json-schema-ref-parser(https://github.com/APIDevTools/json-schema-ref-parser), which resolves external files in the Electron app and sends the resolved spec to the renderer. The renderer then passes it to Swagger, which inlines the external references.

Few Reasons to use an External Library
1)Base Paths Change Per Spec File: An external reference is resolved relative to the file and not root , so for each spec its path has to be tracked
2)Repeated refs needs to be cached so that they are not fetched again and again
3)Visited files need to be tracked so that circular references do not crash the app
4)Updating Pointers after inlining content
5)It covers many edge cases that we would otherwise have to handle ourselves.

Screenshots

| Before | After |
|
image
|
Screenshot 2026-08-12 at 4 30 28 PM
|

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.
  • I've run the claude code review skill locally.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • New Features

    • OpenAPI specifications now resolve local external references, including nested and chained references.
    • Referenced endpoints and schemas are displayed in the API specification viewer while preserving the original source content.
    • Multi-file OpenAPI definitions are supported with improved rendering of referenced paths.
  • Bug Fixes

    • Reduced unresolved-reference errors for specifications using local external files.
  • Tests

    • Added coverage for reference resolution, parsing scenarios, and multi-file OpenAPI rendering.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f90e0a54-7c9b-47e8-8d88-43e36e0f742a

📥 Commits

Reviewing files that changed from the base of the PR and between a8ec7c0 and 015229b.

📒 Files selected for processing (1)
  • packages/bruno-electron/src/utils/apiSpecs.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-electron/src/utils/apiSpecs.js

Walkthrough

Changes

External API specification references

Layer / File(s) Summary
Reference resolution
packages/bruno-electron/src/utils/apiSpecs.js, packages/bruno-electron/package.json, packages/bruno-electron/src/utils/tests/apiSpecs.spec.js
The resolver detects and resolves local $ref files. Tests cover nested, chained, cyclic, partial, and unresolved references.
Specification ingestion
packages/bruno-electron/src/app/apiSpecs.js, packages/bruno-electron/src/app/apiSpecsWatcher.js, packages/bruno-electron/tests/app/apiSpecs.spec.js
API specification loading and file watching retain parsed JSON and attach resolvedJson when external references exist.
State and viewer integration
packages/bruno-app/src/providers/ReduxStore/slices/apiSpec.js, packages/bruno-app/src/components/ApiSpecPanel/*, tests/import/openapi/*
Redux stores resolvedJson. Swagger renders it while the editor retains the original content. End-to-end tests validate multifile OpenAPI rendering.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to 01522

This change adds support for resolving external OpenAPI file references, and no actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant ApiSpecLoader
  participant resolveExternalApiSpecRefs
  participant ReduxStore
  participant SpecViewer
  participant Swagger
  ApiSpecLoader->>resolveExternalApiSpecRefs: Resolve local external $ref files
  resolveExternalApiSpecRefs-->>ApiSpecLoader: Return resolvedJson or null
  ApiSpecLoader->>ReduxStore: Store json and resolvedJson
  ReduxStore->>SpecViewer: Pass content and resolvedSpec
  SpecViewer->>Swagger: Render resolvedSpec when available
Loading

Possibly related PRs

Suggested reviewers: bijin-bruno

Poem

$ref files join the flow,
Resolved paths now clearly show.
Original text remains in view,
Swagger renders the resolved view.

🚥 Pre-merge checks | ✅ 5
✅ 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 clearly summarizes the main change: importing multifile OpenAPI specifications with external references.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/import/openapi/api-spec-panel-validation.spec.ts (1)

72-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move preview locators into the OpenAPI page module.

Expose locators for the rendered endpoint and resolver error through tests/utils/page/openapi/*. Keep this spec limited to actions and assertions.

As per path instructions: “Centralize locators and actions in page modules under tests/utils/page/*; do not inline raw selectors in specs.”

🤖 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 `@tests/import/openapi/api-spec-panel-validation.spec.ts` around lines 72 - 73,
Move the “Hello endpoint” and “Could not resolve reference” locators into the
OpenAPI page module under tests/utils/page/openapi, exposing reusable locator
properties or methods for the rendered endpoint and resolver error. Update the
spec to use those page-module locators while keeping it limited to actions and
assertions, with no inline text selectors.

Source: Path instructions

🤖 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 `@packages/bruno-electron/src/app/apiSpecsWatcher.js`:
- Line 33: Update the API specification watcher at
packages/bruno-electron/src/app/apiSpecsWatcher.js:33-33 to collect external
$ref dependency paths while adding the root specification and register those
dependencies with the watcher. At
packages/bruno-electron/src/app/apiSpecsWatcher.js:55-55, map changes to
dependency files back to their owning root specification, re-resolve it, and
publish the refreshed resolvedJson. Add watcher coverage that edits a referenced
file and verifies the root update contains the new resolved operation.

In `@tests/import/openapi/api-spec-panel-validation.spec.ts`:
- Around line 67-68: Update the OpenAPI external-$ref E2E test around
openApiSpecFromDialog to use the shared Playwright fixtures and create both the
root specification and referenced file inside a test-owned temporary directory.
Ensure the temporary directory is removed during cleanup, and pass the generated
root specification path instead of the repository fixture path to prevent
parallel-run collisions.

---

Nitpick comments:
In `@tests/import/openapi/api-spec-panel-validation.spec.ts`:
- Around line 72-73: Move the “Hello endpoint” and “Could not resolve reference”
locators into the OpenAPI page module under tests/utils/page/openapi, exposing
reusable locator properties or methods for the rendered endpoint and resolver
error. Update the spec to use those page-module locators while keeping it
limited to actions and assertions, with no inline text selectors.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2178052a-d227-48c6-9034-02481021ed0b

📥 Commits

Reviewing files that changed from the base of the PR and between 9af54a4 and a8ec7c0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • packages/bruno-app/src/components/ApiSpecPanel/SpecViewer.js
  • packages/bruno-app/src/components/ApiSpecPanel/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/apiSpec.js
  • packages/bruno-electron/package.json
  • packages/bruno-electron/src/app/apiSpecs.js
  • packages/bruno-electron/src/app/apiSpecsWatcher.js
  • packages/bruno-electron/src/utils/apiSpecs.js
  • packages/bruno-electron/src/utils/tests/apiSpecs.spec.js
  • packages/bruno-electron/tests/app/apiSpecs.spec.js
  • tests/import/openapi/api-spec-panel-validation.spec.ts
  • tests/import/openapi/fixtures/openapi-multifile-endpoint.yaml
  • tests/import/openapi/fixtures/openapi-multifile.yaml

file.filename = basename;
file.pathname = pathname;
file.json = apiSpecContent;
file.resolvedJson = await resolveExternalApiSpecRefs(apiSpecContent, pathname);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Watch external $ref dependencies.

The watcher observes the root API specification only. If endpoint.yaml changes, it does not trigger a new resolved document. The preview then remains stale until the root specification changes.

  • packages/bruno-electron/src/app/apiSpecsWatcher.js#L33-L33: collect and register the external reference dependencies when the root specification is added.
  • packages/bruno-electron/src/app/apiSpecsWatcher.js#L55-L55: map dependency-file changes back to the root specification and refresh its resolvedJson.

Add watcher coverage that edits a referenced file and verifies that the root API-spec update contains the new resolved operation.

📍 Affects 1 file
  • packages/bruno-electron/src/app/apiSpecsWatcher.js#L33-L33 (this comment)
  • packages/bruno-electron/src/app/apiSpecsWatcher.js#L55-L55
🤖 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 `@packages/bruno-electron/src/app/apiSpecsWatcher.js` at line 33, Update the
API specification watcher at
packages/bruno-electron/src/app/apiSpecsWatcher.js:33-33 to collect external
$ref dependency paths while adding the root specification and register those
dependencies with the watcher. At
packages/bruno-electron/src/app/apiSpecsWatcher.js:55-55, map changes to
dependency files back to their owning root specification, re-resolve it, and
publish the refreshed resolvedJson. Add watcher coverage that edits a referenced
file and verifies the root update contains the new resolved operation.

Comment on lines +67 to +68
const openApiFile = path.resolve(__dirname, 'fixtures', 'openapi-multifile.yaml');
await openApiSpecFromDialog(page, electronApp, openApiFile);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use a test-owned temporary fixture directory.

Create the root specification and referenced file in an isolated temporary directory. Remove the directory during cleanup. This prevents fixture-path and persisted-spec collisions across parallel Electron test runs.

As per path instructions: “For the new OpenAPI external-$ref E2E test, use the shared Playwright fixtures and keep the test isolated with its own temporary directory and fixture files.”

🤖 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 `@tests/import/openapi/api-spec-panel-validation.spec.ts` around lines 67 - 68,
Update the OpenAPI external-$ref E2E test around openApiSpecFromDialog to use
the shared Playwright fixtures and create both the root specification and
referenced file inside a test-owned temporary directory. Ensure the temporary
directory is removed during cleanup, and pass the generated root specification
path instead of the repository fixture path to prevent parallel-run collisions.

Source: Path instructions

};

const resolveExternalApiSpecRefs = async (json, apiSpecPath) => {
if (!containsExternalFileRef(json, path.dirname(apiSpecPath))) return null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

containsExternalFileRef here and new $RefParser() on the next line are outside the try below. The recursion can throw a RangeError on a deeply nested spec, and the constructor throws if the dep didn't install. Either one escapes this function, the watcher catch eats it, and the apispec-tree-updated send never runs, so a spec that opened fine before stops opening. Move the try up to cover both lines and return null on failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants