From 4aaab77197bc76593eea3cbfa2f608289b0fd66e Mon Sep 17 00:00:00 2001 From: Jason Desrosiers Date: Mon, 14 Jun 2021 15:36:14 -0700 Subject: [PATCH 1/4] Changes from #2618. Don't merge this commit --- versions/3.2.0.md | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/versions/3.2.0.md b/versions/3.2.0.md index 87a88906b2..712f7545df 100644 --- a/versions/3.2.0.md +++ b/versions/3.2.0.md @@ -2332,7 +2332,8 @@ The OpenAPI Specification's base vocabulary is comprised of the following keywor Field Name | Type | Description ---|:---:|--- -discriminator | [Discriminator Object](#discriminatorObject) | Adds support for polymorphism. The discriminator is an object name that is used to differentiate between other schemas which may satisfy the payload description. See [Composition and Inheritance](#schemaComposition) for more details. +discriminator | [Discriminator +Object](#discriminatorObject) | Adds support for polymorphism. The discriminator is used to determine which of a set of schemas a payload is expected to satisfy. See [Composition and Inheritance](#schemaComposition) for more details. xml | [XML Object](#xmlObject) | This MAY be used only on properties schemas. It has no effect on root schemas. Adds additional metadata to describe the XML representation of this property. externalDocs | [External Documentation Object](#externalDocumentationObject) | Additional external documentation for this schema. example | Any | A free-form property to include an example of an instance for this schema. To represent examples that cannot be naturally represented in JSON or YAML, a string value can be used to contain the example with escaping where necessary.

**Deprecated:** The `example` property has been deprecated in favor of the JSON Schema `examples` keyword. Use of `example` is discouraged, and later versions of this specification may remove it. @@ -2345,13 +2346,8 @@ The OpenAPI Specification allows combining and extending model definitions using `allOf` takes an array of object definitions that are validated *independently* but together compose a single object. While composition offers model extensibility, it does not imply a hierarchy between the models. -To support polymorphism, the OpenAPI Specification adds the `discriminator` field. -When used, the `discriminator` will be the name of the property that decides which schema definition validates the structure of the model. -As such, the `discriminator` field MUST be a required field. -There are two ways to define the value of a discriminator for an inheriting instance. -- Use the schema name. -- Override the schema name by overriding the property with a new value. If a new value exists, this takes precedence over the schema name. -As such, inline schema definitions, which do not have a given id, *cannot* be used in polymorphism. +To support polymorphism, the OpenAPI Specification adds the `discriminator` keyword. +When used, the `discriminator` will indicate the name of the property that hints which schema definition is expected to validate the structure of the model. ###### XML Modeling @@ -2698,9 +2694,14 @@ components: #### Discriminator Object -When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the document of an alternative schema based on the value associated with it. +When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The `discriminator` keyword is used to inform the consumer of the document which of the alternatives is expected or preferred. -When using the discriminator, _inline_ schemas will not be considered. +`discriminator` uses a schema's "name" to automatically map a property value to +a schema. The schema's "name" is the property name used when declaring the +schema as a component in an OpenAPI document. For example, the name of the +schema at `#/components/schemas/Cat` is "Cat". Therefore, when using +`discriminator`, _inline_ schemas will not be considered because they don't have +a "name". ##### Fixed Fields Field Name | Type | Description @@ -2722,8 +2723,7 @@ MyResponseType: - $ref: '#/components/schemas/Lizard' ``` -which means the payload _MUST_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. In this case, a discriminator MAY act as a "hint" to shortcut validation and selection of the matching schema which may be a costly operation, depending on the complexity of the schema. We can then describe exactly which field tells us which schema to use: - +which means the payload _MUST_, by validation, match exactly one of the schemas described by `Cat`, `Dog`, or `Lizard`. Evaluating a `oneOf` can be a costly operation, so `discriminator` MAY be used as a "hint" to improve the efficiency of validation and selection of the matching schema. The `discriminator` keyword can not change the validation result of the `oneOf`, it can only help make the evaluation more efficient and provide better error messaging. We can then describe exactly which field tells us which schema is expected to match the instance: ```yaml MyResponseType: @@ -2744,7 +2744,7 @@ The expectation now is that a property with name `petType` _MUST_ be present in } ``` -Will indicate that the `Cat` schema be used in conjunction with this payload. +Will indicate that the `Cat` schema is the alternative that is expected to match this payload. In scenarios where the value of the discriminator field does not match the schema name or implicit mapping is not possible, an optional `mapping` definition MAY be used: @@ -2762,9 +2762,9 @@ MyResponseType: monster: 'https://gigantic-server.com/schemas/Monster/schema.json' ``` -Here the discriminator _value_ of `dog` will map to the schema `#/components/schemas/Dog`, rather than the default (implicit) value of `Dog`. If the discriminator _value_ does not match an implicit or explicit mapping, no schema can be determined and validation SHOULD fail. Mapping keys MUST be string values, but tooling MAY convert response values to strings for comparison. +Here the discriminator _value_ of `dog` will map to the schema `#/components/schemas/Dog`, rather than the default (implicit) `#/components/schemas/dog`. If the discriminator _value_ does not match an implicit or explicit mapping, no schema can be determined and validation SHOULD fail. Mapping keys MUST be string values, but tooling MAY convert response values to strings for comparison. -When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity where multiple schemas may satisfy a single payload. +When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity for serializers/deserializers where multiple schemas may satisfy a single payload. In both the `oneOf` and `anyOf` use cases, all possible schemas MUST be listed explicitly. To avoid redundancy, the discriminator MAY be added to a parent schema definition, and all schemas comprising the parent schema in an `allOf` construct may be used as an alternate schema. @@ -2773,6 +2773,11 @@ For example: ```yaml components: schemas: + MyResponseType: + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + - $ref: '#/components/schemas/Lizard' Pet: type: object required: @@ -2810,7 +2815,12 @@ components: type: boolean ``` -a payload like this: +The `MyResponseType` schema will use the discriminator defined by the `Pet` +schema because it is part of the `Cat`, `Dog`, and `Lizard` schemas in the +`oneOf`. The behavior if not all schemas define a `discriminator` and they are +not all the same is undefined. + +Validated against the `MyResponseType` schema, a payload like this: ```json { @@ -2819,7 +2829,7 @@ a payload like this: } ``` -will indicate that the `Cat` schema be used. Likewise this schema: +will indicate that the `#/components/schemas/Cat` schema is expected to match. Likewise this payload: ```json { @@ -2828,7 +2838,7 @@ will indicate that the `Cat` schema be used. Likewise this schema: } ``` -will map to `Dog` because of the definition in the `mapping` element. +will map to `#/components/schemas/Dog` because the `dog` entry in the `mapping` element maps to `Dog` which is the schema name for `#/components/schemas/Dog`. #### XML Object From 6e8b9aff38e6ad72a39a9882cabd559b87838c39 Mon Sep 17 00:00:00 2001 From: Jason Desrosiers Date: Mon, 14 Jun 2021 11:39:27 -0700 Subject: [PATCH 2/4] Discriminator 3.2: propertyName should not be required in object The `discriminator` keyword is an annotation. It doesn't affect validation, so I would not be ideal for it to cause a failure if not used properly. This changes the behavior of `discriminator` from being an error if the propertyName is not a required property in the object to being a no-op. This is also more consistent with other JSON Schema keywords that ignore things that don't apply to them, like `properties` only applying to instances that are objects. --- versions/3.2.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/versions/3.2.0.md b/versions/3.2.0.md index 712f7545df..e2a87b8960 100644 --- a/versions/3.2.0.md +++ b/versions/3.2.0.md @@ -2735,7 +2735,7 @@ MyResponseType: propertyName: petType ``` -The expectation now is that a property with name `petType` _MUST_ be present in the response payload, and the value will correspond to the name of a schema defined in the OAS document. Thus the response payload: +If the response payload has a property with name `petType` and that value corresponds to the name of a schema defined in the OAS document and that schema is one of the schemas defined the `oneOf`, then that schema is the schema we will expect to pass the `oneOf` validation. If any of those conditions aren't met, the `discriminator` has no effect. Thus the response payload: ```json { @@ -2762,7 +2762,7 @@ MyResponseType: monster: 'https://gigantic-server.com/schemas/Monster/schema.json' ``` -Here the discriminator _value_ of `dog` will map to the schema `#/components/schemas/Dog`, rather than the default (implicit) `#/components/schemas/dog`. If the discriminator _value_ does not match an implicit or explicit mapping, no schema can be determined and validation SHOULD fail. Mapping keys MUST be string values, but tooling MAY convert response values to strings for comparison. +Here the discriminator _value_ of `dog` will map to the schema `#/components/schemas/Dog`, rather than the default (implicit) `#/components/schemas/dog`. Mapping keys MUST be string values, but tooling MAY convert response values to strings for comparison. When used in conjunction with the `anyOf` construct, the use of the discriminator can avoid ambiguity for serializers/deserializers where multiple schemas may satisfy a single payload. From 0cb1d5e2bba195d9841d21cd6c56f401308b2ac0 Mon Sep 17 00:00:00 2001 From: Jason Desrosiers Date: Mon, 14 Jun 2021 12:00:00 -0700 Subject: [PATCH 3/4] Discriminator 3.2: Don't allow schema names in mapping There is no way for implementations to tell the difference between a value that is intended to be a schema name and a value that is expected to be a URI. Requiring all mappings to use URIs avoids this problem while not losing any functionality. The only consequence is that some mappings would be a little more verbose than they otherwise would have needed to be (example: `#/components/schemas/Dog` vs `Dog`). --- versions/3.2.0.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/versions/3.2.0.md b/versions/3.2.0.md index e2a87b8960..839ceab92b 100644 --- a/versions/3.2.0.md +++ b/versions/3.2.0.md @@ -2707,7 +2707,7 @@ a "name". Field Name | Type | Description ---|:---:|--- propertyName | `string` | **REQUIRED**. The name of the property in the payload that will hold the discriminator value. - mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema names or references. + mapping | Map[`string`, `string`] | An object to hold mappings between payload values and schema references. This object MAY be extended with [Specification Extensions](#specificationExtensions). @@ -2788,7 +2788,7 @@ components: discriminator: propertyName: petType mapping: - dog: Dog + dog: '#/components/schemas/Dog' Cat: allOf: - $ref: '#/components/schemas/Pet' @@ -2838,7 +2838,7 @@ will indicate that the `#/components/schemas/Cat` schema is expected to match. } ``` -will map to `#/components/schemas/Dog` because the `dog` entry in the `mapping` element maps to `Dog` which is the schema name for `#/components/schemas/Dog`. +will map to `#/components/schemas/Dog` because the `dog` entry in the `mapping` element maps to `#/components/schemas/Dog`. #### XML Object From f6ac637ac20379e5a8da7ebebdfaeb8e440118b6 Mon Sep 17 00:00:00 2001 From: Jason Desrosiers Date: Mon, 14 Jun 2021 12:09:36 -0700 Subject: [PATCH 4/4] Discriminator 3.2: discriminator doesn't affect allOf It doesn't make sense that the `discriminator` keyword would affect `allOf`. There's no point in discriminating if all schemas need to validate successfully. --- versions/3.2.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions/3.2.0.md b/versions/3.2.0.md index 839ceab92b..84bf5a1fb3 100644 --- a/versions/3.2.0.md +++ b/versions/3.2.0.md @@ -2711,7 +2711,7 @@ Field Name | Type | Description This object MAY be extended with [Specification Extensions](#specificationExtensions). -The discriminator object is legal only when using one of the composite keywords `oneOf`, `anyOf`, `allOf`. +The discriminator object is legal only when using one of the composite keywords `oneOf` and `anyOf`. If both are present with a `discriminator`, the `discriminator` may apply to both `oneOf` and `anyOf`. In OAS 3.0, a response payload MAY be described to be exactly one of any number of types: