diff --git a/examples/v3.0/callback-example.yaml b/examples/v3.0/callback-example.yaml index 426ee39a76..c698b9704c 100644 --- a/examples/v3.0/callback-example.yaml +++ b/examples/v3.0/callback-example.yaml @@ -32,7 +32,7 @@ paths: onData: # when data is sent, it will be sent to the `callbackUrl` provided # when making the subscription PLUS the suffix `/data` - $request.query.callbackUrl/data: + {$request.query.callbackUrl}/data: post: requestBody: description: subscription payload diff --git a/examples/v3.0/link-example.yaml b/examples/v3.0/link-example.yaml new file mode 100644 index 0000000000..5837d705ee --- /dev/null +++ b/examples/v3.0/link-example.yaml @@ -0,0 +1,203 @@ +openapi: 3.0.0 +info: + title: Link Example + version: 1.0.0 +paths: + /2.0/users/{username}: + get: + operationId: getUserByName + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + '200': + description: The User + content: + application/json: + schema: + $ref: '#/components/schemas/user' + links: + userRepositories: + $ref: '#/components/links/UserRepositories' + /2.0/repositories/{username}: + get: + operationId: getRepositoriesByOwner + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + '200': + description: repositories owned by the supplied user + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/repository' + links: + userRepository: + $ref: '#/components/links/UserRepository' + /2.0/repositories/{username}/{slug}: + get: + operationId: getRepository + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + responses: + '200': + description: The repository + content: + application/json: + schema: + $ref: '#/components/schemas/repository' + links: + repositoryPullRequests: + $ref: '#/components/links/RepositoryPullRequests' + /2.0/repositories/{username}/{slug}/pullrequests: + get: + operationId: getPullRequestsByRepository + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: state + in: query + schema: + type: string + enum: + - open + - merged + - declined + responses: + '200': + description: an array of pull request objects + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/pullrequest' + /2.0/repositories/{username}/{slug}/pullrequests/{pid}: + get: + operationId: getPullRequestsById + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: pid + in: path + required: true + schema: + type: string + responses: + '200': + description: a pull request object + content: + application/json: + schema: + $ref: '#/components/schemas/pullrequest' + links: + pullRequestMerge: + $ref: '#/components/links/PullRequestMerge' + /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge: + post: + operationId: mergePullRequest + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: slug + in: path + required: true + schema: + type: string + - name: pid + in: path + required: true + schema: + type: string + responses: + '204': + description: the PR was successfully merged +components: + links: + UserRepositories: + # returns array of '#/components/schemas/repository' + operationId: getRepositoriesByOwner + parameters: + username: $response.body#/username + UserRepository: + # returns '#/components/schemas/repository' + operationId: getRepository + parameters: + username: $response.body#/owner/username + slug: $response.body#/slug + RepositoryPullRequests: + # returns '#/components/schemas/pullrequest' + operationId: getPullRequestsByRepository + parameters: + username: $response.body#/owner/username + slug: $response.body#/slug + PullRequestMerge: + # executes /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge + operationId: mergePullRequest + parameters: + username: $response.body#/author/username + slug: $response.body#/repository/slug + pid: $response.body#/id + schemas: + user: + type: object + properties: + username: + type: string + uuid: + type: string + repository: + type: object + properties: + slug: + type: string + owner: + $ref: '#/components/schemas/user' + pullrequest: + type: object + properties: + id: + type: integer + title: + type: string + repository: + $ref: '#/components/schemas/repository' + author: + $ref: '#/components/schemas/user' diff --git a/versions/3.0.md b/versions/3.0.md index 6691a128f9..5d2e30261e 100644 --- a/versions/3.0.md +++ b/versions/3.0.md @@ -1760,10 +1760,10 @@ This object can be extended with [Specification Extensions](#specificationExtens ##### Key Expression -The key that identifies the [Path Item Object](#pathItemObject) is a variable expression that can be evaluated in the context of a runtime HTTP request/response to identify the URL for the callback request. -A simple example is `$request.body#/url`. -However, using [variable substitution](#variableSubstitution) syntax, the complete HTTP message can be accessed. -This includes accessing any part of a body that a JSON Pointer [RFC6901](https://tools.ietf.org/html/rfc6901) can reference. +The key that identifies the [Path Item Object](#pathItemObject) is a [runtime expression](#runtimeExpression) that can be evaluated in the context of a runtime HTTP request/response to identify the URL to be used for the callback request. +A simple example might be `$request.body#/url`. +However, using a [runtime expression](#runtimeExpression) the complete HTTP message can be accessed. +This includes accessing any part of a body that a JSON Pointer [RFC6901](https://tools.ietf.org/html/rfc6901) can reference. For example, given the following HTTP request: @@ -1775,7 +1775,7 @@ Content-Length: 123 { "failedUrl" : "http://clientdomain.com/failed" - "successUrls : [ + "successUrls" : [ "http://clientdomain.com/fast", "http://clientdomain.com/medium", "http://clientdomain.com/slow" @@ -1784,7 +1784,6 @@ Content-Length: 123 201 Created Location: http://example.org/subscription/1 - ``` Here are the examples of how the various expressions evaluate, assuming a the callback operation has a path parameter named `eventType` and a query parameter named `queryUrl`. @@ -1803,12 +1802,11 @@ $response.header.Location | http://example.org/subscription/1 ##### Callback Object Example -The following example shows a callback to the URL specified by the `url` parameter in the request: - +The following example shows a callback to the URL specified by the `id` and `email` property in the request body. ```yaml myWebhook: - '$request.body#/url': + 'http://notificationServer.com?transactionId={$request.body#/id}&email={$request.body#/email}': post: requestBody: description: Callback payload @@ -1905,18 +1903,17 @@ schemas: The `Link object` represents a possible design-time link for a response. The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations. -As opposed to _dynamic_ links (links provided **in** the response payload), the OAS linking mechanism does not require that link information be provided in a specific response format at runtime. +As opposed to _dynamic_ links (i.e. links provided **in** the response payload), the OAS linking mechanism does not require that link information be provided in a specific response format at runtime. -Many operations require parameters to be passed, and these MAY be dynamic depending on the response itself. For computing links, and providing instructions to execute them, [variable substitution](#variableSubstitution) is used for accessing values in a response and using them as values while invoking the linked operation. +For computing links, and providing instructions to execute them, a [runtime expression](#runtimeExpression) is used for accessing values in an operation and using them as parameters while invoking the linked operation. ##### Fixed Fields Field Name | Type | Description ---|:---:|--- -operationRef | `string` | a relative or absolute reference to an OAS operation. This field is mutually exclusive with the `operationId` field, and MUST point to the fragment of a valid OAS definition. +operationRef | `string` | a relative or absolute reference to an OAS operation. This field is mutually exclusive with the `operationId` field, and MUST point to an [Operation Object](#operationObject). operationId | `string` | the name of an _existing_, resolvable OAS operation, as defined with a unique `operationId`. This field is mutually exclusive with the `operationRef` field. Relative `operationRef` values MAY be used to locate an existing [Operation Object](#operationObject) in the OAS. -parameters | Map[`string` \| Any \| [{expression}](#variableSubstitution)] | A map representing parameters to pass to the operation as specified with `operationId` or identified via `operationRef`. The key is the parameter name to be used, whereas the value can be a constant or an expression to be evaluated and passed to the linked operation. -headers | Map[`string`, [Header Object](#headerObject) \| [Reference Object](#referenceObject)] | Maps a header name to its definition. Note that [RFC7230](https://tools.ietf.org/html/rfc7230#page-22) states header names are case insensitive. This represents the headers to pass to the linked resource. Where conflicts occur between these headers, and those defined in the related operation, these headers override. +parameters | Map[`string`, Any \| [{expression}](#runtimeExpression)] | A map representing parameters to pass to an operation as specified with `operationId` or identified via `operationRef`. The key is the parameter name to be used, whereas the value can be a constant or an expression to be evaluated and passed to the linked operation. description | `string` | a description of the link, supports [CommonMark syntax](http://spec.commonmark.org/). server | [Server Object](#serverObject) | a server object to be used by the target operation. @@ -1927,91 +1924,9 @@ In the case of an `operationId`, it MUST be unique and resolved in the scope of Because of the potential for name clashes, consider the `operationRef` syntax as the preferred method for specifications with external references. -##### Response Payload Values - -Payload values are only available in parsable response payloads which match the advertised media -type and for media types that can be referenced using a JSON Pointer fragment Id. In all cases, -if a value does _not_ exist, the parameter will be considered a `null` value (as opposed to an -empty value) and _not_ passed as a parameter to the linked resource. If a value is -required, and a parameter is not supplied, the client MAY choose not to follow the link definition. +##### Examples -##### Example - -Response payload: -```json -{ - "id": "df71a505-07bc-458a-a5c0-73c0340d1ec7", - "firstname": "Ash", - "lastname": "Williams" -} -``` - -Payload Variables: -```yaml -id: df71a505-07bc-458a-a5c0-73c0340d1ec7 -firstname: Ash -lastname: Williams -missingValue: null -``` - -If variables appear in an array, an array of variables will be extracted. -For example: - -```json -[ - { "color": "red" }, - { "color": "green" }, - { "color": "blue" } -] -``` - -will be extracted as such: - -```json -color: ["red", "green", "blue"] -``` - -The generated variables can be used in locations prescribed by the definition. - - -##### Variable Substitution -In all cases, _variables_ from request and responses MAY be substituted for link generation. -The table below provides examples of variable expressions and examples of their use in a value: - -Source Location | variable expression | example reference | notes ----|:---|:---|:--- -HTTP Method | `$method` | `/users/{$method}` | The allowable values for the `$method` will be those for the HTTP operation -Requested content type | `$request.header.accept` | `/users/3?format={$request.header.accept}` | -Request parameter | `$request.path.id` | `/users/{$request.path.id}` | Request parameters MUST be declared in the `parameters` section for the operation or they cannot be used in substitution. This includes request headers -Request body | `$request.body` | `/users/{$request.body#/user/uuid}` | For operations which accept payloads, references MAY be made to portions of the `requestBody` or the entire body itself -Request URL | `$url` | `/track?url={$url}` | -Response value | `$response.body` | `{$response.body#/uuid}` | Only the payload in the response can be accessed with the `$response` syntax. -Response header | `$response.header` | `{$response.header.Server}` | Single header values only are available - -From the request, the `parameter`s used in calling the operation are made available through the `$request` syntax. -For responses, the response payload MAY be used with the `$response` syntax. -For both requests and responses, values will be substituted in the link in sections designated with a variable expression, surrounded by curly brackets `{}`. - -The variable expression is defined by the following [ABNF](https://tools.ietf.org/html/rfc5234) syntax - -``` - expression = ( "$url" | "$method" | "$request." [ source ] | "$response." [ source ]) - source = ( header-reference | query-reference | path-reference | body-reference ) - header-reference = "header." token - query-reference = "query." name - path-reference = "path." name - body-reference = "body#" fragment - fragment = a JSON Pointer [RFC6901](https://tools.ietf.org/html/rfc6901) - name = *( char ) - char = as per [RFC7159](https://tools.ietf.org/html/rfc7159#section-7) - token = as per [RFC7230](https://tools.ietf.org/html/rfc7230#section-3.2.6) -``` - -The `name` identifier is case-sensitive, whereas `token` is not. - - -##### Request Parameter Example -Computing a link from a request operation like this: +Computing a link from a request operation where the `$request.path.id` is used to pass a request parameter to the linked operation. ```yaml paths: @@ -2020,293 +1935,136 @@ paths: - name: id in: path required: true - description: the user identifier, as userId or username + description: the user identifier, as userId schema: type: string - responses: - '200': - description: the user being returned - content: - application/json: - schema: - type: object - properties: - uuid: the unique user id - type: string - format: uuid -``` - -Can be used in a link like this: - -```yaml -Addresses: - # the target link operationId - operationId: getUserAddress - parameters: - # get the `id` field from the request path parameter named `id` - userId: '{$request.path.id}' + get: + responses: + '200': + description: the user being returned + content: + application/json: + schema: + type: object + properties: + uuid: the unique user id + type: string + format: uuid + links: + address: + # the target link operationId + operationId: getUserAddress + parameters: + # get the `id` field from the request path parameter named `id` + userId: $request.path.id + # the path item of the linked operation + /users/{userid}/address: + parameters: + - name: userid + in: path + required: true + description: the user identifier, as userId + schema: + type: string + # linked operation + get: + operationId: getUserAddress + responses: + '200': + description: the user's address ``` -Where the `$request.path.id` is the value passed in the request to `/users/{id}`. - -##### Response Payload Example - -```yaml -Addresses: - operationId: getUserAddressByUUID - parameters: - # get the `id` field from the request path parameter named `id` - userUuid: '{$response.body#/uuid}' -``` +When a runtime expression fails to evaluate, no parameter value is passed to the target operation. -And the array example: +Values from the response body can be used to drive a linked operation. ```yaml -ColorSelection: - operationId: getColorSample - parameters: - colorName: '{$response.body#/color}' +links: + address: + operationId: getUserAddressByUUID + parameters: + # get the `id` field from the request path parameter named `id` + userUuid: $response.body#/uuid ``` -Would produce three links with the `colorName` of `red`, `green`, and `blue`: - As with all links, it is at the clients' discretion to follow them, neither permissions nor the ability to make a successful call to that link is guaranteed solely by the existence of a relationship. -##### Example - -The example below shows how relationships in the BitBucket API can be represented -with the link schema. This example uses `operationId` values to link responses to -possible operations. - -```yaml -paths: - /2.0/users/{username}: - get: - operationId: getUserByName - parameters: - - name: username - in: path - required: true - schema: - type: string - responses: - '200': - description: The User - content: - application/json: - schema: - $ref: '#/components/schemas/user' - links: - userRepositories: - $ref: '#/components/links/UserRepositories' - /2.0/repositories/{username}: - get: - operationId: getRepositoriesByOwner - parameters: - - name: username - in: path - required: true - schema: - type: string - responses: - '200': - description: repositories owned by the supplied user - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/repository' - links: - userRepository: - $ref: '#/components/links/UserRepository' - /2.0/repositories/{username}/{slug}: - get: - operationId: getRepository - parameters: - - name: username - in: path - required: true - schema: - type: string - - name: slug - in: path - required: true - schema: - type: string - responses: - '200': - description: The repository - content: - application/json: - schema: - $ref: '#/components/schemas/repository' - links: - repositoryPullRequests: - $ref: '#/components/links/RepositoryPullRequests' - /2.0/repositories/{username}/{slug}/pullrequests: - get: - operationId: getPullRequestsByRepository - parameters: - - name: username - in: path - required: true - schema: - type: string - - name: slug - in: path - required: true - schema: - type: string - - name: state - in: query - schema: - type: string - enum: - - open - - merged - - declined - responses: - '200': - description: an array of pull request objects - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/pullrequest' - /2.0/repositories/{username}/{slug}/pullrequests/{pid}: - get: - operationId: getPullRequestsById - parameters: - - name: username - in: path - required: true - schema: - type: string - - name: slug - in: path - required: true - schema: - type: string - - name: pid - in: path - required: true - schema: - type: string - responses: - '200': - description: a pull request object - content: - application/json: - schema: - $ref: '#/components/schemas/pullrequest' - links: - pullRequestMerge: - $ref: '#/components/links/PullRequestMerge' - /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge: - post: - operationId: mergePullRequest - parameters: - - name: username - in: path - required: true - schema: - type: string - - name: slug - in: path - required: true - schema: - type: string - - name: pid - in: path - required: true - schema: - type: string - responses: - '204': - description: the PR was successfully merged -components: - links: - UserRepositories: - # returns array of '#/components/schemas/repository' - operationId: getRepositoriesByOwner - parameters: - username: $response.body#/username - UserRepository: - # returns '#/components/schemas/repository' - operationId: getRepository - parameters: - username: $response.body#/owner/username - slug: $response.body#/slug - RepositoryPullRequests: - # returns '#/components/schemas/pullrequest' - operationId: getPullRequestsByRepository - parameters: - username: $response.body#/owner/username - slug: $response.body#/slug - PullRequestMerge: - # executes /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge - operationId: mergePullRequest - parameters: - username: $response.body#/author/username - slug: $response.body#/repository/slug - pid: $response.body#/id - schemas: - user: - type: object - properties: - username: - type: string - uuid: - type: string - repository: - type: object - properties: - slug: - type: string - owner: - $ref: '#/components/schemas/user' - pullrequest: - type: object - properties: - id: - type: integer - title: - type: string - repository: - $ref: '#/components/schemas/repository' - author: - $ref: '#/components/schemas/user' -``` +##### OperationRef Examples As references to `operationId` MAY NOT be possible (the `operationId` is an optional value), references MAY also be made through a relative `operationRef`: ```yaml -components: - links: - UserRepositories: - # returns array of '#/components/schemas/repository' - operationRef: '#paths~12.0~1repositories~1{$response.body#/username}' +links: + UserRepositories: + # returns array of '#/components/schemas/repository' + operationRef: '#/paths~12.0~1repositories~1/{username}/get' + parameters: + username: $response.body#/username ``` or an absolute `operationRef`: ```yaml -components: - links: - UserRepositories: - # returns array of '#/components/schemas/repository' - href: 'https://na2.gigantic-server.com/#/paths/~12.0~1repositories~1{$response.body#/username}' +links: + UserRepositories: + # returns array of '#/components/schemas/repository' + operationRef: 'https://na2.gigantic-server.com/#/paths/~12.0~1repositories~1{username}/get' + parameters: + username: $response.body#/username ``` Note that in the use of `operationRef`, the _escaped forward-slash_ is necessary when using JSON references. + +##### Runtime Expressions + +Runtime expressions allow defining values based on information that will only be available within the HTTP message in an actual API call. +This mechanism is used by [Link Objects](#linkObject) and [Callback Objects](#callbackObject). + +The runtime expression is defined by the following [ABNF](https://tools.ietf.org/html/rfc5234) syntax + +``` + expression = ( "$url" | "$method" | "$statusCode" | "$request." source | "$response." source ) + source = ( header-reference | query-reference | path-reference | body-reference ) + header-reference = "header." token + query-reference = "query." name + path-reference = "path." name + body-reference = "body#" fragment + fragment = a JSON Pointer [RFC 6901](https://tools.ietf.org/html/rfc6901) + name = *( char ) + char = as per RFC [7159](https://tools.ietf.org/html/rfc7159#section-7) + token = as per RFC [7230](https://tools.ietf.org/html/rfc7230#section-3.2.6) +``` + +The `name` identifier is case-sensitive, whereas `token` is not. + +The table below provides examples of runtime expressions and examples of their use in a value: + +##### Examples + +Source Location | example expression | notes +---|:---|:---| +HTTP Method | `$method` | The allowable values for the `$method` will be those for the HTTP operation +Requested media type | `$request.header.accept` | +Request parameter | `$request.path.id` | Request parameters MUST be declared in the `parameters` section for the parent operation or they cannot be evaluated. This includes request headers. +Request body property | `$request.body#/user/uuid` | For operations which accept payloads, references may be made to portions of the `requestBody` or the entire body itself +Request URL | `$url` | +Response value | `$response.body#/status` | Only the payload in the response can be accessed with the `$response` syntax. +Response header | `$response.header.Server` | Single header values only are available + +Runtime expressions preserve the type of the referenced value. +Expression values can be embeded into string values by surrounding the expression with `{}` curly braces. + +`$request.body`and `$response.body`expressions are only available in payloads which that can be accessed using a JSON Pointer. +If a value does _not_ exist, the result of the expression will be considered a `null` value (as opposed to an empty value). + + + #### Header Object The Header Object follows the structure of the [Parameter Object](#parameterObject) with the following changes: