diff --git a/schemas/v1.2-dev/schema.yaml b/schemas/v1.2-dev/schema.yaml index 4891415..cb393c3 100644 --- a/schemas/v1.2-dev/schema.yaml +++ b/schemas/v1.2-dev/schema.yaml @@ -6,11 +6,18 @@ properties: overlay: type: string pattern: ^1\.2\.\d+$ + $self: + type: string + format: uri-reference + $comment: MUST NOT contain a fragment + pattern: "^[^#]*$" info: $ref: "#/$defs/info-object" extends: type: string format: uri-reference + $comment: MUST NOT contain a fragment + pattern: "^[^#]*$" actions: type: array minItems: 1 diff --git a/tests/v1.2-dev/fail/extends-with-fragment.yaml b/tests/v1.2-dev/fail/extends-with-fragment.yaml new file mode 100644 index 0000000..59a6841 --- /dev/null +++ b/tests/v1.2-dev/fail/extends-with-fragment.yaml @@ -0,0 +1,7 @@ +overlay: 1.2.0 +info: + title: Invalid extends with fragment + version: 1.0.0 +extends: https://api.example.com/openapi/petstore.yaml#/paths/~1pets +actions: + - target: '$' diff --git a/tests/v1.2-dev/fail/self-with-fragment.yaml b/tests/v1.2-dev/fail/self-with-fragment.yaml new file mode 100644 index 0000000..c98a981 --- /dev/null +++ b/tests/v1.2-dev/fail/self-with-fragment.yaml @@ -0,0 +1,7 @@ +overlay: 1.2.0 +$self: https://example.com/overlays/petstore.overlay.yaml#fragment +info: + title: Invalid self with fragment + version: 1.0.0 +actions: + - target: '$' diff --git a/tests/v1.2-dev/pass/self-and-extends-identifier.yaml b/tests/v1.2-dev/pass/self-and-extends-identifier.yaml new file mode 100644 index 0000000..862f4ac --- /dev/null +++ b/tests/v1.2-dev/pass/self-and-extends-identifier.yaml @@ -0,0 +1,10 @@ +overlay: 1.2.0 +$self: https://example.com/overlays/petstore.overlay.yaml +info: + title: Overlay with identifier-based extends + version: 1.0.0 +extends: https://api.example.com/openapi/petstore.yaml +actions: + - target: '$.info' + update: + x-overlay-applied: true diff --git a/tests/v1.2-dev/pass/self-with-relative-extends.yaml b/tests/v1.2-dev/pass/self-with-relative-extends.yaml new file mode 100644 index 0000000..f512f58 --- /dev/null +++ b/tests/v1.2-dev/pass/self-with-relative-extends.yaml @@ -0,0 +1,10 @@ +overlay: 1.2.0 +$self: https://example.com/overlays/petstore.overlay.yaml +info: + title: Overlay with relative extends + version: 1.0.0 +extends: ../openapi/petstore.yaml +actions: + - target: '$.info' + update: + x-overlay-applied: true diff --git a/versions/1.2.0-dev.md b/versions/1.2.0-dev.md index 7f771e7..0c529b2 100644 --- a/versions/1.2.0-dev.md +++ b/versions/1.2.0-dev.md @@ -65,9 +65,38 @@ Where Overlay tooling renders rich text it MUST support, at a minimum, markdown While the framing of CommonMark 0.27 as a minimum requirement means that tooling MAY choose to implement extensions on top of it, note that any such extensions are by definition implementation-defined and will not be interoperable. Overlay Description authors SHOULD consider how text using such extensions will be rendered by tools that offer only the minimum support. +### Parsing Documents + +Implementations MUST NOT treat an `extends` value as unresolvable before checking all possible target [[OpenAPI]] Description documents provided to the implementation. + +To ensure portability, when a target [[OpenAPI]] Description defines `$self`, the Overlay's `extends` value SHOULD match the target's `$self` URI. +Implementations MUST examine all available target documents for matching `$self` values before concluding that no document matches the `extends` URI. + ### Relative References in URIs -Unless specified otherwise, all fields that are URI references MAY be relative references as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-4.2). +URIs used in an Overlay document, including `$self` and `extends`, are identifiers, and not necessarily locators (URLs). + +Unless specified otherwise, all fields that are URI references MAY be relative references as defined by [RFC3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2). + +#### Establishing the Base URI + +Relative URI references are resolved using the appropriate base URI, which MUST be determined in accordance with [RFC3986 Section 5.1.1 - 5.1.4](https://tools.ietf.org/html/rfc3986#section-5.1.1). + +The base URI for resolving relative references within an Overlay document is determined as follows: + +- If the [`$self`](#overlay-self) field is present and is an absolute URI, the base URI is the `$self` URI (per [RFC3986 Section 5.1.1](https://tools.ietf.org/html/rfc3986#section-5.1.1): Base URI Embedded in Content). +- If the [`$self`](#overlay-self) field is present and is a relative URI-reference, it MUST first be resolved against the next possible base URI source per [RFC3986 Section 5.1.2 - 5.1.4](https://tools.ietf.org/html/rfc3986#section-5.1.2) before being used as the base URI for resolving other relative references. +- If the [`$self`](#overlay-self) field is not present, the base URI MUST be determined from the next possible base URI source per [RFC3986 Section 5.1.2 - 5.1.4](https://tools.ietf.org/html/rfc3986#section-5.1.2). The most common base URI source in this case is the retrieval URI of the Overlay document (per [RFC3986 Section 5.1.3](https://tools.ietf.org/html/rfc3986#section-5.1.3)), though other sources such as encapsulating entities ([RFC3986 Section 5.1.2](https://tools.ietf.org/html/rfc3986#section-5.1.2)) or application-specific defaults ([RFC3986 Section 5.1.4](https://tools.ietf.org/html/rfc3986#section-5.1.4)) MAY apply. + +#### Resolving URI Fragments + +Where a URI appears in contexts such as CommonMark hyperlinks within `description` fields and contains a fragment identifier, the fragment MUST be resolved per the fragment resolution mechanism of the referenced document. + +For Overlay Object fields that identify whole documents (`$self` and `extends`), fragment identifiers MUST NOT be used. + +#### Relative URI References in CommonMark Fields + +Relative references in CommonMark hyperlinks (such as those in `description` fields) are resolved in their rendered context, which might differ from the context of the Overlay document. ### Schema @@ -82,6 +111,7 @@ This is the root object of the [Overlay](#overlay). | Field Name | Type | Description | | ---- | :----: | ---- | | overlay | `string` | **REQUIRED**. This string MUST be the [version number](#versions) of the Overlay Specification that the Overlay document uses. The `overlay` field SHOULD be used by tooling to interpret the Overlay document. | +| $self | `string` | A URI-reference for the Overlay document. This string MUST be in the form of a URI-reference as defined by [RFC3986 Section 4.1](https://tools.ietf.org/html/rfc3986#section-4.1). When present, this field provides the self-assigned URI of this Overlay document, which also serves as its base URI in accordance with [RFC3986 Section 5.1.1](https://tools.ietf.org/html/rfc3986#section-5.1.1) for resolving relative references within this document. The `$self` URI MUST NOT contain a fragment identifier. Overlay documents MAY include a `$self` field to ensure portable, unambiguous reference resolution. | | info | [Info Object](#info-object) | **REQUIRED**. Provides metadata about the Overlay. The metadata MAY be used by tooling as required. | | extends | `string` | URI reference that identifies the target document (such as an [[OpenAPI]] document) this overlay applies to. | | actions | [[Action Object](#action-object)] | **REQUIRED** An ordered list of actions to be applied to the target document. The array MUST contain at least one value. | @@ -90,29 +120,46 @@ This object MAY be extended with [Specification Extensions](#specification-exten The list of actions MUST be applied in sequential order to ensure a consistent outcome. Actions are applied to the result of the previous action. This enables objects to be deleted in one action and then re-created in a subsequent action, for example. -The `extends` property can be used to indicate that the Overlay was designed to update a specific [[OpenAPI]] document. Where no `extends` is provided it is the responsibility of tooling to apply the Overlay document to the appropriate OpenAPI document(s). +The `extends` property can be used to indicate that the Overlay was designed to update a specific [[OpenAPI]] description. +Where no `extends` is provided it is the responsibility of tooling to apply the Overlay document to the appropriate OpenAPI description(s). + +In the following example, the Overlay identifies a target OpenAPI description using an absolute URI: + +```yaml +overlay: 1.2.0 +info: + title: Overlay for the Tic Tac Toe API document + version: 1.0.0 +extends: 'https://api.example.com/openapi/tictactoe.yaml' +... +``` -In the following example the `extends` property specifies that the overlay is designed to update the OpenAPI Tic Tac Toe example document, identified by an absolute URI. +In the following example, the Overlay document also includes `$self`, which establishes the document's identity and base URI: ```yaml overlay: 1.2.0 +$self: 'https://example.com/overlays/tictactoe.overlay.yaml' info: title: Overlay for the Tic Tac Toe API document version: 1.0.0 -extends: 'https://raw.githubusercontent.com/OAI/learn.openapis.org/refs/heads/main/examples/v3.1/tictactoe.yaml' +extends: 'https://api.example.com/openapi/tictactoe.yaml' ... ``` The `extends` property can also specify a relative URI reference. +Relative `extends` values are resolved using the base URI rules in [Relative References in URIs](#relative-references-in-uris): ```yaml overlay: 1.2.0 +$self: 'https://example.com/overlays/tictactoe.overlay.yaml' info: title: Overlay for the Tic Tac Toe API document version: 1.0.0 -extends: './tictactoe.yaml' +extends: '../openapi/tictactoe.yaml' ``` +With the above Overlay document, `extends` resolves to `https://example.com/openapi/tictactoe.yaml`. + #### Info Object The object provides metadata about the Overlay. @@ -594,3 +641,144 @@ Some formats, like [YAML](https://yaml.org/) or [JSONC](https://jsonc.org/), sup | 1.2.0 | TBD | Release of the Overlay Specification 1.2.0 | | 1.1.0 | 2026-01-14 | Release of the Overlay Specification 1.1.0 | | 1.0.0 | 2024-10-17 | First release of the Overlay Specification | + +## Appendix B: Examples of Base URI Determination and Identifier Resolution + +This appendix provides concrete examples demonstrating how the [`$self`](#overlay-self) field, Source Description URLs, and relative references work together across different deployment scenarios. + +### Base URI Within Content (Using `$self`) + +Assume the following Overlay document is retrieved from `file:///Users/dev/projects/overlays/purchase.overlay.yaml`: + +```yaml +overlay: 1.2.0 +$self: https://example.com/overlays/purchase.overlay.yaml +info: + title: Purchase overlay + version: 1.0.0 +extends: ../openapi/petstore.yaml +actions: + - target: '$.info' + update: + x-overlay: purchase +``` + +The relative `extends` URI `../openapi/petstore.yaml` resolves against the `$self` base URI `https://example.com/overlays/purchase.overlay.yaml`, producing `https://example.com/openapi/petstore.yaml`, regardless of the retrieval URI. + +### Base URI From the Retrieval URI (No `$self`) + +If the same Overlay document does not define `$self`: + +```yaml +overlay: 1.2.0 +info: + title: Purchase overlay + version: 1.0.0 +extends: ../openapi/petstore.yaml +actions: + - target: '$.info' + update: + x-overlay: purchase +``` + +Retrieved from `file:///Users/dev/projects/overlays/purchase.overlay.yaml`, the relative `extends` URI resolves to `file:///Users/dev/projects/openapi/petstore.yaml`. + +### Base URI From Encapsulating Entity + +Per [RFC3986 Section 5.1.2](https://tools.ietf.org/html/rfc3986#section-5.1.2), the base URI can be provided by an encapsulating entity. For example, in a `multipart/related` response where an Overlay document is embedded: + +```http +Content-Type: multipart/related; boundary=example; type=application/json + +--example +Content-Type: application/json +Content-Location: https://example.com/overlays/purchase.overlay.json + +{ + "overlay": "1.2.0", + "info": {"title": "Purchase overlay", "version": "1.0.0"}, + "extends": "../openapi/petstore.json", + "actions": [ + { + "target": "$.info", + "update": {"x-overlay": "purchase"} + } + ] +} +--example-- +``` + +The `Content-Location` header provides the base URI (`https://example.com/overlays/purchase.overlay.json`), so `../openapi/petstore.json` resolves to `https://example.com/openapi/petstore.json` even without a `$self` field. + +### Application-Specific Default Base URI + +Per [RFC3986 Section 5.1.4](https://tools.ietf.org/html/rfc3986#section-5.1.4), applications may define default base URIs. For documents loaded without explicit retrieval URIs (e.g., from a database), implementations typically generate a unique base URI per document using a fixed prefix plus a unique identifier. + +For example, an API management platform might construct base URIs as `https://overlays.example.com/{uuid}` for each document: + +```yaml +overlay: 1.2.0 +# No $self field +# Loaded from database, assigned base URI: https://overlays.example.com/a7b3c4d5 +info: + title: Purchase overlay + version: 1.0.0 +extends: specs/petstore.yaml # Resolves using application default +actions: + - target: '$.info' + update: + x-overlay: purchase +``` + +If the application assigns base URI `https://overlays.example.com/a7b3c4d5` to this document, then `specs/petstore.yaml` resolves to `https://overlays.example.com/specs/petstore.yaml`. + +### Resolving Relative `$self` + +When `$self` is itself a relative URI-reference, it must be resolved before being used as a base URI: + +```yaml +overlay: 1.2.0 +$self: overlays/purchase.overlay.yaml +info: + title: Purchase overlay + version: 1.0.0 +extends: ../openapi/petstore.yaml +actions: + - target: '$.info' + update: + x-overlay: purchase +``` + +Retrieved from `https://example.com/v2/api-description.yaml`: + +1. First, resolve the `$self` relative reference `overlays/purchase.overlay.yaml` against the retrieval URI `https://example.com/v2/api-description.yaml`, which resolves to `https://example.com/v2/overlays/purchase.overlay.yaml` per [RFC3986 Section 5.2](https://tools.ietf.org/html/rfc3986#section-5.2). +2. Then resolve the `extends` relative reference `../openapi/petstore.yaml` against the resolved `$self` base URI `https://example.com/v2/overlays/purchase.overlay.yaml`, which resolves to `https://example.com/v2/openapi/petstore.yaml`. + +### Why Potential Target Documents Should Be Checked for `$self` + +An Overlay may be given this `extends` value: + +```yaml +overlay: 1.2.0 +info: + title: Overlay for approved API description + version: 1.0.0 +extends: https://apis.example.com/approved/petstore.yaml +actions: + - target: '$.info' + update: + x-overlay: approved +``` + +A candidate target OpenAPI description might be retrieved from `https://cdn.example.net/mirror/petstore.yaml` while declaring: + +```yaml +openapi: 3.2.0 +$self: https://apis.example.com/approved/petstore.yaml +info: + title: Petstore + version: 1.0.0 +paths: {} +``` + +Because `extends` is identifier-based, the match succeeds using the target description's `$self` URI, even though retrieval occurred from a different location.