diff --git a/src/oas.md b/src/oas.md index b81e67fe8b..1b35b81252 100644 --- a/src/oas.md +++ b/src/oas.md @@ -1134,13 +1134,13 @@ See [Appendix E](#appendix-e-percent-encoding-and-form-media-types) for a detail There are four possible parameter locations specified by the `in` field: * path - Used together with [Path Templating](#path-templating), where the parameter value is actually part of the operation's URL. This does not include the host or base path of the API. For example, in `/items/{itemId}`, the path parameter is `itemId`. -* query - Parameters that are appended to the URL. For example, in `/items?id=###`, the query parameter is `id`. +* query - Parameters that are appended to the URL with the `?` character (or for subsequent query parameters, with the `&` character). * header - Custom headers that are expected as part of the request. Note that [RFC7230](https://tools.ietf.org/html/rfc7230#section-3.2) states header names are case insensitive. * cookie - Used to pass a specific cookie value to the API. ##### Fixed Fields -The rules for serialization of the parameter are specified in one of two ways. +The rules for serialization and deserialization of the parameter are specified in one of two ways. Parameter Objects MUST include either a `content` field or a `schema` field, but not both. See [Appendix B](#appendix-b-data-type-conversion) for a discussion of converting values of various types to string representations. @@ -1177,7 +1177,7 @@ Serializing with `schema` is NOT RECOMMENDED for `in: "cookie"` parameters; see | style | `string` | Describes how the parameter value will be serialized depending on the type of the parameter value. Default values (based on value of `in`): for `"query"` - `"form"`; for `"path"` - `"simple"`; for `"header"` - `"simple"`; for `"cookie"` - `"form"`. | | explode | `boolean` | When this is true, parameter values of type `array` or `object` generate separate parameters for each value of the array or key-value pair of the map. For other types of parameters this field has no effect. When [`style`](#parameter-style) is `"form"`, the default value is `true`. For all other styles, the default value is `false`. Note that despite `false` being the default for `deepObject`, the combination of `false` with `deepObject` is undefined. | | allowReserved | `boolean` | When this is true, parameter values are serialized using reserved expansion, as defined by [RFC6570](https://datatracker.ietf.org/doc/html/rfc6570#section-3.2.3), which allows [RFC3986's reserved character set](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2), as well as percent-encoded triples, to pass through unchanged, while still percent-encoding all other disallowed characters (including `%` outside of percent-encoded triples). Applications are still responsible for percent-encoding reserved characters that are [not allowed in the query string](https://datatracker.ietf.org/doc/html/rfc3986#section-3.4) (`[`, `]`, `#`), or have a special meaning in `application/x-www-form-urlencoded` (`-`, `&`, `+`); see [URL Percent-Encoding](#url-percent-encoding) for details. This field only applies to parameters with an `in` value of `query`. The default value is `false`. | -| schema | [Schema Object](#schema-object) | The schema defining the type used for the parameter. | +| schema | [Schema Object](#schema-object) | The schema defining the type and other constraints used for the parameter. | | example | Any | Example of the parameter's potential value; see [Working With Examples](#working-with-examples). | | examples | Map[ `string`, [Example Object](#example-object) \| [Reference Object](#reference-object)] | Examples of the parameter's potential value; see [Working With Examples](#working-with-examples). | @@ -1277,14 +1277,14 @@ The following table shows serialized examples, as would be shown with the `examp | label | true | _empty_ | .blue | .blue.black.brown | .R=100.G=200.B=150 | | simple | false | _empty_ | blue | blue,black,brown | R,100,G,200,B,150 | | simple | true | _empty_ | blue | blue,black,brown | R=100,G=200,B=150 | -| form | false | color= | color=blue | color=blue,black,brown | color=R,100,G,200,B,150 | -| form | true | color= | color=blue | color=blue&color=black&color=brown | R=100&G=200&B=150 | -| spaceDelimited | false | _n/a_ | _n/a_ | color=blue%20black%20brown | color=R%20100%20G%20200%20B%20150 | +| form | false | color= | color=blue | color=blue,black,brown | color=R,100,G,200,B,150 | +| form | true | color= | color=blue | color=blue&color=black&color=brown | R=100&G=200&B=150 | +| spaceDelimited | false | _n/a_ | _n/a_ | color=blue%20black%20brown | color=R%20100%20G%20200%20B%20150 | | spaceDelimited | true | _n/a_ | _n/a_ | _n/a_ | _n/a_ | -| pipeDelimited | false | _n/a_ | _n/a_ | color=blue%7Cblack%7Cbrown | color=R%7C100%7CG%7C200%7CB%7C150 | +| pipeDelimited | false | _n/a_ | _n/a_ | color=blue%7Cblack%7Cbrown | color=R%7C100%7CG%7C200%7CB%7C150 | | pipeDelimited | true | _n/a_ | _n/a_ | _n/a_ | _n/a_ | | deepObject | false | _n/a_ | _n/a_ | _n/a_ | _n/a_ | -| deepObject | true | _n/a_ | _n/a_ | _n/a_ | color%5BR%5D=100&color%5BG%5D=200&color%5BB%5D=150 | +| deepObject | true | _n/a_ | _n/a_ | _n/a_ | color%5BR%5D=100&color%5BG%5D=200&color%5BB%5D=150 | ##### Parameter Object Examples @@ -4361,7 +4361,7 @@ parameters: type: string ``` -This example is equivalent to RFC6570's `{?foo*,bar}`, and **NOT** `{?foo*}{&bar}`. The latter is problematic because if `foo` is not defined, the result will be an invalid URI. +This example is equivalent to RFC6570's `{?foo*,bar}`, and **NOT** `{?foo*}{&bar}`. The latter is problematic because if `foo` is not defined (see [RFC6570 ยง2.3](https://www.rfc-editor.org/rfc/rfc6570#section-2.3) for details on what is considered undefined), the result will be an invalid URI. The `&` prefix operator has no equivalent in the Parameter Object. Note that RFC6570 does not specify behavior for compound values beyond the single level addressed by `explode`. The result of using objects or arrays where no behavior is clearly specified for them is implementation-defined. @@ -4423,6 +4423,7 @@ parameters: type: array items: type: string + explode: false ``` This translates to the following URI Template: diff --git a/src/schemas/validation/schema.yaml b/src/schemas/validation/schema.yaml index f4059cb1d3..565fe8545f 100644 --- a/src/schemas/validation/schema.yaml +++ b/src/schemas/validation/schema.yaml @@ -359,7 +359,6 @@ $defs: - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header' - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query' - $ref: '#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie' - - $ref: '#/$defs/styles-for-form' $defs: styles-for-path: @@ -369,6 +368,8 @@ $defs: const: path then: properties: + name: + pattern: '^[^{}]+$' style: default: simple enum: @@ -377,6 +378,8 @@ $defs: - simple required: const: true + explode: + default: false required: - required @@ -390,6 +393,8 @@ $defs: style: default: simple const: simple + explode: + default: false styles-for-query: if: @@ -408,6 +413,7 @@ $defs: allowReserved: default: false type: boolean + $ref: '#/$defs/explode-for-form' styles-for-cookie: if: @@ -419,6 +425,7 @@ $defs: style: default: form const: form + $ref: '#/$defs/explode-for-form' $ref: '#/$defs/specification-extensions' unevaluatedProperties: false @@ -508,6 +515,7 @@ $defs: properties: allowReserved: default: false + $ref: '#/$defs/explode-for-form' explode: properties: style: @@ -518,9 +526,8 @@ $defs: properties: style: default: form - allOf: - - $ref: '#/$defs/specification-extensions' - - $ref: '#/$defs/styles-for-form' + $ref: '#/$defs/explode-for-form' + $ref: '#/$defs/specification-extensions' unevaluatedProperties: false responses: @@ -956,13 +963,12 @@ $defs: additionalProperties: type: string - styles-for-form: + explode-for-form: + $comment: for encoding objects, and query and cookie parameters, style=form is the default if: properties: style: const: form - required: - - style then: properties: explode: diff --git a/tests/schema/fail/header-object-allowReserved.yaml b/tests/schema/fail/header-object-allowReserved.yaml new file mode 100644 index 0000000000..0babaf22df --- /dev/null +++ b/tests/schema/fail/header-object-allowReserved.yaml @@ -0,0 +1,12 @@ +openapi: 3.1.0 +info: + title: "allowReserved only permitted with in: query" + version: 1.0.0 +components: + headers: + Style: + schema: + type: array + style: simple + explode: true + allowReserved: true diff --git a/tests/schema/fail/parameter-object-cookie-form-allowReserved.yaml b/tests/schema/fail/parameter-object-cookie-form-allowReserved.yaml new file mode 100644 index 0000000000..ee88d3ac79 --- /dev/null +++ b/tests/schema/fail/parameter-object-cookie-form-allowReserved.yaml @@ -0,0 +1,18 @@ +openapi: 3.1.0 +info: + title: allowReserved only permitted with in and style values that percent-encode + version: 1.0.0 +components: + parameters: + style_form: + name: my_form_cookie + in: cookie + # default style is form, therefore allowReserved is allowed + allowReserved: true + schema: {} + style_cookie: + name: my_cookie_cookie + in: cookie + style: cookie + # no percent decoding for style=cookie, therefore allowReserved is not allowed + schema: {} diff --git a/tests/schema/fail/parameter-object-header-allowReserved.yaml b/tests/schema/fail/parameter-object-header-allowReserved.yaml new file mode 100644 index 0000000000..4b956a080d --- /dev/null +++ b/tests/schema/fail/parameter-object-header-allowReserved.yaml @@ -0,0 +1,11 @@ +openapi: 3.1.0 +info: + title: allowReserved only permitted with in and style values that percent-encode + version: 1.0.0 +components: + parameters: + header: + name: my-header + in: header + allowReserved: false + schema: {} diff --git a/tests/schema/fail/parameter-object-path-allowReserved.yaml b/tests/schema/fail/parameter-object-path-allowReserved.yaml new file mode 100644 index 0000000000..09a5f94a86 --- /dev/null +++ b/tests/schema/fail/parameter-object-path-allowReserved.yaml @@ -0,0 +1,11 @@ +openapi: 3.1.0 +info: + title: "allowReserved only permitted with in: query" + version: 1.0.0 +components: + parameters: + path: + name: my-path + in: path + allowReserved: false + schema: {} diff --git a/tests/schema/failparameter-object-path-allowReserved.yaml b/tests/schema/failparameter-object-path-allowReserved.yaml new file mode 100644 index 0000000000..1d01ac62e0 --- /dev/null +++ b/tests/schema/failparameter-object-path-allowReserved.yaml @@ -0,0 +1,12 @@ +openapi: 3.1.0 +info: + title: api + version: 1.0.0 +components: + parameters: + path: + name: my-path + in: path + required: true + allowReserved: false + schema: {} diff --git a/tests/schema/pass/header-object-examples.yaml b/tests/schema/pass/header-object-examples.yaml index 7b91efbbae..305e598486 100644 --- a/tests/schema/pass/header-object-examples.yaml +++ b/tests/schema/pass/header-object-examples.yaml @@ -22,4 +22,4 @@ components: schema: type: array style: simple - explode: true \ No newline at end of file + explode: true diff --git a/tests/schema/pass/parameter-object-examples.yaml b/tests/schema/pass/parameter-object-examples.yaml index fe6a13ea7c..30c7b29058 100644 --- a/tests/schema/pass/parameter-object-examples.yaml +++ b/tests/schema/pass/parameter-object-examples.yaml @@ -9,21 +9,23 @@ paths: in: header description: token to be passed as a header required: true + explode: false schema: type: array items: type: integer format: int64 style: simple - - name: username + - name: usernames in: path - description: username to fetch + description: usernames to fetch required: true + explode: false schema: - type: string + type: array - name: id in: query - description: ID of the object to fetch + description: IDs of the object to fetch required: false schema: type: array @@ -51,4 +53,9 @@ paths: lat: type: number long: - type: number \ No newline at end of file + type: number + - in: cookie + name: my_cookie1 + style: form + explode: false + schema: {} diff --git a/tests/schema/pass/parameter-object-query-allowReserved.yaml b/tests/schema/pass/parameter-object-query-allowReserved.yaml new file mode 100644 index 0000000000..26f89ab781 --- /dev/null +++ b/tests/schema/pass/parameter-object-query-allowReserved.yaml @@ -0,0 +1,11 @@ +openapi: 3.1.0 +info: + title: "allowReserved only permitted with in: query" + version: 1.0.0 +components: + parameters: + my_query: + name: my_query + in: query + allowReserved: true + schema: {} diff --git a/tests/schema/pass/style-defaults.yaml b/tests/schema/pass/style-defaults.yaml new file mode 100644 index 0000000000..c3942bdc50 --- /dev/null +++ b/tests/schema/pass/style-defaults.yaml @@ -0,0 +1,102 @@ +openapi: 3.1.0 +info: + title: various permutations of parameter objects, with non-required values left to their defaults + version: 1.0.0 +components: + parameters: + encoding_object_defaults: + name: encoding_object_defaults + in: path + content: + encoding_object_defaults: # media type name + encoding: + no_styles: # property name + x-comment: "style, explode and allowReserved are not present, so contentType is used; no defaults expected as default contentType cannot be determined by the schema" + style_form: + x-comment: "expecting defaults: explode=true, allowReserved=false" + style: form + style_spaceDelimited: + x-comment: "expecting defaults: explode=false, allowReserved=false" + style: spaceDelimited + explode: + x-comment: "expecting defaults: style=form, allowReserved=false" + explode: false + allowReserved: + x-comment: "expecting default: style=form, explode=true" + allowReserved: true + path_media_type: + x-comment: "expecting defaults: deprecated=false" + name: path_media-type + in: path + required: true + content: + text/*: + schema: {} + path_simple: + x-comment: "expecting defaults: deprecated=false, style=simple, explode=false, allowReserved=false" + name: path_simple + in: path + required: true + schema: {} + path_matrix: + x-comment: "expecting defaults: deprecated=false, explode=false, allowReserved=false" + name: path_matrix + in: path + required: true + style: matrix + schema: {} + path_label: + x-comment: "expecting defaults: deprecated=false, explode=false, allowReserved=false" + name: path_label + in: path + required: true + style: label + schema: {} + query_media_type: + x-comment: "expecting defaults: required=false, deprecated=false, allowEmptyValue=false" + name: query_media_type + in: query + content: + text/*: + schema: {} + query_form: + x-comment: "expecting defaults: required=false, deprecated=false, allowEmptyValue=false, style=form, explode=true, allowReserved=false" + name: query_form + in: query + schema: {} + query_spaceDelimited: + x-comment: "expecting defaults: required=false, deprecated=false, allowEmptyValue=false, explode=false, allowReserved=false" + name: query_spaceDelimited + in: query + style: spaceDelimited + schema: {} + query_pipeDelimited: + x-comment: "expecting defaults: required=false, deprecated=false, allowEmptyValue=false, explode=false, allowReserved=false" + name: query_pipeDelimited + in: query + style: pipeDelimited + schema: {} + query_deepObject: + x-comment: "expecting defaults: required=false, deprecated=false, allowEmptyValue=false, allowReserved=false" + name: query_deepObject + in: query + style: deepObject + schema: {} + header: + x-comment: "expecting defaults: deprecated=false, style=simple, explode=false, allowReserved=false" + name: header + in: path + required: true + schema: {} + cookie_media_type: + x-comment: "expecting defaults: required=false, deprecated=false" + name: cookie_media_type + in: cookie + content: + text/*: + schema: {} + cookie_form: + x-comment: "expecting defaults: required=false, deprecated=false, style=form, explode=true, allowReserved=false" + name: cookie_form + in: cookie + schema: {}