Skip to content

Nested external $ref resolved against the main openapi.yml directory instead of the referring file #378

Description

@joseegman-idoneea

Summary

When an OpenAPI contract is split across multiple files, a $ref that lives inside an externally-referenced file is resolved against the main openapi.yml directory instead of against the directory of the file that actually contains the $ref. As a result, nested cross-file references cannot be found and generation fails with FileNotFoundException.

Environment

  • Plugin: com.sngular:scs-multiapi-maven-plugin (reproduced on the 6.3.x/6.4.0 engine code)
  • OpenAPI: 3.0/3.1, modular multi-file layout with external $ref

Reproduction

Layout:

api/rest/openapi.yml
api/rest/components/schemas/Service.yml
api/rest/components/schemas/ServiceType.yml

openapi.yml references components/schemas/Service.yml, and inside Service.yml:

service_type:
  $ref: './ServiceType.yml'

Expected resolution: api/rest/components/schemas/ServiceType.yml (relative to Service.yml).
Actual: the plugin looks in the wrong directory (the rest/ segment is dropped / it resolves relative to the main file) and throws:

com.sngular.api.generator.plugin.openapi.exception.FileParseException: Failure trying to parse file Error reading api file
    at com.sngular.api.generator.plugin.common.tools.SchemaUtil.readFile (SchemaUtil.java:98)
    at com.sngular.api.generator.plugin.common.tools.SchemaUtil.getPojoFromRef (SchemaUtil.java:52)
    at com.sngular.api.generator.plugin.common.tools.SchemaUtil.solveRef (SchemaUtil.java:37)
    at com.sngular.api.generator.plugin.openapi.utils.MapperPathUtil.getRefSchema (MapperPathUtil.java:428)
    ...
Caused by: java.io.FileNotFoundException: .../api/components/schemas/ServiceType.yml (No such file or directory)

Root cause

The base URI used to resolve a nested ref is always derived from the main spec file, not from the referring file. Both resolution sites hard-code the main file's parent directory:

  • MapperPathUtil.getRefSchema(...) — every branch passes baseDir.resolve(specFile.getFilePath()).getParent().toUri() to SchemaUtil.solveRef.
  • ModelBuilder.solveRef(...) — same expression baseDir.resolve(specFile.getFilePath()).getParent().toUri().

Because a resolved JsonNode carries no provenance (the directory it was loaded from), any relative $ref it contains is later resolved against the root spec directory.

Suggested fix

Resolve a nested $ref relative to the referring file's directory. A localized, general approach is to rewrite relative external $ref values to absolute paths at file-load time in SchemaUtil.getPojoFromRef(...) (using the loaded file's own directory), so later resolution against any base still points at the correct file. This fixes both call sites at once.

Notes

No existing test exercises a nested cross-directory $ref (a $ref inside an external file pointing to another external file); the current multi-file fixtures (testExternalRefsGeneration, testReferenceFile) only use a single external components file. A reproduction fixture will be added with the fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions