From 7bd8a15ecb6f8952acec93426bb9c60d8e093bcd Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Fri, 28 Feb 2025 07:03:31 -0800 Subject: [PATCH 01/12] Add `$self` for self-identifying documents This adds `$self` as a way for a document to define its own URI for use in reference targets, and as the base URI for relative URI references in the document. This does not impact the resolution of relative API URLs. --- src/oas.md | 184 ++++++++++++++++++++++++++++- src/schemas/validation/schema.yaml | 5 + 2 files changed, 186 insertions(+), 3 deletions(-) diff --git a/src/oas.md b/src/oas.md index f2e4eeb749..c4249d1997 100644 --- a/src/oas.md +++ b/src/oas.md @@ -342,13 +342,165 @@ Note that some URI fields are named `url` for historical reasons, but the descri Unless specified otherwise, all fields that are URIs MAY be relative references as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-4.2). -Relative references in [Schema Objects](#schema-object), including any that appear as `$id` values, use the nearest parent `$id` as a Base URI, as described by [JSON Schema Specification Draft 2020-12](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#section-8.2). +#### Establishing the Base URI -Relative URI references in other Objects, and in Schema Objects where no parent schema contains an `$id`, MUST be resolved using the referring document's base URI, which is determined in accordance with [[RFC3986]] [Section 5.1.2 – 5.1.4](https://tools.ietf.org/html/rfc3986#section-5.1.2). -In practice, this is usually the retrieval URI of the document, which MAY be determined based on either its current actual location or a user-supplied expected location. +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) and, for Schema objects, [JSON Schema draft 2020-12 Section 8.2](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#section-8.2), as illustrated by the examles below. + +The most common base URI source in the absence of the [OpenAPI Object's](#openapi-object) `$self` or the [Schema Object's](#schema-object) `$id` is the retrieval URI. +Implementations MAY support document retrieval, although see the [Security Considerations](#security-considerations) sections for additional guidance. +Even if retrieval is supported, it may be impossible due to network configuration or server unavailability (including the server hosting an older version while a new version is in development), or undesirable due to performance impacts. +Therefore, all implementations SHOULD allow users to provide the intended retrieval URI for each document so that references can be resolved as if retrievals were performed. + +##### Examples of Base URI Determination and Reference Resolution + +###### Base URI Within Content + +A base URI within the resource's content (RFC3986 Section 5.1.1) is the highest-precedence source of a base URI. +For OpenAPI Documents, this source is the OpenAPI Object's `$self` field, while for Schema Objects that contain a `$id`, or are a subschema of a Schema Object containing a `$id`, the source is the `$id` field: + +```YAML +openapi: 3.2.0 +$self: https://example.com/openapi +info: + title: Example API + version: 1.0 +components: + requestBodies: + Foo: + content: + application/json: + schema: + $ref: schemas/foo + schemas: + Foo: + $id: https://example.com/api/schemas/foo + properties: + bar: + $ref: bar + Bar: + $id: https://example.com/api/schemas/bar + type: string +``` + +In the example above, the `$ref` in the Request Body Object is resolved using `$self` as the base URI, producing `https://example.com/schemas/foo`. +This matches the `$id` at `#/components/schemas/Foo/$id` so it points to that Schema Object. +That Schema Object has a subschema with `$ref: bar`, which is resolved against the `$id` to produce `https://example.com/schemas/bar`, which matches the `$id` at `#/components/schemas/Bar/$id`. + +Note that referring to a schema with a JSON Pointer that crosses a Schema Object with a `$id` [is not interoperable](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#name-json-pointer-fragments-and-). +The JSON Schema specification does not address the case of using a pointer _to_ a Schema Object containing an `$id` without crossing into that Schema Object. +Therefore it is RECOMMENDED that OAD authors use `$id` values to reference such schemas rather than JSON Pointers. + +Note also that it is impossible for the reference at `#/components/schemas/Foo/properties/bar/$ref` to reference the schema at `#/components/schemas/Bar` using a JSON Pointer, as the JSON Pointer would be resolved relative to `https://example.com/schemas/foo`, not to the OpenAPI Document's base URI from `$self`. + + +###### Base URI From Encapsulating Entity + +If no base URI can be determined within the content, the next location to search is any encapsulating entity (RFC3986 Section 5.1.2). + +This is common for Schema Objects encapsulated within an OpenAPI Document. +An example of an OpenAPI Document itself being encapsulated in another entity would be a `multipart/related` archive ([[?RFC2557]]), such as the following `multipart/related; boundary="boundary-example"; type="application/openapi+yaml"` document. +Note that this is purely an example, and support for such multipart documents or any other format that could encapsulate an OpenAPI Document is not a requirement of this specification. + +```MULTIPART +--boundary-example +Content-Type: application/openapi+yaml +Content-Location: https://inaccessible-domain.com/api/openapi.yaml + +openapi: 3.2.0 +info: + title: Example API + version: 1.0 + externalDocs: + url: docs.html +components: + requestBodies: + Foo: + content: + application/json: + schema: + $ref: "#/components/api/schemas/Foo" + schemas: + Foo: + properties: + bar: + $ref: schemas/bar + +--boundary-example +Content-Type: application/schema+json; schema=https://spec.openapis.org/oas/3.2/schema-base/YYYY-MM-DD +Content-Location: https://example.com/api/schemas/bar + +{ + "type": "string" +} + +--boundary-example +Content-Type: text/html +Content-Location: https://example.com/api/docs.html + + + + API Documentation + + +

Awesome documentation goes here

+ + +``` + +In this example, the URI for each part, which also serves as its base URI, comes from the part's `Content-Location` header as specified by RFC2557. +Since the Schema Object at `#/components/schemas/Foo` does not contain an `$id`, the reference in its subschema uses the OpenAPI Document's base URI, which is taken from the `Content-Location` header of its part within the `multipart/related` format. +The resulting reference to `https://example.com/schemas/bar` matches the `Content-Location` header of the second part, which allows the reference target to be located within the multipart archive. + +Similarly, the `url` field of the [External Documentation Object](#external-documentation-object) is resolved against the base URI from `Content-Location`, producing `https://example.com/api/docs.html` which matches the `Content-Location` of the third part. + +###### Base URI From the Retrieval URI + +If no base URI is provided from either of the previous sources, the next source is the retrieval URI (RFC 3986 Section 5.1.3). + +For this example, assume that the YAML OpenAPI Document was retrieved from `https://example.com/api/openapis.yaml` and the JSON Schema document from `https://example.com/api/schemas/foo` + +Assume this document was retrieved from `https://example.com/api/openapis.yaml`: + +```YAML +openapi: 3.2.0 +info: + title: Example API + version: 1.0 +components: + requestBodies: + Foo: + content: + application/json: + schema: + $ref: schemas/foo +``` + +Assume this document was retrieved from `https://example.com/api/schemas/foo`: + +```JSON +{ + "type": "object", + "properties": { + "bar": { + "type": "string" + } + } +} +``` + +Resolving the `$ref: schemas/foo` against the retrieval URI of the OpenAPI Document produces `https://example.com/api/schemas/foo`, the retrieval URI of the JSON Schema document. + +###### Application-Specific Default Base URI + +When constructing an OpenAPI Document in memory that does not have a `$self`, or an encapsulating entity, or a retrieval URI, applications can resolve internal (fragment-only) references by assuming a default base URI (RFC3986 Section 5.1.4). +While this sort of internal resolution an be performed in practice without choosing a base URI, choosing one avoids the need to implement it as a special case. + +#### Resolving URI fragments If a URI contains a fragment identifier, then the fragment should be resolved per the fragment resolution mechanism of the referenced document. If the representation of the referenced document is JSON or YAML, then the fragment identifier SHOULD be interpreted as a JSON-Pointer as per [RFC6901](https://tools.ietf.org/html/rfc6901). +#### Relative URI References in CommonMark Fields + Relative references in CommonMark hyperlinks are resolved in their rendered context, which might differ from the context of the API description. ### Relative References in API URLs @@ -356,8 +508,29 @@ Relative references in CommonMark hyperlinks are resolved in their rendered cont API endpoints are by definition accessed as locations, and are described by this specification as **_URLs_**. Unless specified otherwise, all fields that are URLs MAY be relative references as defined by [RFC3986](https://tools.ietf.org/html/rfc3986#section-4.2). + +Because the API Is a distinct entity from the OpenAPI Document, RFC3986's base URI rules for the OpenAPI Document do not apply. Unless specified otherwise, relative references are resolved using the URLs defined in the [Server Object](#server-object) as a Base URL. Note that these themselves MAY be relative to the referring document. +#### Examples of API Base URL Determination + +Assume a retrieval URI of `https://device1.example.com` for the following OpenAPI Document: + +```YAML +openapi: 3.2.0 +$self: https://apidescriptions.example.com/foo +info: + title: Example API + version: 1.0 +servers: +- url: . + description: The production API on this device +- url: ./test + description: The test API on this device +``` + +For API URLs, the `$self` field, which identifies the OpenAPI Document, is ignored, and the retrieval URI is used instead. This produces a normalized production URL of `https://device1.example.com`, and a normalized test URL of `https://device1.example.com/test`. + ### Schema This section describes the structure of the OpenAPI Description format. @@ -376,6 +549,7 @@ This is the root object of the [OpenAPI Description](#openapi-description). | Field Name | Type | Description | | ---- | :----: | ---- | | openapi | `string` | **REQUIRED**. This string MUST be the [version number](#versions) of the OpenAPI Specification that the OpenAPI Document uses. The `openapi` field SHOULD be used by tooling to interpret the OpenAPI Document. This is _not_ related to the API [`info.version`](#info-version) string. | +| $self | `string` | This string MUST be in the form of an absolute URI as defined by [[RFC3986]] [Section 4.3](https://www.rfc-editor.org/rfc/rfc3986#section-4.3). The `$self` field provides the self-assigned URI of this document, which also serves as its base URI in accordance with [[RFC3986]] [Section 5.1.1](https://www.rfc-editor.org/rfc/rfc3986#section-5.1.1). Implementations MUST support identifying the targets of [API description URIs](#relative-references-in-api-description-uris) using the URI defined by this field when it is present. See [Establishing the Base URI](#establishing-the-base-uri) for the base URI behavior when `$self` is absent, and for examples of using `$self` to resolve references. | | info | [Info Object](#info-object) | **REQUIRED**. Provides metadata about the API. The metadata MAY be used by tooling as required. | | jsonSchemaDialect | `string` | The default value for the `$schema` keyword within [Schema Objects](#schema-object) contained within this OAS document. This MUST be in the form of a URI. | | servers | [[Server Object](#server-object)] | An array of Server Objects, which provide connectivity information to a target server. If the `servers` field is not provided, or is an empty array, the default value would be a [Server Object](#server-object) with a [url](#server-url) value of `/`. | @@ -388,6 +562,8 @@ This is the root object of the [OpenAPI Description](#openapi-description). This object MAY be extended with [Specification Extensions](#specification-extensions). +Implementations MAY choose to support referencing OpenAPI Documents that contain `$self` by another URI such as the retrieval URI, however this behavior is not interoperable and relying on it is NOT RECOMMENDED. + #### Info Object The object provides metadata about the API. @@ -516,6 +692,8 @@ An object representing a Server. This object MAY be extended with [Specification Extensions](#specification-extensions). +See [Examples of API Base URL Determination](#examples-of-api-base-url-determination) for examples of resolving relative server URLs. + ##### Server Object Example A single server would be described as: diff --git a/src/schemas/validation/schema.yaml b/src/schemas/validation/schema.yaml index f03bc55586..cc57c62530 100644 --- a/src/schemas/validation/schema.yaml +++ b/src/schemas/validation/schema.yaml @@ -8,6 +8,11 @@ properties: openapi: type: string pattern: '^3\.2\.\d+(-.+)?$' + $self: + type: string + format: uri + $comment: MUST NOT contain a fragment + pattern: '^[^#]*$' info: $ref: '#/$defs/info' jsonSchemaDialect: From 71ec162344cbbdb9f6d5e6ce3cac2cb0f67353f4 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Tue, 29 Apr 2025 17:37:02 -0700 Subject: [PATCH 02/12] Fix missing multipart boundary --- src/oas.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/oas.md b/src/oas.md index c4249d1997..cbf0de1123 100644 --- a/src/oas.md +++ b/src/oas.md @@ -424,7 +424,6 @@ components: properties: bar: $ref: schemas/bar - --boundary-example Content-Type: application/schema+json; schema=https://spec.openapis.org/oas/3.2/schema-base/YYYY-MM-DD Content-Location: https://example.com/api/schemas/bar @@ -432,7 +431,6 @@ Content-Location: https://example.com/api/schemas/bar { "type": "string" } - --boundary-example Content-Type: text/html Content-Location: https://example.com/api/docs.html @@ -445,6 +443,7 @@ Content-Location: https://example.com/api/docs.html

Awesome documentation goes here

+--boundary-example ``` In this example, the URI for each part, which also serves as its base URI, comes from the part's `Content-Location` header as specified by RFC2557. From db9f6435e11ff907382ff5c426cc1f4d161935e4 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Tue, 29 Apr 2025 20:38:26 -0700 Subject: [PATCH 03/12] Move base URI examples to appendix --- src/oas.md | 288 ++++++++++++++++++++++++++--------------------------- 1 file changed, 144 insertions(+), 144 deletions(-) diff --git a/src/oas.md b/src/oas.md index cbf0de1123..986e2e4e3b 100644 --- a/src/oas.md +++ b/src/oas.md @@ -344,156 +344,13 @@ Unless specified otherwise, all fields that are URIs MAY be relative references #### 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) and, for Schema objects, [JSON Schema draft 2020-12 Section 8.2](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#section-8.2), as illustrated by the examles below. +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) and, for Schema objects, [JSON Schema draft 2020-12 Section 8.2](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#section-8.2), as illustrated by the examles in [Appendix G: Examples of Base URI Determination and Reference Resolution](#appendix-g-examples-of-base-uri-determination-and-reference-resolution). The most common base URI source in the absence of the [OpenAPI Object's](#openapi-object) `$self` or the [Schema Object's](#schema-object) `$id` is the retrieval URI. Implementations MAY support document retrieval, although see the [Security Considerations](#security-considerations) sections for additional guidance. Even if retrieval is supported, it may be impossible due to network configuration or server unavailability (including the server hosting an older version while a new version is in development), or undesirable due to performance impacts. Therefore, all implementations SHOULD allow users to provide the intended retrieval URI for each document so that references can be resolved as if retrievals were performed. -##### Examples of Base URI Determination and Reference Resolution - -###### Base URI Within Content - -A base URI within the resource's content (RFC3986 Section 5.1.1) is the highest-precedence source of a base URI. -For OpenAPI Documents, this source is the OpenAPI Object's `$self` field, while for Schema Objects that contain a `$id`, or are a subschema of a Schema Object containing a `$id`, the source is the `$id` field: - -```YAML -openapi: 3.2.0 -$self: https://example.com/openapi -info: - title: Example API - version: 1.0 -components: - requestBodies: - Foo: - content: - application/json: - schema: - $ref: schemas/foo - schemas: - Foo: - $id: https://example.com/api/schemas/foo - properties: - bar: - $ref: bar - Bar: - $id: https://example.com/api/schemas/bar - type: string -``` - -In the example above, the `$ref` in the Request Body Object is resolved using `$self` as the base URI, producing `https://example.com/schemas/foo`. -This matches the `$id` at `#/components/schemas/Foo/$id` so it points to that Schema Object. -That Schema Object has a subschema with `$ref: bar`, which is resolved against the `$id` to produce `https://example.com/schemas/bar`, which matches the `$id` at `#/components/schemas/Bar/$id`. - -Note that referring to a schema with a JSON Pointer that crosses a Schema Object with a `$id` [is not interoperable](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#name-json-pointer-fragments-and-). -The JSON Schema specification does not address the case of using a pointer _to_ a Schema Object containing an `$id` without crossing into that Schema Object. -Therefore it is RECOMMENDED that OAD authors use `$id` values to reference such schemas rather than JSON Pointers. - -Note also that it is impossible for the reference at `#/components/schemas/Foo/properties/bar/$ref` to reference the schema at `#/components/schemas/Bar` using a JSON Pointer, as the JSON Pointer would be resolved relative to `https://example.com/schemas/foo`, not to the OpenAPI Document's base URI from `$self`. - - -###### Base URI From Encapsulating Entity - -If no base URI can be determined within the content, the next location to search is any encapsulating entity (RFC3986 Section 5.1.2). - -This is common for Schema Objects encapsulated within an OpenAPI Document. -An example of an OpenAPI Document itself being encapsulated in another entity would be a `multipart/related` archive ([[?RFC2557]]), such as the following `multipart/related; boundary="boundary-example"; type="application/openapi+yaml"` document. -Note that this is purely an example, and support for such multipart documents or any other format that could encapsulate an OpenAPI Document is not a requirement of this specification. - -```MULTIPART ---boundary-example -Content-Type: application/openapi+yaml -Content-Location: https://inaccessible-domain.com/api/openapi.yaml - -openapi: 3.2.0 -info: - title: Example API - version: 1.0 - externalDocs: - url: docs.html -components: - requestBodies: - Foo: - content: - application/json: - schema: - $ref: "#/components/api/schemas/Foo" - schemas: - Foo: - properties: - bar: - $ref: schemas/bar ---boundary-example -Content-Type: application/schema+json; schema=https://spec.openapis.org/oas/3.2/schema-base/YYYY-MM-DD -Content-Location: https://example.com/api/schemas/bar - -{ - "type": "string" -} ---boundary-example -Content-Type: text/html -Content-Location: https://example.com/api/docs.html - - - - API Documentation - - -

Awesome documentation goes here

- - ---boundary-example -``` - -In this example, the URI for each part, which also serves as its base URI, comes from the part's `Content-Location` header as specified by RFC2557. -Since the Schema Object at `#/components/schemas/Foo` does not contain an `$id`, the reference in its subschema uses the OpenAPI Document's base URI, which is taken from the `Content-Location` header of its part within the `multipart/related` format. -The resulting reference to `https://example.com/schemas/bar` matches the `Content-Location` header of the second part, which allows the reference target to be located within the multipart archive. - -Similarly, the `url` field of the [External Documentation Object](#external-documentation-object) is resolved against the base URI from `Content-Location`, producing `https://example.com/api/docs.html` which matches the `Content-Location` of the third part. - -###### Base URI From the Retrieval URI - -If no base URI is provided from either of the previous sources, the next source is the retrieval URI (RFC 3986 Section 5.1.3). - -For this example, assume that the YAML OpenAPI Document was retrieved from `https://example.com/api/openapis.yaml` and the JSON Schema document from `https://example.com/api/schemas/foo` - -Assume this document was retrieved from `https://example.com/api/openapis.yaml`: - -```YAML -openapi: 3.2.0 -info: - title: Example API - version: 1.0 -components: - requestBodies: - Foo: - content: - application/json: - schema: - $ref: schemas/foo -``` - -Assume this document was retrieved from `https://example.com/api/schemas/foo`: - -```JSON -{ - "type": "object", - "properties": { - "bar": { - "type": "string" - } - } -} -``` - -Resolving the `$ref: schemas/foo` against the retrieval URI of the OpenAPI Document produces `https://example.com/api/schemas/foo`, the retrieval URI of the JSON Schema document. - -###### Application-Specific Default Base URI - -When constructing an OpenAPI Document in memory that does not have a `$self`, or an encapsulating entity, or a retrieval URI, applications can resolve internal (fragment-only) references by assuming a default base URI (RFC3986 Section 5.1.4). -While this sort of internal resolution an be performed in practice without choosing a base URI, choosing one avoids the need to implement it as a special case. - #### Resolving URI fragments If a URI contains a fragment identifier, then the fragment should be resolved per the fragment resolution mechanism of the referenced document. If the representation of the referenced document is JSON or YAML, then the fragment identifier SHOULD be interpreted as a JSON-Pointer as per [RFC6901](https://tools.ietf.org/html/rfc6901). @@ -5338,3 +5195,146 @@ components: ``` In the `other` document, the referenced path item has a Security Requirement for a Security Scheme, `MySecurity`. The same Security Scheme exists in the original entry document. As outlined in [Resolving Implicit Connections](#resolving-implicit-connections), `MySecurity` is resolved with an [implementation-defined behavior](#undefined-and-implementation-defined-behavior). However, documented in that section, it is RECOMMENDED that tools resolve component names from the [entry document](#openapi-description-structure). As with all implementation-defined behavior, it is important to check tool documentation to determine which behavior is supported. + +## Appendix G: Examples of Base URI Determination and Reference Resolution + +### Base URI Within Content + +A base URI within the resource's content (RFC3986 Section 5.1.1) is the highest-precedence source of a base URI. +For OpenAPI Documents, this source is the OpenAPI Object's `$self` field, while for Schema Objects that contain a `$id`, or are a subschema of a Schema Object containing a `$id`, the source is the `$id` field: + +```YAML +openapi: 3.2.0 +$self: https://example.com/openapi +info: + title: Example API + version: 1.0 +components: + requestBodies: + Foo: + content: + application/json: + schema: + $ref: schemas/foo + schemas: + Foo: + $id: https://example.com/api/schemas/foo + properties: + bar: + $ref: bar + Bar: + $id: https://example.com/api/schemas/bar + type: string +``` + +In the example above, the `$ref` in the Request Body Object is resolved using `$self` as the base URI, producing `https://example.com/schemas/foo`. +This matches the `$id` at `#/components/schemas/Foo/$id` so it points to that Schema Object. +That Schema Object has a subschema with `$ref: bar`, which is resolved against the `$id` to produce `https://example.com/schemas/bar`, which matches the `$id` at `#/components/schemas/Bar/$id`. + +Note that referring to a schema with a JSON Pointer that crosses a Schema Object with a `$id` [is not interoperable](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#name-json-pointer-fragments-and-). +The JSON Schema specification does not address the case of using a pointer _to_ a Schema Object containing an `$id` without crossing into that Schema Object. +Therefore it is RECOMMENDED that OAD authors use `$id` values to reference such schemas rather than JSON Pointers. + +Note also that it is impossible for the reference at `#/components/schemas/Foo/properties/bar/$ref` to reference the schema at `#/components/schemas/Bar` using a JSON Pointer, as the JSON Pointer would be resolved relative to `https://example.com/schemas/foo`, not to the OpenAPI Document's base URI from `$self`. + + +### Base URI From Encapsulating Entity + +If no base URI can be determined within the content, the next location to search is any encapsulating entity (RFC3986 Section 5.1.2). + +This is common for Schema Objects encapsulated within an OpenAPI Document. +An example of an OpenAPI Document itself being encapsulated in another entity would be a `multipart/related` archive ([[?RFC2557]]), such as the following `multipart/related; boundary="boundary-example"; type="application/openapi+yaml"` document. +Note that this is purely an example, and support for such multipart documents or any other format that could encapsulate an OpenAPI Document is not a requirement of this specification. + +```MULTIPART +--boundary-example +Content-Type: application/openapi+yaml +Content-Location: https://inaccessible-domain.com/api/openapi.yaml + +openapi: 3.2.0 +info: + title: Example API + version: 1.0 + externalDocs: + url: docs.html +components: + requestBodies: + Foo: + content: + application/json: + schema: + $ref: "#/components/api/schemas/Foo" + schemas: + Foo: + properties: + bar: + $ref: schemas/bar +--boundary-example +Content-Type: application/schema+json; schema=https://spec.openapis.org/oas/3.2/schema-base/YYYY-MM-DD +Content-Location: https://example.com/api/schemas/bar + +{ + "type": "string" +} +--boundary-example +Content-Type: text/html +Content-Location: https://example.com/api/docs.html + + + + API Documentation + + +

Awesome documentation goes here

+ + +--boundary-example +``` + +In this example, the URI for each part, which also serves as its base URI, comes from the part's `Content-Location` header as specified by RFC2557. +Since the Schema Object at `#/components/schemas/Foo` does not contain an `$id`, the reference in its subschema uses the OpenAPI Document's base URI, which is taken from the `Content-Location` header of its part within the `multipart/related` format. +The resulting reference to `https://example.com/schemas/bar` matches the `Content-Location` header of the second part, which allows the reference target to be located within the multipart archive. + +Similarly, the `url` field of the [External Documentation Object](#external-documentation-object) is resolved against the base URI from `Content-Location`, producing `https://example.com/api/docs.html` which matches the `Content-Location` of the third part. + +### Base URI From the Retrieval URI + +If no base URI is provided from either of the previous sources, the next source is the retrieval URI (RFC 3986 Section 5.1.3). + +For this example, assume that the YAML OpenAPI Document was retrieved from `https://example.com/api/openapis.yaml` and the JSON Schema document from `https://example.com/api/schemas/foo` + +Assume this document was retrieved from `https://example.com/api/openapis.yaml`: + +```YAML +openapi: 3.2.0 +info: + title: Example API + version: 1.0 +components: + requestBodies: + Foo: + content: + application/json: + schema: + $ref: schemas/foo +``` + +Assume this document was retrieved from `https://example.com/api/schemas/foo`: + +```JSON +{ + "type": "object", + "properties": { + "bar": { + "type": "string" + } + } +} +``` + +Resolving the `$ref: schemas/foo` against the retrieval URI of the OpenAPI Document produces `https://example.com/api/schemas/foo`, the retrieval URI of the JSON Schema document. + +### Application-Specific Default Base URI + +When constructing an OpenAPI Document in memory that does not have a `$self`, or an encapsulating entity, or a retrieval URI, applications can resolve internal (fragment-only) references by assuming a default base URI (RFC3986 Section 5.1.4). +While this sort of internal resolution an be performed in practice without choosing a base URI, choosing one avoids the need to implement it as a special case. From 2832652e1e6c126a4f9f95ec1c0982d368a2c2b1 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Wed, 30 Apr 2025 12:14:43 -0700 Subject: [PATCH 04/12] Improved examples Including fixing a bug in one of the URIs. --- src/oas.md | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/oas.md b/src/oas.md index 986e2e4e3b..f07d7c7dfe 100644 --- a/src/oas.md +++ b/src/oas.md @@ -5203,19 +5203,36 @@ In the `other` document, the referenced path item has a Security Requirement for A base URI within the resource's content (RFC3986 Section 5.1.1) is the highest-precedence source of a base URI. For OpenAPI Documents, this source is the OpenAPI Object's `$self` field, while for Schema Objects that contain a `$id`, or are a subschema of a Schema Object containing a `$id`, the source is the `$id` field: +Assume the retrieval URI of the following document is `file://home/someone/src/api/openapi.yaml`: + ```YAML openapi: 3.2.0 -$self: https://example.com/openapi +$self: https://example.com/api/openapi info: title: Example API version: 1.0 +paths: + /foo: + get: + requestBody: + $ref: "shared#/components/requestBodies/Foo" +``` + +Assume the retrieval URI for the following document is `https://git.example.com/shared/blob/main/shared/foo.yaml`: + +```YAML +openapi: 3.2.0 +$self: https://example.com/api/shared/foo +info: + title: Shared components for all APIs + version: 1.0 components: requestBodies: Foo: content: application/json: schema: - $ref: schemas/foo + $ref: ../schemas/foo schemas: Foo: $id: https://example.com/api/schemas/foo @@ -5227,7 +5244,12 @@ components: type: string ``` -In the example above, the `$ref` in the Request Body Object is resolved using `$self` as the base URI, producing `https://example.com/schemas/foo`. +In this example, the retrieval URIs are irrelevant because both documents define `$self`. + +For the relative `$ref` in the first document, it is resolved against `$self` to produce `https://example.com/shared/foo#/components/requestBodies/Foo`. +The portion of that URI before the '#' matches the `$self` of the second document, so the reference target is resolved to `#/components/requestBodies/Foo` in that second document. + +In that document, the `$ref` in the Request Body Object is resolved using that document's `$self` as the base URI, producing `https://example.com/schemas/foo`. This matches the `$id` at `#/components/schemas/Foo/$id` so it points to that Schema Object. That Schema Object has a subschema with `$ref: bar`, which is resolved against the `$id` to produce `https://example.com/schemas/bar`, which matches the `$id` at `#/components/schemas/Bar/$id`. @@ -5237,7 +5259,6 @@ Therefore it is RECOMMENDED that OAD authors use `$id` values to reference such Note also that it is impossible for the reference at `#/components/schemas/Foo/properties/bar/$ref` to reference the schema at `#/components/schemas/Bar` using a JSON Pointer, as the JSON Pointer would be resolved relative to `https://example.com/schemas/foo`, not to the OpenAPI Document's base URI from `$self`. - ### Base URI From Encapsulating Entity If no base URI can be determined within the content, the next location to search is any encapsulating entity (RFC3986 Section 5.1.2). @@ -5246,6 +5267,8 @@ This is common for Schema Objects encapsulated within an OpenAPI Document. An example of an OpenAPI Document itself being encapsulated in another entity would be a `multipart/related` archive ([[?RFC2557]]), such as the following `multipart/related; boundary="boundary-example"; type="application/openapi+yaml"` document. Note that this is purely an example, and support for such multipart documents or any other format that could encapsulate an OpenAPI Document is not a requirement of this specification. +RFC2557 was written to allow sending hyperlinked sets of documents as email attachments, in which case there would not be a retrieval URI for the multipart attachment (although the format could also be used in HTTP as well). + ```MULTIPART --boundary-example Content-Type: application/openapi+yaml @@ -5293,7 +5316,7 @@ Content-Location: https://example.com/api/docs.html In this example, the URI for each part, which also serves as its base URI, comes from the part's `Content-Location` header as specified by RFC2557. Since the Schema Object at `#/components/schemas/Foo` does not contain an `$id`, the reference in its subschema uses the OpenAPI Document's base URI, which is taken from the `Content-Location` header of its part within the `multipart/related` format. -The resulting reference to `https://example.com/schemas/bar` matches the `Content-Location` header of the second part, which allows the reference target to be located within the multipart archive. +The resulting reference to `https://example.com/schemas/bar` matches the `Content-Location` header of the second part, which according to RFC2557 allows the reference target to be located within the multipart archive. Similarly, the `url` field of the [External Documentation Object](#external-documentation-object) is resolved against the base URI from `Content-Location`, producing `https://example.com/api/docs.html` which matches the `Content-Location` of the third part. From 4435fd3d7b96094e13a44feeae3369bade0d9e46 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Sat, 3 May 2025 18:31:39 -0700 Subject: [PATCH 05/12] Fix more example bugs Never change your directory structure halfway through writing examples... --- src/oas.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oas.md b/src/oas.md index f07d7c7dfe..e110ccc235 100644 --- a/src/oas.md +++ b/src/oas.md @@ -5215,7 +5215,7 @@ paths: /foo: get: requestBody: - $ref: "shared#/components/requestBodies/Foo" + $ref: "shared/foo#/components/requestBodies/Foo" ``` Assume the retrieval URI for the following document is `https://git.example.com/shared/blob/main/shared/foo.yaml`: @@ -5249,7 +5249,7 @@ In this example, the retrieval URIs are irrelevant because both documents define For the relative `$ref` in the first document, it is resolved against `$self` to produce `https://example.com/shared/foo#/components/requestBodies/Foo`. The portion of that URI before the '#' matches the `$self` of the second document, so the reference target is resolved to `#/components/requestBodies/Foo` in that second document. -In that document, the `$ref` in the Request Body Object is resolved using that document's `$self` as the base URI, producing `https://example.com/schemas/foo`. +In that document, the `$ref` in the Request Body Object is resolved using that document's `$self` as the base URI, producing `https://example.com/api/schemas/foo`. This matches the `$id` at `#/components/schemas/Foo/$id` so it points to that Schema Object. That Schema Object has a subschema with `$ref: bar`, which is resolved against the `$id` to produce `https://example.com/schemas/bar`, which matches the `$id` at `#/components/schemas/Bar/$id`. From 182ca17dfb348597328c5456ca2d0a440e6ccb85 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Thu, 8 May 2025 10:12:41 -0700 Subject: [PATCH 06/12] All example URI paths start with /api/... Fix the $self resolution examples to consistently use /api/, which is used in all of the relevant base URIs and is not impacted by any of the relative paths. --- src/oas.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/oas.md b/src/oas.md index e110ccc235..cfe8453e81 100644 --- a/src/oas.md +++ b/src/oas.md @@ -5246,18 +5246,18 @@ components: In this example, the retrieval URIs are irrelevant because both documents define `$self`. -For the relative `$ref` in the first document, it is resolved against `$self` to produce `https://example.com/shared/foo#/components/requestBodies/Foo`. +For the relative `$ref` in the first document, it is resolved against `$self` to produce `https://example.com/api/shared/foo#/components/requestBodies/Foo`. The portion of that URI before the '#' matches the `$self` of the second document, so the reference target is resolved to `#/components/requestBodies/Foo` in that second document. In that document, the `$ref` in the Request Body Object is resolved using that document's `$self` as the base URI, producing `https://example.com/api/schemas/foo`. This matches the `$id` at `#/components/schemas/Foo/$id` so it points to that Schema Object. -That Schema Object has a subschema with `$ref: bar`, which is resolved against the `$id` to produce `https://example.com/schemas/bar`, which matches the `$id` at `#/components/schemas/Bar/$id`. +That Schema Object has a subschema with `$ref: bar`, which is resolved against the `$id` to produce `https://example.com/api/schemas/bar`, which matches the `$id` at `#/components/schemas/Bar/$id`. Note that referring to a schema with a JSON Pointer that crosses a Schema Object with a `$id` [is not interoperable](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#name-json-pointer-fragments-and-). The JSON Schema specification does not address the case of using a pointer _to_ a Schema Object containing an `$id` without crossing into that Schema Object. Therefore it is RECOMMENDED that OAD authors use `$id` values to reference such schemas rather than JSON Pointers. -Note also that it is impossible for the reference at `#/components/schemas/Foo/properties/bar/$ref` to reference the schema at `#/components/schemas/Bar` using a JSON Pointer, as the JSON Pointer would be resolved relative to `https://example.com/schemas/foo`, not to the OpenAPI Document's base URI from `$self`. +Note also that it is impossible for the reference at `#/components/schemas/Foo/properties/bar/$ref` to reference the schema at `#/components/schemas/Bar` using a JSON Pointer, as the JSON Pointer would be resolved relative to `https://example.com/api/schemas/foo`, not to the OpenAPI Document's base URI from `$self`. ### Base URI From Encapsulating Entity From 7be51a40042ae45d37b95f67863b594615041232 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Thu, 8 May 2025 11:02:16 -0700 Subject: [PATCH 07/12] Allow relative `$self`, include examples This also further clarifies the need to use `$self` in reference targets for interoperability even if other URIs might work in some cases. --- src/oas.md | 61 ++++++++++++++++++++++++++++-- src/schemas/validation/schema.yaml | 2 +- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/oas.md b/src/oas.md index cfe8453e81..57e54504b3 100644 --- a/src/oas.md +++ b/src/oas.md @@ -346,7 +346,9 @@ Unless specified otherwise, all fields that are URIs MAY be relative references 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) and, for Schema objects, [JSON Schema draft 2020-12 Section 8.2](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#section-8.2), as illustrated by the examles in [Appendix G: Examples of Base URI Determination and Reference Resolution](#appendix-g-examples-of-base-uri-determination-and-reference-resolution). -The most common base URI source in the absence of the [OpenAPI Object's](#openapi-object) `$self` or the [Schema Object's](#schema-object) `$id` is the retrieval URI. +If `$self` is a relative URI-reference, it is resolved agains the next possible base URI source ([[RFC3986]] [Section 5.1.2 – 5.1.4](https://tools.ietf.org/html/rfc3986#section-5.1.2)) before being used for the resolution of other relative URI-references. + +The most common base URI source that is used in the event of a missing or relative `$self` (in the [OpenAPI Object](#openapi-object)) and (for [Schema Object](#schema-object)) `$id` is the retrieval URI. Implementations MAY support document retrieval, although see the [Security Considerations](#security-considerations) sections for additional guidance. Even if retrieval is supported, it may be impossible due to network configuration or server unavailability (including the server hosting an older version while a new version is in development), or undesirable due to performance impacts. Therefore, all implementations SHOULD allow users to provide the intended retrieval URI for each document so that references can be resolved as if retrievals were performed. @@ -405,7 +407,7 @@ This is the root object of the [OpenAPI Description](#openapi-description). | Field Name | Type | Description | | ---- | :----: | ---- | | openapi | `string` | **REQUIRED**. This string MUST be the [version number](#versions) of the OpenAPI Specification that the OpenAPI Document uses. The `openapi` field SHOULD be used by tooling to interpret the OpenAPI Document. This is _not_ related to the API [`info.version`](#info-version) string. | -| $self | `string` | This string MUST be in the form of an absolute URI as defined by [[RFC3986]] [Section 4.3](https://www.rfc-editor.org/rfc/rfc3986#section-4.3). The `$self` field provides the self-assigned URI of this document, which also serves as its base URI in accordance with [[RFC3986]] [Section 5.1.1](https://www.rfc-editor.org/rfc/rfc3986#section-5.1.1). Implementations MUST support identifying the targets of [API description URIs](#relative-references-in-api-description-uris) using the URI defined by this field when it is present. See [Establishing the Base URI](#establishing-the-base-uri) for the base URI behavior when `$self` is absent, and for examples of using `$self` to resolve references. | +| $self | `string` | This string MUST be in the form of a URI-reference as defined by [[RFC3986]] [Section 4.1](https://www.rfc-editor.org/rfc/rfc3986#section-4.1). The `$self` field provides the self-assigned URI of this document, which also serves as its base URI in accordance with [[RFC3986]] [Section 5.1.1](https://www.rfc-editor.org/rfc/rfc3986#section-5.1.1). Implementations MUST support identifying the targets of [API description URIs](#relative-references-in-api-description-uris) using the URI defined by this field when it is present. See [Establishing the Base URI](#establishing-the-base-uri) for the base URI behavior when `$self` is absent or relative, and see [Appendix G]((#appendix-g-examples-of-base-uri-determination-and-reference-resolution)) for examples of using `$self` to resolve references. | | info | [Info Object](#info-object) | **REQUIRED**. Provides metadata about the API. The metadata MAY be used by tooling as required. | | jsonSchemaDialect | `string` | The default value for the `$schema` keyword within [Schema Objects](#schema-object) contained within this OAS document. This MUST be in the form of a URI. | | servers | [[Server Object](#server-object)] | An array of Server Objects, which provide connectivity information to a target server. If the `servers` field is not provided, or is an empty array, the default value would be a [Server Object](#server-object) with a [url](#server-url) value of `/`. | @@ -419,6 +421,7 @@ This is the root object of the [OpenAPI Description](#openapi-description). This object MAY be extended with [Specification Extensions](#specification-extensions). Implementations MAY choose to support referencing OpenAPI Documents that contain `$self` by another URI such as the retrieval URI, however this behavior is not interoperable and relying on it is NOT RECOMMENDED. +OAD authors MUST write references using the target document's `$self` URI in order to have interoperable behavior. #### Info Object @@ -5198,6 +5201,8 @@ In the `other` document, the referenced path item has a Security Requirement for ## Appendix G: Examples of Base URI Determination and Reference Resolution +This section shows each of the four possible sources of base URIs, followed by an example with a relative `$self` and `$id`. + ### Base URI Within Content A base URI within the resource's content (RFC3986 Section 5.1.1) is the highest-precedence source of a base URI. @@ -5360,4 +5365,54 @@ Resolving the `$ref: schemas/foo` against the retrieval URI of the OpenAPI Docum ### Application-Specific Default Base URI When constructing an OpenAPI Document in memory that does not have a `$self`, or an encapsulating entity, or a retrieval URI, applications can resolve internal (fragment-only) references by assuming a default base URI (RFC3986 Section 5.1.4). -While this sort of internal resolution an be performed in practice without choosing a base URI, choosing one avoids the need to implement it as a special case. +While this sort of internal resolution an be performed in practice without choosing a base URI, choosing one, such as a URN with a randomly generated UUID (e.g. `urn:uuid:f26cdaad-3193-4398-a838-4ecb7326c4c5`) avoids the need to implement it as a special case. + +### Resolving Relative `$self` and `$id` + +Let's re-consider the first example in this appendix, but with relative URI-references for `$self` and `$id`, and retrieval URLs that support that relative usage: + + +Assume that the following is retrieved from `https://staging.example.com/api/openapi`: + +```YAML +openapi: 3.2.0 +$self: /api/openapi +info: + title: Example API + version: 1.0 +paths: + /foo: + get: + requestBody: + $ref: "shared/foo#/components/requestBodies/Foo" +``` + +Assume the retrieval URI for the following document is `https://staging.example.com/api/shared/foo`: + +```YAML +openapi: 3.2.0 +$self: /api/shared/foo +info: + title: Shared components for all APIs + version: 1.0 +components: + requestBodies: + Foo: + content: + application/json: + schema: + $ref: ../schemas/foo + schemas: + Foo: + $id: /api/schemas/foo + properties: + bar: + $ref: bar + Bar: + $id: /api/schemas/bar + type: string +``` + +In this example, All of the `$self` and `$id` values are relative URI-references consisting of an absolute path. +This allows the retrieval URL to set the host (and scheme), in this case `https://staging.example.com`, resulting in the first document's `$self` being `https://staging.example.com/openapi`, and the second document's `$self` being `https://staging.example.com/api/shared/foo`, with `$id` values of `https://staging.example.com/api/schemas/foo` and `https://staging.example.com/api/schemas/bar`. +Relative `$self` and `$id` values of this sort allow the same set of documents to work when deployed to other hosts, e.g. `https://example.com` (production) or `https://localhost:8080` (local development). diff --git a/src/schemas/validation/schema.yaml b/src/schemas/validation/schema.yaml index cc57c62530..bbaa260abf 100644 --- a/src/schemas/validation/schema.yaml +++ b/src/schemas/validation/schema.yaml @@ -10,7 +10,7 @@ properties: pattern: '^3\.2\.\d+(-.+)?$' $self: type: string - format: uri + format: uri-reference $comment: MUST NOT contain a fragment pattern: '^[^#]*$' info: From 4068c092c63d4599050ea2559c79a13a01245a04 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Sun, 11 May 2025 08:33:58 -0700 Subject: [PATCH 08/12] Fix another base URI example bug --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index 57e54504b3..baec25b134 100644 --- a/src/oas.md +++ b/src/oas.md @@ -5277,7 +5277,7 @@ RFC2557 was written to allow sending hyperlinked sets of documents as email atta ```MULTIPART --boundary-example Content-Type: application/openapi+yaml -Content-Location: https://inaccessible-domain.com/api/openapi.yaml +Content-Location: https://example.com/api/openapi.yaml openapi: 3.2.0 info: From 966c0c6003d16a8a917c17a443cfc8cbaffff38e Mon Sep 17 00:00:00 2001 From: Henry Andrews Date: Mon, 12 May 2025 13:28:02 -0700 Subject: [PATCH 09/12] Apply suggestions from code review Co-authored-by: Ralf Handl --- src/oas.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/oas.md b/src/oas.md index baec25b134..f30e257b0b 100644 --- a/src/oas.md +++ b/src/oas.md @@ -387,7 +387,7 @@ servers: description: The test API on this device ``` -For API URLs, the `$self` field, which identifies the OpenAPI Document, is ignored, and the retrieval URI is used instead. This produces a normalized production URL of `https://device1.example.com`, and a normalized test URL of `https://device1.example.com/test`. +For API URLs the `$self` field, which identifies the OpenAPI Document, is ignored and the retrieval URI is used instead. This produces a normalized production URL of `https://device1.example.com`, and a normalized test URL of `https://device1.example.com/test`. ### Schema @@ -5329,8 +5329,6 @@ Similarly, the `url` field of the [External Documentation Object](#external-docu If no base URI is provided from either of the previous sources, the next source is the retrieval URI (RFC 3986 Section 5.1.3). -For this example, assume that the YAML OpenAPI Document was retrieved from `https://example.com/api/openapis.yaml` and the JSON Schema document from `https://example.com/api/schemas/foo` - Assume this document was retrieved from `https://example.com/api/openapis.yaml`: ```YAML @@ -5413,6 +5411,6 @@ components: type: string ``` -In this example, All of the `$self` and `$id` values are relative URI-references consisting of an absolute path. +In this example, all of the `$self` and `$id` values are relative URI-references consisting of an absolute path. This allows the retrieval URL to set the host (and scheme), in this case `https://staging.example.com`, resulting in the first document's `$self` being `https://staging.example.com/openapi`, and the second document's `$self` being `https://staging.example.com/api/shared/foo`, with `$id` values of `https://staging.example.com/api/schemas/foo` and `https://staging.example.com/api/schemas/bar`. Relative `$self` and `$id` values of this sort allow the same set of documents to work when deployed to other hosts, e.g. `https://example.com` (production) or `https://localhost:8080` (local development). From eace593a1105f83945827f338e341d766052dae2 Mon Sep 17 00:00:00 2001 From: Henry Andrews Date: Mon, 12 May 2025 13:40:41 -0700 Subject: [PATCH 10/12] Better wording Co-authored-by: Ralf Handl --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index f30e257b0b..28638b4a6b 100644 --- a/src/oas.md +++ b/src/oas.md @@ -5251,7 +5251,7 @@ components: In this example, the retrieval URIs are irrelevant because both documents define `$self`. -For the relative `$ref` in the first document, it is resolved against `$self` to produce `https://example.com/api/shared/foo#/components/requestBodies/Foo`. +The relative `$ref` in the first document is resolved against `$self` to produce `https://example.com/api/shared/foo#/components/requestBodies/Foo`. The portion of that URI before the '#' matches the `$self` of the second document, so the reference target is resolved to `#/components/requestBodies/Foo` in that second document. In that document, the `$ref` in the Request Body Object is resolved using that document's `$self` as the base URI, producing `https://example.com/api/schemas/foo`. From 8709e12e02a542c306fd0aa08a15c71d54a152ad Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Mon, 12 May 2025 13:48:49 -0700 Subject: [PATCH 11/12] Review feedback --- src/oas.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/oas.md b/src/oas.md index 28638b4a6b..4f65661c05 100644 --- a/src/oas.md +++ b/src/oas.md @@ -420,8 +420,8 @@ This is the root object of the [OpenAPI Description](#openapi-description). This object MAY be extended with [Specification Extensions](#specification-extensions). -Implementations MAY choose to support referencing OpenAPI Documents that contain `$self` by another URI such as the retrieval URI, however this behavior is not interoperable and relying on it is NOT RECOMMENDED. -OAD authors MUST write references using the target document's `$self` URI in order to have interoperable behavior. +To ensure interoperability, references MUST use the target document's `$self` URI if the `$self` field is present. +Implementations MAY choose to support referencing by other URIs such as the retrieval URI even when `$self` is present, however this behavior is not interoperable and relying on it is NOT RECOMMENDED. #### Info Object @@ -5258,11 +5258,10 @@ In that document, the `$ref` in the Request Body Object is resolved using that d This matches the `$id` at `#/components/schemas/Foo/$id` so it points to that Schema Object. That Schema Object has a subschema with `$ref: bar`, which is resolved against the `$id` to produce `https://example.com/api/schemas/bar`, which matches the `$id` at `#/components/schemas/Bar/$id`. -Note that referring to a schema with a JSON Pointer that crosses a Schema Object with a `$id` [is not interoperable](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#name-json-pointer-fragments-and-). -The JSON Schema specification does not address the case of using a pointer _to_ a Schema Object containing an `$id` without crossing into that Schema Object. -Therefore it is RECOMMENDED that OAD authors use `$id` values to reference such schemas rather than JSON Pointers. +To guarantee interoperability, Schema Objects containing an `$id`, or that are under a schema containing an `$id`, MUST be referenced by the nearest such `$id` for the non-fragment part of the reference. +As the JSON Schema specification notes, using a base URI other than the nearest `$id` and crossing that `$id` with a JSON Pointer fragment [is not interoperable](https://www.ietf.org/archive/id/draft-bhutton-json-schema-01.html#name-json-pointer-fragments-and-). -Note also that it is impossible for the reference at `#/components/schemas/Foo/properties/bar/$ref` to reference the schema at `#/components/schemas/Bar` using a JSON Pointer, as the JSON Pointer would be resolved relative to `https://example.com/api/schemas/foo`, not to the OpenAPI Document's base URI from `$self`. +Note also that it is impossible for the reference at `#/components/schemas/Foo/properties/bar/$ref` to reference the schema at `#/components/schemas/Bar` using _only_ a JSON Pointer fragment, as the JSON Pointer would be resolved relative to `https://example.com/api/schemas/foo`, not to the OpenAPI Document's base URI from `$self`. ### Base URI From Encapsulating Entity @@ -5367,7 +5366,7 @@ While this sort of internal resolution an be performed in practice without choos ### Resolving Relative `$self` and `$id` -Let's re-consider the first example in this appendix, but with relative URI-references for `$self` and `$id`, and retrieval URLs that support that relative usage: +Let's re-consider the first example in this appendix, but with relative URI-references for `$self` and `$id`, and retrieval URIs that support that relative usage: Assume that the following is retrieved from `https://staging.example.com/api/openapi`: @@ -5412,5 +5411,5 @@ components: ``` In this example, all of the `$self` and `$id` values are relative URI-references consisting of an absolute path. -This allows the retrieval URL to set the host (and scheme), in this case `https://staging.example.com`, resulting in the first document's `$self` being `https://staging.example.com/openapi`, and the second document's `$self` being `https://staging.example.com/api/shared/foo`, with `$id` values of `https://staging.example.com/api/schemas/foo` and `https://staging.example.com/api/schemas/bar`. +This allows the retrieval URI to set the host (and scheme), in this case `https://staging.example.com`, resulting in the first document's `$self` being `https://staging.example.com/openapi`, and the second document's `$self` being `https://staging.example.com/api/shared/foo`, with `$id` values of `https://staging.example.com/api/schemas/foo` and `https://staging.example.com/api/schemas/bar`. Relative `$self` and `$id` values of this sort allow the same set of documents to work when deployed to other hosts, e.g. `https://example.com` (production) or `https://localhost:8080` (local development). From fc66d13ac94d2b09a623d4cec59f0f83ed86102a Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Mon, 12 May 2025 13:54:36 -0700 Subject: [PATCH 12/12] Remove link to metaschema... ...because the YYYY-MM-DD in the URI makes it more trouble than it's worth. --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index 4f65661c05..a548d0701c 100644 --- a/src/oas.md +++ b/src/oas.md @@ -5297,7 +5297,7 @@ components: bar: $ref: schemas/bar --boundary-example -Content-Type: application/schema+json; schema=https://spec.openapis.org/oas/3.2/schema-base/YYYY-MM-DD +Content-Type: application/schema+json Content-Location: https://example.com/api/schemas/bar {