oaspec/openapi/external_loader
Support for external $ref values that point at component definitions
in a sibling YAML/JSON file — ./other.yaml#/components/schemas/Foo
style. Walks components.schemas, pulls referenced schemas from the
target file into the main spec, and rewrites the refs to local form.
Supported shapes:
- top-level component schema entries (
components.schemas.Foo: $ref: ...) - ObjectSchema property values (
properties.field: $ref: ...) - ArraySchema item values (
items: $ref: ...) - ObjectSchema additionalProperties values (
additionalProperties: $ref: ...) - composition branches (
oneOf,anyOf,allOfvariant refs) components.parameters.*.schema: $ref: ...(parameter schema only)components.parameters.*.content.*.schema: $ref: ...components.request_bodies.*.content.*.schema: $ref: ...components.responses.*.content.*.schema: $ref: ...- operation-level
parameters[*].schema/parameters[*].content.*.schemaon bothpaths.<path>.parametersandpaths.<path>.<method>.parameters - operation-level
requestBody.content.*.schema - operation-level
responses.<code>.content.*.schema components.headers.*.schemaand everyResponse.headers.*.schema(both undercomponents.responsesand operation-level responses)- refs inside
components.path_items.*(reusable PathItem definitions, walked via the samerewrite_path_itemhelper used at the top level) - refs inside
operation.callbacks.*.entries.*PathItems (recursive) - whole-object external refs for
components.parameters,components.requestBodies,components.responses, andcomponents.pathItems(e.g.,$ref: './other.yaml#/components/parameters/Foo') - one level of alias chaining inside the same external file
(
LegacyX: $ref: "#/components/schemas/X"in the external file). Cross-file chains collapse naturally because parse_file runs the resolver on every loaded file before handing it back, so a target that is itself an external ref is already inline by the time we look it up.
Out of scope (see issue #98 parent):
- alias chains longer than one hop inside the same file
- HTTP/HTTPS URLs
- cyclic external refs (e.g.,
A.yaml→B.yaml→A.yaml) would loop forever; callers are expected to maintain acyclic file graphs.
Name collisions — when an external ref would overwrite an existing local
schema, or when two external refs pull in the same fragment name from
different files — are surfaced as Diagnostic errors rather than
silently dropping one side.
Values
pub fn base_dir_of(path: String) -> option.Option(String)
Helper used by parser.parse_file to compute a spec’s base directory.
Empty-string (current working directory) is returned when the filepath
module can’t extract a parent — callers can then pass Some("") which
resolves relative refs against CWD.
pub fn resolve_external_component_refs(
spec: spec.OpenApiSpec(spec.Unresolved),
base_dir: option.Option(String),
parse_file: fn(String) -> Result(
spec.OpenApiSpec(spec.Unresolved),
diagnostic.Diagnostic,
),
) -> Result(
spec.OpenApiSpec(spec.Unresolved),
diagnostic.Diagnostic,
)
Load every components.schemas entry whose value is an external
filesystem ref, merge the referenced schema into the main spec, and
rewrite the entry to a local ref. base_dir is the directory of the
file this spec was loaded from (used to resolve relative paths).
If base_dir is None (spec loaded from string), external refs are
treated as unresolvable and passed through unchanged — downstream
validation still rejects them.