Skip to content

Bundling: align operationRef handling with spec §4.6 and stop emitting absolute filesystem paths #363

Description

@char0n

Summary

When bundling, the OpenAPI Link Object operationRef is rewritten to an absolute URI. For a document loaded from the local filesystem this produces an absolute filesystem path in the output (e.g. /home/alice/project/ops.json#/paths/~1things/get), which leaks a machine-specific path and is not transferable. This diverges from how the reference implementations treat operationRef, and it's worth revisiting against the OpenAPI spec's two distinct relative-reference rules.

Current behavior

packages/apidom-reference/src/bundle/strategies/openapi-3-1/visitor.ts (and the identical logic in openapi-3-0):

// rewrite to an absolute URI so the link keeps resolving once the document
// is treated as self-contained
const absoluteURI = url.resolve(this.reference.uri, operationRef);
if (absoluteURI !== operationRef) {
  linkElement.set('operationRef', absoluteURI);
}

An external operationRef therefore becomes:

  • entry doc is an HTTP(S) URL → absolute URL (portable, fine)
  • entry doc is a local file → absolute filesystem path (leaks /home/..., not transferable)

operationRef points at an Operation Object, which has no Components bucket in 3.0/3.1, so it cannot be hoisted/internalized like a Reference Object or Schema.

Spec context (OpenAPI 3.1)

The spec defines two relative-reference rules with different bases:

  • §4.6 Relative References in URIs — governs $ref and operationRef: "resolved using the referring document as the Base URI" (nearest parent $id for Schemas).
  • §4.7 Relative References in URLs — governs externalDocs.url, license.url, contact.url, termsOfService, Server.url: "resolved using the URLs defined in the Server Object as a Base URL" — NOT the document base.

So operationRef re-basing against the document URI is spec-legal (§4.6). But the §4.7 URL fields must not be resolved against the document base — and ApiDOM correctly leaves all of them untouched today (verified: no license/externalDocs/contact/server.url handling in bundle or dereference). This issue is only about operationRef.

Prior art

  • libopenapi (datamodel/low/v3/link.go): OperationRef is low.NodeReference[string], treated as an opaque stringBuild() performs no $ref/index resolution on it, Hash() writes it verbatim. It is never resolved or rewritten. Likewise externalDocs.url (external_doc.go) is opaque.
  • Redocly: hoists/rewrites $ref; URL references are resolved/inlined by default with --keep-url-references to preserve. No path-leaking absolute-filesystem rewrite.
  • Neither reference implementation emits an absolute filesystem path into a reference value.

Proposal (decide later)

Revisit operationRef bundling across the openapi-2, openapi-3-0, and openapi-3-1 strategies. Options:

  1. Leave it opaque (match libopenapi) — simplest; correct for an operationRef authored in the entry document, since its §4.6 base (the document) is unchanged.
  2. Re-base only when needed — leave entry-document operationRef as-is; only re-base one carried in from a non-entry external file (where the §4.6 base changes), and even then prefer an absolute URL, never an absolute filesystem path.
  3. At minimum: never emit an absolute filesystem path — for a file-sourced entry doc, keep the relative form rather than leak /home/....

Also: while here, confirm/keep the §4.7 URL fields (license.url, externalDocs.url, contact.url, termsOfService, server.url) untouched, and document that contract.

Acceptance for now

The current absolute-URI behavior is accepted as-is for the OpenAPI 3.1 bundle strategy (PR #361) — it keeps the link resolvable on the originating machine and matches the existing openapi-2 / openapi-3-0 behavior. This issue tracks the cross-version cleanup so it's decided deliberately rather than diverging one strategy mid-stream.

Surfaced during review of #361.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions