Skip to content

Add support for cross-file $ref resolution - #502

Open
mathecruz wants to merge 1 commit into
Apitomy:mainfrom
mathecruz:issues/499
Open

mathecruz wants to merge 1 commit into
Apitomy:mainfrom
mathecruz:issues/499

Conversation

@mathecruz

@mathecruz mathecruz commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds support for resolving $refs that point at other local files (e.g. ./common-spec.yaml#/components/schemas/Pet), not just same-document refs.

Previously every consumer (e.g. quarkus-openapi-generator) had to pre-flatten multi-file specs themselves before handing a single JSON document to this library.

Closes #499

Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
*
* Remote (http(s)://) refs are out of scope and are left unresolved.
*
* @author eric.wittmann@gmail.com

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.

Why are you giving me credit for this? 🤣

@EricWittmann EricWittmann 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.

Thanks for this. The approach is sound: a new FileReferenceResolver is plugged into Library.dereferenceDocument after the document is converted to 3.1 and before pre-processing. The change is small, and nothing happens unless the spec is loaded from a local file. The issues below should be fixed before merging.

Must fix

  1. The generated META-INF/openapi.json still contains the external $ref. The expected output shows "$ref": "./multi-file-common.json#/components/schemas/Pet" copied unchanged into the generated project. multi-file-common.json isn't in that project, so anything that serves this file at runtime (e.g. MicroProfile OpenAPI / Swagger UI) will hit a broken reference. Either write out the dereferenced document, or state this limitation explicitly and add a test for it.

  2. openApiDocBaseDir can be left over from an earlier load. Only setOpenApiDocument(URL) sets it. If the same OpenApi2JaxRs instance is later given a String or InputStream, the old directory is reused and refs resolve against the wrong place. Reset it to null in those methods, or add an explicit setBaseDir(Path) so String/InputStream callers such as quarkus-openapi-generator can also use this feature. As written, the main use case from #499 only works through the URL path.

  3. Load failures are silently ignored. loadDocument catches IOException | RuntimeException and returns null. A missing file, a YAML typo or a non-object root (the ObjectNode cast) all end up as an unresolved ref, and the user gets confusing generated code later instead of an error. At minimum log a warning with the path and the cause; failing fast is probably better.

Should fix

  1. Tests only cover the simplest case. The Javadoc says it supports YAML, refs that chain across files and directories, files that contain only a fragment (no openapi header), and whole-file refs. The single test is one JSON file pointing at a sibling JSON file. Please add tests for:

    • a YAML external file
    • a nested relative ref from a subdirectory (this is what the base-dir tagging exists for)
    • a fragment-only file
    • a ref to a file that doesn't exist
    • two files that reference each other (the cache should prevent infinite recursion; confirm dereferenceDocument handles it)
  2. Base-dir tagging may not survive the import. Nested refs depend on node attributes surviving when the content is imported or copied into the main document. The Javadoc says they do, but nothing tests it. If the library copies nodes rather than re-parenting them, the tags are lost and nested refs quietly fall back to the root directory. This needs a test (see item 4).

  3. Refs can reach any file on disk. A ref like ../../../../etc/passwd is read without restriction. That's low risk for local codegen, but it matters if this ever runs on user-supplied specs on a server. Consider limiting refs to stay under rootBaseDir, or document the behaviour.

Nits

  • In FileReferenceResolver, @author eric.wittmann@gmail.com looks copied from another file; it should probably be you.
  • The isRemote regex is compiled on every call; make it a static final Pattern.
  • The Javadoc on dereferenceExternalRefs has an empty @param document, and resolveBaseDir has a trailing blank Javadoc line.
  • The test fixtures mix versions (entry file is 3.0.2, common file is 3.1.0). That's fine and arguably a good test, but a short comment saying it's deliberate would help.

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.

Support OpenAPI specs split across multiple files (cross-file $ref resolution)

2 participants