Skip to content

Commit

Permalink
Appendix on RFC6570-derived behavior
Browse files Browse the repository at this point in the history
This is the one of several appendixes to be added to clarify
the most obscure details of Parameter Object and Encoding Object
serialization.

This clarifies the correspondence between OAS fields and RFC6570
operators, and acknowledges that some field values and combinations
do not have analogues.  It provides further guidance for how to
use RFC6570 implementations to support these configurations.

This includes a SHOULD directive regarding using RFC6570 expansion
with the non-RFC6570 styles, as the use of "explode" and
"allowReserved" does not otherwise make any sense.  It perhaps
could be a MUST.

Examples are included to show both typical usage, and how to
work around the lack of exact RFC6570 equivalences for certain
configurations.
  • Loading branch information
handrews committed May 25, 2024
1 parent 1bfe6c3 commit 8a73f7c
Showing 1 changed file with 211 additions and 0 deletions.
211 changes: 211 additions & 0 deletions versions/3.0.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,8 @@ Field Name | Type | Description
<a name="parameterExample"></a>example | Any | Example of the parameter's potential value. The example SHOULD match the specified schema and encoding properties if present. The `example` field is mutually exclusive of the `examples` field. Furthermore, if referencing a `schema` that contains an example, the `example` value SHALL _override_ the example provided by the schema. To represent examples of media types that cannot naturally be represented in JSON or YAML, a string value can contain the example with escaping where necessary.
<a name="parameterExamples"></a>examples | Map[ `string`, [Example Object](#exampleObject) \| [Reference Object](#referenceObject)] | Examples of the parameter's potential value. Each example SHOULD contain a value in the correct format as specified in the parameter encoding. The `examples` field is mutually exclusive of the `example` field. Furthermore, if referencing a `schema` that contains an example, the `examples` value SHALL _override_ the example provided by the schema.

See also [Appendix C: Using RFC6570 Implementations](#usingRFC6570Implementations) for additional guidance.

###### Fixed Fields and considerations for use with `content`

For more complex scenarios, the [`content`](#parameterContent) property can define the media type and schema of the parameter, as well as give examples of its use.
Expand Down Expand Up @@ -1622,6 +1624,8 @@ Field Name | Type | Description

This object MAY be extended with [Specification Extensions](#specificationExtensions).

See also [Appendix C: Using RFC6570 Implementations](#usingRFC6570Implementations) for additional guidance.

##### Encoding Object Example

`multipart/form-data` allows for binary parts:
Expand Down Expand Up @@ -3523,3 +3527,210 @@ Version | Date | Notes
1.2 | 2014-03-14 | Initial release of the formal document.
1.1 | 2012-08-22 | Release of Swagger 1.1
1.0 | 2011-08-10 | First release of the Swagger Specification

## <a name="usingRFC6570Implementations"></a>Appendix C: Using RFC6570 Implementations

Serialization is defined in terms of RFC6570 URI Templates in two scenarios:

Object | Condition
------ | ---------
[Parameter Object](#parameterObject) | When `schema` is present
[Encoding Object](#encodingObject) | When encoding for `application/x-www-form-urlencoded` and any of `style`, `explode`, or `allowReserved` are used

Implementations of this specification MAY use an implementation of RFC6570 to perform variable expansion, however, some caveats apply.

### Equivalences Between Fields and RFC6570 Operators

Certain field values translate to RFC6570 operators (or lack thereof):

field | value | equivalent
----- | ----- | ----------
style | simple | _n/a_
style | matrix | `;` prefix operator
style | label | `.` prefix operator
style | form | `?` prefix operator
allowReserved | `false` | _n/a_
allowReserved | `true` | `+` prefix operator
explode | `false` | _n/a_
explode | `true` | `*` modifier suffix

Multiple `style: form` parameters are equivalent to a single RFC6570 [variable list](https://www.rfc-editor.org/rfc/rfc6570#section-2.2) using the `?` prefix operator:

```YAML
- name: foo
in: query
schema:
type: object
explode: true
- name: bar
in: query
schema:
type: string
```

This example is equivalent to RFC6570's `{?foo*,bar}`, and ***NOT*** `{?foo*}{&bar}`, which is problematic because if `foo` is not defined, the result will be an invalid URI.
The `&` prefix operator has no equivalent in the Parameter Object.

### Non-RFC6570 Field Values and Combinations

Configurations with no direct RFC6570 equivalent SHOULD also be handled according to RFC6570.
Implementations MAY create a properly delimited URI Template with variables for individual names and values using RFC6570 regular or reserved expansion (based on `allowReserved`).

This includes:
* the styles `pipeDelimited`, `spaceDelimited`, and `deepObject`, which have no equivalents at all
* the combination of the style `form` with `allowReserved: true`, which is not allowed because only one prefix operator can be used at a time
* any parameter name that is not a legal RFC6570 variable name (alphanumerics, `_`, and percent-encoded characters)

The Parameter Object's `name` field has a much more permissive syntax than [RFC6570 variable name syntax](https://www.rfc-editor.org/rfc/rfc6570#section-2.3).
A parameter name that includes characters outside of the allowed RFC6570 variable character set MUST be percent-encoded before it can be used in a URI Template, and MUST set `allowReserved: true` to avoid double percent-encoding.

### Examples

Let's say we want to use the following data in a form query string, where `formulas` is exploded, and `words` is not:

```YAML
formulas:
a: x+y
b: x/y
c: x^y
words:
- math
- is
- fun
```

#### RFC6570-Equivalent Expansion

This array of parameter objects uses regular `style: form` expansion, fully supported by RFC6570:

```YAML
parameters:
- name: formulas
in: query
schema:
type: object
additionalProperties:
type: string
explode: true
- name: words
in: query
schema:
type: array
items:
type: string
```

This translates to the following URI Template:

```urlencoded
{?formulas*,words}
```

when expanded with the data given earlier, we get:

```urlencoded
?a=x%2By&b=x%2Fy&c=x%5Ey&words=math,is,fun
```

#### Expansion With Non-RFC6570-Supported Options

But now let's say that (for some reason), we really want that `/` in the `b` formula to show up as-is in the query string, and we want our words to be space-separated like in a written phrase.
To do that, we'll add `allowReserved: true` to `formulas`, and change to `style: spaceDelimited` for `words`:

```YAML
parameters:
- name: formulas
in: query
schema:
type: object
additionalProperties
type: string
explode: true
allowReserved: true
- name: words
in: query
style: spaceDelimited
schema:
type: array
items:
type: string
```

We can't combine the `?` and `+` RFC6570 prefixes, and there's no way with RFC6570 to replace the `,` separator with a space character.
So we need to restructure the data to fit a manually constructed URI Template that passes all of the pieces through the right sort of expansion.

Here is one such template.
The exact variable names are unimportant, but in this example "f" and "w" are meant to suggest "formulas" and "words", while "n" and "v" suggest "name" and "value":

```urlencoded
?{+fn0}={+fv0}&{+fn1}={+fv1}&{+fn2}={+fv2}&{wn}={wv0} {wv1} {wv2}
```

We'll also need to pre-process the names and values for `formaulas` because while `/` and most other reserved characters are allowed in the query string by RFC3986, `[`, `]`, and `#` [are not](https://datatracker.ietf.org/doc/html/rfc3986#appendix-A), and `&`, `=`, and `+` all have [special behavior](https://www.rfc-editor.org/rfc/rfc1866#section-8.2.1) in the `application/x-www-form-urlencoded` format, which is what we are using in the query string.

Setting `allowReserved: true` does _not_ make reserved characters that are not allowed in URIs allowed, it just allows them to be _passed through expansion unchanged._
Therefore, any tooling still needs to percent-encode those characters because reserved expansion will not do it, but it _will_ leave the percent-encoded triples unchanged.
See also Appendix D for further guidance on percent-encoding and form media types, including guidance on handling the delimiter characters for `spaceDelimited`, `pipeDelimited`, and `deepObject` in parameter names and values.

So here is our data structure that arranges the names and values to suit the template above, where names and values for `formulas` have `[]#&=+` pre-percent encoded (although only `+` appears in this example):

```YAML
fn0: a
fv0: x%2By
fn1: b
fv1: x/y
fn2: c
fv2: x^y
wn: words
wv0: math
wv1: is
wv2: fun
```

Expanding our manually assembled template with our restructured data yields the following query string:

```urlencoded
?a=x%2By&b=x/y&c=x%5Ey&words=math%20is%20fun
```
The `/` and the pre-percent-encoded `%2B` have been left alone, but the disallowed `^` character (inside a value) and space characters (in the template but outside of the expanded variables) were percent-encoded.

#### Undefined Values and Manual URI Template Construction

Care must be taken when manually constructing templates to handle the values that [RFC6570 considers to be _undefined_](https://datatracker.ietf.org/doc/html/rfc6570#section-2.3) correctly:

```YAML
formulas: {}
words:
- hello
- world
```

Using this data with our original RFC6570-friendly URI Template, `{?formulas*,words}`, produces the following:


```urlencoded
?words=hello,world
```

This means that the manually constructed URI Template and restructured data need to leave out the `formulas` object entirely so that the `words` parameter is the first and only parameter in the query string.

Restructured data:

```YAML
wn: words
wv0: hello
wv1: world
```

Manually constructed URI Template:

```urlencoded
?{wn}={wv0} {wv1}
```

Result:

```urlencoded
?words=hello%20world
```

0 comments on commit 8a73f7c

Please sign in to comment.