diff --git a/.gitignore b/.gitignore index 72ed1f8..7cbe86f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ _site/ .jekyll-metadata .ruby-version node_modules -tests/**/schema.json \ No newline at end of file +tests/**/schema.json +.vscode diff --git a/examples/v3.1/tictactoe.yaml b/examples/v3.1/tictactoe.yaml index 3753791..ebf13b7 100644 --- a/examples/v3.1/tictactoe.yaml +++ b/examples/v3.1/tictactoe.yaml @@ -26,7 +26,8 @@ paths: security: - defaultApiKey: [] - app2AppOauth: - - board:read + - board:read + # Single square operations /board/{row}/{column}: parameters: @@ -45,7 +46,14 @@ paths: application/json: schema: $ref: '#/components/schemas/mark' - '400': + links: + markSquare: + description: Operation to use if the mark value returned indicates the square is empty + operationId: put-square + parameters: + row: $request.path.row + column: $request.path.column + "400": description: The provided parameters are incorrect content: text/html: @@ -55,13 +63,20 @@ paths: security: - bearerHttpAuthentication: [] - user2AppOauth: - - board:read + - board:read put: summary: Set a single board square description: Places a mark on the board and retrieves the whole board and the winner (if any). tags: - Gameplay operationId: put-square + parameters: + - name: progressUrl + in: header + description: Progress URL that should be called if asynchronous response is returned + required: false + schema: + type: string requestBody: required: true content: @@ -74,8 +89,15 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/status' - '400': + $ref: "#/components/schemas/status" + "202": + description: Mark operation has not completed. Use callback to check for progress + headers: + Location: + description: Callback URL + schema: + type: string + "400": description: The provided parameters are incorrect content: text/html: @@ -91,7 +113,22 @@ paths: security: - bearerHttpAuthentication: [] - user2AppOauth: - - board:write + - board:write + callbacks: + statusCallback: + '{$request.header.progressUrl}': + post: + summary: Status of mark operation + description: Provides the status of the mark operation + operationId: markOperationCallback + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/status" + responses: + "200": + description: Mark operation status received components: parameters: rowParam: @@ -184,3 +221,16 @@ components: # Reads and writes permitted via authorization code flow board:read: Read the board board:write: Write to the board +webhooks: + markStatus: + post: + summary: Status of mark operation + description: Provides the status of the mark operation on completion + operationId: markOperationWebhook + responses: + "200": + description: Mark operation has completed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/status" diff --git a/img/callback-object.dot b/img/callback-object.dot new file mode 100644 index 0000000..db636c4 --- /dev/null +++ b/img/callback-object.dot @@ -0,0 +1,77 @@ +digraph componentsObject { + node [shape = none; fontname = monospace; tooltip = "Click to jump to the specification of this object";]; + rankdir = LR; + bgcolor = white; + + openapiObject [URL = "https://spec.openapis.org/oas/v3.1.0#oasObject";target = "_blank";label = < + + + + + + +
OpenAPI Object
openapi
info
components
security
paths
>;]; + + componentsObject [URL = "https://spec.openapis.org/oas/v3.1.0#componentsObject";target = "_blank";label = < + + +
Components Object
callbacks
>;]; + + pathsObject [URL = "https://spec.openapis.org/oas/v3.1.0#pathsObject";target = "_blank";label = < + + + + +
Paths Object
/endpoint1
/endpoint2
/endpoint3
>;]; + + pathItemObject [URL = "https://spec.openapis.org/oas/v3.1.0#pathItemObject";target = "_blank";label = < + + + + + + +
Path Item Object
delete
get
patch
post
put
>;]; + + operationObject [URL = "https://spec.openapis.org/oas/v3.1.0#operationObject";target = "_blank";label = < + + +
Operation Object
callbacks
>;]; + + + callbackPathItemObject [URL = "https://spec.openapis.org/oas/v3.1.0#pathItemObject";target = "_blank";label = < + + + + + + +
Path Item Object
delete
get
patch
post
put
>;]; + + + callbackObject [URL = "https://spec.openapis.org/oas/v3.1.0#callbackObject";target = "_blank";label = < + + +
Callback Object
{expression}
>;]; + + openapiObject:components -> componentsObject:header; + + componentsObject:callbacks -> callbackObject:header; + + openapiObject:paths -> pathsObject:header; + + pathsObject:endpoint1 -> pathItemObject:header; + pathsObject:endpoint2 -> pathItemObject:header; + pathsObject:endpoint3 -> pathItemObject:header; + + pathItemObject:delete -> operationObject:header; + pathItemObject:get -> operationObject:header; + pathItemObject:patch -> operationObject:header; + pathItemObject:post -> operationObject:header; + pathItemObject:put -> operationObject:header; + + operationObject:callbacks -> callbackObject:header; + + + callbackObject:expression -> callbackPathItemObject:header; +} \ No newline at end of file diff --git a/img/callback-object.svg b/img/callback-object.svg new file mode 100644 index 0000000..8408996 --- /dev/null +++ b/img/callback-object.svg @@ -0,0 +1,245 @@ + + + + + + +componentsObject + + + +openapiObject + + + +OpenAPI Object + + +openapi + + +info + + +components + + +security + + +paths + + + + + + + + +componentsObject + + + +Components Object + + +callbacks + + + + + + + + +openapiObject:components->componentsObject:header + + + + + +pathsObject + + + +Paths Object + + +/endpoint1 + + +/endpoint2 + + +/endpoint3 + + + + + + + + +openapiObject:paths->pathsObject:header + + + + + +callbackObject + + + +Callback Object + + +{expression} + + + + + + + + +componentsObject:callbacks->callbackObject:header + + + + + +pathItemObject + + + +Path Item Object + + +delete + + +get + + +patch + + +post + + +put + + + + + + + + +pathsObject:endpoint1->pathItemObject:header + + + + + +pathsObject:endpoint2->pathItemObject:header + + + + + +pathsObject:endpoint3->pathItemObject:header + + + + + +operationObject + + + +Operation Object + + +callbacks + + + + + + + + +pathItemObject:delete->operationObject:header + + + + + +pathItemObject:get->operationObject:header + + + + + +pathItemObject:patch->operationObject:header + + + + + +pathItemObject:post->operationObject:header + + + + + +pathItemObject:put->operationObject:header + + + + + +operationObject:callbacks->callbackObject:header + + + + + +callbackPathItemObject + + + +Path Item Object + + +delete + + +get + + +patch + + +post + + +put + + + + + + + + +callbackObject:expression->callbackPathItemObject:header + + + + + diff --git a/img/link-object.dot b/img/link-object.dot new file mode 100644 index 0000000..f141f2a --- /dev/null +++ b/img/link-object.dot @@ -0,0 +1,79 @@ +digraph componentsObject { + node [shape = none; fontname = monospace; tooltip = "Click to jump to the specification of this object";]; + rankdir = LR; + bgcolor = white; + + openapiObject [URL = "https://spec.openapis.org/oas/v3.1.0#oasObject";target = "_blank";label = < + + + + + + +
OpenAPI Object
openapi
info
components
security
paths
>;]; + + componentsObject [URL = "https://spec.openapis.org/oas/v3.1.0#componentsObject";target = "_blank";label = < + + +
Components Object
links
>;]; + + pathsObject [URL = "https://spec.openapis.org/oas/v3.1.0#pathsObject";target = "_blank";label = < + + + + +
Paths Object
/endpoint1
/endpoint2
/endpoint3
>;]; + + pathItemObject [URL = "https://spec.openapis.org/oas/v3.1.0#pathItemObject";target = "_blank";label = < + + + + + + +
Path Item Object
delete
get
patch
post
put
>;]; + + operationObject [URL = "https://spec.openapis.org/oas/v3.1.0#operationObject";target = "_blank";label = < + + +
Operation Object
responses
>;]; + + responsesObject [URL = "https://spec.openapis.org/oas/v3.1.0#responsesObject";target = "_blank";label = < + + +
Responses Object
*
>;]; + + responseObject [URL = "https://spec.openapis.org/oas/v3.1.0#responseObject";target = "_blank";label = < + + +
Response Object
links
>;]; + + linkObject [URL = "https://spec.openapis.org/oas/v3.1.0#linkObject";target = "_blank";label = < + + + + + + +
Link Object
operationRef
operationId
parameters
requestBody
description
server
>;]; + + openapiObject:components -> componentsObject:header; + + componentsObject:links -> linkObject:header; + + openapiObject:paths -> pathsObject:header; + + pathsObject:endpoint1 -> pathItemObject:header; + pathsObject:endpoint2 -> pathItemObject:header; + pathsObject:endpoint3 -> pathItemObject:header; + + pathItemObject:delete -> operationObject:header; + pathItemObject:get -> operationObject:header; + pathItemObject:patch -> operationObject:header; + pathItemObject:post -> operationObject:header; + pathItemObject:put -> operationObject:header; + + operationObject:responses -> responsesObject:header; + responsesObject:all -> responseObject:header; + responseObject:links -> linkObject:header; +} \ No newline at end of file diff --git a/img/link-object.svg b/img/link-object.svg new file mode 100644 index 0000000..1a9f4db --- /dev/null +++ b/img/link-object.svg @@ -0,0 +1,267 @@ + + + + + + +componentsObject + + + +openapiObject + + + +OpenAPI Object + + +openapi + + +info + + +components + + +security + + +paths + + + + + + + + +componentsObject + + + +Components Object + + +links + + + + + + + + +openapiObject:components->componentsObject:header + + + + + +pathsObject + + + +Paths Object + + +/endpoint1 + + +/endpoint2 + + +/endpoint3 + + + + + + + + +openapiObject:paths->pathsObject:header + + + + + +linkObject + + + +Link Object + + +operationRef + + +operationId + + +parameters + + +requestBody + + +description + + +server + + + + + +componentsObject:links->linkObject:header + + + + + +pathItemObject + + + +Path Item Object + + +delete + + +get + + +patch + + +post + + +put + + + + + + + + +pathsObject:endpoint1->pathItemObject:header + + + + + +pathsObject:endpoint2->pathItemObject:header + + + + + +pathsObject:endpoint3->pathItemObject:header + + + + + +operationObject + + + +Operation Object + + +responses + + + + + + + + +pathItemObject:delete->operationObject:header + + + + + +pathItemObject:get->operationObject:header + + + + + +pathItemObject:patch->operationObject:header + + + + + +pathItemObject:post->operationObject:header + + + + + +pathItemObject:put->operationObject:header + + + + + +responsesObject + + + +Responses Object + + +* + + + + + + + + +operationObject:responses->responsesObject:header + + + + + +responseObject + + + +Response Object + + +links + + + + + + + + +responsesObject:all->responseObject:header + + + + + +responseObject:links->linkObject:header + + + + + diff --git a/img/webhook-object.dot b/img/webhook-object.dot new file mode 100644 index 0000000..8c4b293 --- /dev/null +++ b/img/webhook-object.dot @@ -0,0 +1,29 @@ +digraph componentsObject { + node [shape = none; fontname = monospace; tooltip = "Click to jump to the specification of this object";]; + rankdir = LR; + bgcolor = white; + + openapiObject [URL = "https://spec.openapis.org/oas/v3.1.0#oasObject";target = "_blank";label = < + + + + + + + +
OpenAPI Object
openapi
info
components
security
paths
webhooks
>;]; + + + + pathItemObject [URL = "https://spec.openapis.org/oas/v3.1.0#pathItemObject";target = "_blank";label = < + + + + + + +
Path Item Object
delete
get
patch
post
put
>;]; + + + openapiObject:webhooks -> pathItemObject:header; +} \ No newline at end of file diff --git a/img/webhook-object.svg b/img/webhook-object.svg new file mode 100644 index 0000000..1defb96 --- /dev/null +++ b/img/webhook-object.svg @@ -0,0 +1,78 @@ + + + + + + +componentsObject + + + +openapiObject + + + +OpenAPI Object + + +openapi + + +info + + +components + + +security + + +paths + + +webhooks + + + + + + + + +pathItemObject + + + +Path Item Object + + +delete + + +get + + +patch + + +post + + +put + + + + + + + + +openapiObject:webhooks->pathItemObject:header + + + + + diff --git a/index.md b/index.md index 329daa5..b9f2e16 100644 --- a/index.md +++ b/index.md @@ -5,6 +5,7 @@ nav_order: 1 --- # Getting started + ## Intended Audience This guide is directed at **HTTP-based API** designers and writers wishing to benefit from having their API formalized in an **OpenAPI Description** (**OAD**). @@ -29,7 +30,7 @@ Having your API formally described in a machine-readable format allows automated - **Documentation Generation**: Create traditional human-readable documentation based on the machine-readable description, which always stays up-to-date. - **Code Generation**: Create both server and client code in any programming language, freeing developers from having to perform data validation or write SDK glue code, for example. - **Graphical Editors**: Allow easy creation of description files using a GUI instead of typing them by hand. -- **Mock Servers**: Create fake servers providing example responses which you and your customers can start testing with before you write a single line of code. +- **Mock Servers**: Create fake servers providing example responses that you and your customers can start testing with before you write a single line of code. - **Security Analysis**: Discover possible vulnerabilities at the API design stage instead of much, much later. On top of this, the **OpenAPI Specification** also provides you with: diff --git a/specification/callbacks.md b/specification/callbacks.md new file mode 100644 index 0000000..1a6d928 --- /dev/null +++ b/specification/callbacks.md @@ -0,0 +1,83 @@ +--- +layout: default +title: Providing Callbacks +parent: The OpenAPI Specification Explained +nav_order: 11 +--- + +# Providing Callbacks + +Callbacks describe an asynchronous request pattern where the API consumer makes a request to the API provider and includes a URL where the response should be sent. The API provider sends an immediate acknowledgement, then when the result is ready, sends it as a request to the URL the consumer supplied. +The server can use the contents of the Callback and the dynamic values received during a given Operation to make the followup call back to the client (hence the name "callback") at the URL provided. + +
+ +
Callbacks are implemented in an Operation and also be described by reference using a Reference Object.
+
+ +A common use case for callbacks is to provide information on the success or failure of long-running operations that cannot be completed synchronously. An example is in the context of payments API, where payments backend systems are asynchronous and therefore cannot always return a definitive response within the typical timeout period for an HTTP request. Callbacks are therefore implemented to allow an immediate, synchronous response to be made so that payment API providers can relay the status of the payment out-of-band. A Callback object is defined as part of an Operation. It differs from a [Webhook](webhooks.md) in that it is always allied to a specific Operation. Webhooks should be considered for more generic use cases as they define Path Items outside the context of a given Operation. + +Our Tic Tac Toe example implements a Response object that indicates an asynchronous response to a requested move on the board. A 202 HTTP response is returned indicating the request has been accepted but the operation is not completed. The API consumer can send a URL in the HTTP header `progressUrl` so the API provider can send a notification when the move is complete. This header is optional, so if no value is supplied no callback will be made by the API provider: + +```yaml +put: + summary: Set a single board square + description: Places a mark on the board and retrieves the whole board and the winner (if any). + tags: + - Gameplay + operationId: put-square + parameters: + - name: progressUrl + in: header + description: Progress URL that should be called if asynchronous response is returned + required: false + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/mark" + responses: + "200": + description: "OK" + content: + application/json: + schema: + $ref: "#/components/schemas/status" + "202": + description: Mark operation has not completed. Callback will be used to send notification once operation is complete +``` + +This operation also describes the callback, which is implemented as a [Runtime Expression](https://spec.openapis.org/oas/v3.1.0#runtime-expressions) that points at the `progressUrl` header from the parameter above. The `callbacks` object is defined as a Map. Each callback is defined with a name that uniquely identifies the Callback object and the runtime expression value: + +```yaml +callbacks: + statusCallback: + "{$request.header.progressUrl}": + post: + summary: Status of mark operation + description: Provides the status of the ongoing mark operation + operationId: markOperationCallback + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/status" + responses: + "200": + description: Mark operation status received +``` + +The object defines or references a Path Item, qualified by the Runtime Expression. The API provider will use this Runtime Expression to resolve the value of the URL to call. The API consumer uses this object to define the implementation code for their callback API and perform activities like request payload schema validation. + +The behaviors that govern when an API provider should call a callback URL are largely based on the type of operation being supported and the specifics of the implementation. We therefore do not cover such considerations in this topic. + +## Summary + +In this topic we've learned that: + +- Callbacks provide the means to associate a given Operation with a Callback that provides additional, out-of-band information. +- An API provider can use Callbacks to support features like long-running operations that are completed asynchronously. +- API providers can resolve a Callback URL using a Runtime Expression, which resolves to attributes of the request or response messages and allows them to call the API consumer at the resolved URL. diff --git a/specification/components.md b/specification/components.md index 7a23bef..ba7ca9d 100644 --- a/specification/components.md +++ b/specification/components.md @@ -2,7 +2,7 @@ layout: default title: Reusing Description parent: The OpenAPI Specification Explained -nav_order: 5 +nav_order: 6 --- # Reusing Descriptions diff --git a/specification/content.md b/specification/content.md index 12ef6f3..27b0308 100644 --- a/specification/content.md +++ b/specification/content.md @@ -2,7 +2,7 @@ layout: default title: Content of Message Bodies parent: The OpenAPI Specification Explained -nav_order: 3 +nav_order: 4 --- # Content of Message Bodies diff --git a/specification/docs.md b/specification/docs.md index fcc317c..edac246 100644 --- a/specification/docs.md +++ b/specification/docs.md @@ -2,7 +2,7 @@ layout: default title: Providing Documentation and Examples parent: The OpenAPI Specification Explained -nav_order: 6 +nav_order: 7 --- # Providing Documentation and Examples diff --git a/specification/http-methods.md b/specification/http-methods.md index f72c8bf..68d977f 100644 --- a/specification/http-methods.md +++ b/specification/http-methods.md @@ -2,7 +2,7 @@ layout: default title: HTTP Methods parent: The OpenAPI Specification Explained -nav_order: 10 +nav_order: 3 --- # HTTP Methods diff --git a/specification/index.md b/specification/index.md index bedf9c3..ba07e7a 100644 --- a/specification/index.md +++ b/specification/index.md @@ -10,12 +10,19 @@ has_toc: false The [OpenAPI Specification](https://spec.openapis.org/oas/latest) is the ultimate source of knowledge regarding this API description format. However, its length is daunting to newcomers and makes it hard for experienced users to find specific bits of information. This chapter provides a soft landing for readers not yet familiar with OpenAPI and is organized by topic, simplifying browsing. -The following pages introduce the syntax and structure of an OpenAPI Description (OAD), its main building blocks and a minimal API description. Afterwards, the different blocks are detailed, starting from the most common and progressing towards advanced ones. +The following pages introduce the syntax and structure of an OpenAPI Description (OAD), its main building blocks, and a minimal API description. The different blocks are then detailed, starting from the most common and progressing towards advanced ones. -- [Structure of an OpenAPI Description](structure): JSON, YAML, `openapi` and `info` +- [Structure of an OpenAPI Description](structure): JSON, YAML, `openapi`, and `info`. - [API Endpoints](paths): `paths` and `responses`. +- [HTTP Methods](http-methods): standard and custom HTTP methods. - [Content of Message Bodies](content): `content` and `schema`. - [Parameters and Payload of an Operation](parameters): `parameters` and `requestBody`. - [Reusing Descriptions](components): `components` and `$ref`. - [Providing Documentation and Examples](docs): `summary`, `description` and `example`/`examples`. - [API Servers](servers): `servers`. +- [Describing API Security](security): `securitySchemes` and `security`. +- [Enhanced Tags](tags): `tags`. +- [Providing Callbacks](callbacks): `callbacks`. +- [Providing Webhooks](webhooks): `webhooks`. +- [Implementing Links](links): `links`. +- [Sequential Media Types](media-types): `itemSchema` and streaming media types. diff --git a/specification/links.md b/specification/links.md new file mode 100644 index 0000000..91aabc3 --- /dev/null +++ b/specification/links.md @@ -0,0 +1,61 @@ +--- +layout: default +title: Implementing Links +parent: The OpenAPI Specification Explained +nav_order: 13 +--- + +# Implementing Links + +As the API economy has grown the number of ways to indicate a relationship between one-or-more operations has grown with it. Most approaches equate to something loosely resembling the REST constraint of hypermedia as the engine of state (HATEOAS), and this has been replayed in many standards-based approaches like HAL and `json:api`. + +There has, however, always been a need to _describe_ how the relationships are manifested and how to process such information. This is because the promise of dynamically consuming links returned from an API has rarely been born out in the practicalities of both publishing APIs and software development for API consumers. The Link Object is a feature of OAS that attempts to address this. Links are designed to describe a relationship between two operations _at design time_. + +
+ +
Links are implemented in an Response and also be described by reference using a Reference Object.
+
+ +A Link provides a reference to another operation through either an `operationRef` - which uses [References](../referencing/) to resolve a target operation - or through an unqualified `operationId` value that must be resolved to an Operation object locally. Links allow API providers to decouple the data they return in response payloads from properties specific to resolving a relationship, a pattern that is generally manifested in returning URLs in the response payload encapsulated in properties like `links`. The Link object instead uses [Runtime Expressions](https://spec.openapis.org/oas/v3.1.0#runtime-expressions) to provide the relationship, which can reference any part of the request, response, and the headers and parameters therein. Runtime Expressions are important in this context as they allow the binding of design-time information to runtime data in a very flexible way. A Link can also implement a specific [Server object](https://spec.openapis.org/oas/v3.1.0#server-object) that is only applicable to that Link. + +Our Tic Tac Toe description includes a very simple example of a Link object that indicates a relationship between the `get` and `put` Operations on the Path Item `/board/{row}/{column}`. The Link object is a property of a Response object, and is therefore always qualified by a specific HTTP return code from a given Operation: + +```yaml +/board/{row}/{column}: + parameters: + - $ref: "#/components/parameters/rowParam" + - $ref: "#/components/parameters/columnParam" + get: + summary: Get a single board square + description: Retrieves the requested square. + tags: + - Gameplay + operationId: get-square + responses: + "200": + description: "OK" + content: + application/json: + schema: + $ref: "#/components/schemas/mark" + links: + markSquare: + description: Operation to use if the mark value returned indicates the square is empty + operationId: put-square + parameters: + row: $request.path.row + column: $request.path.column +``` + +The Link object indicates a relationship to the `put-square` operation and identifies the required `row` and `column` parameters through Runtime Expressions referencing the same values in the request. This is (obviously) a trivial example as the API consumer would almost certainly understand this relationship without the additional information. However, it serves the purpose of showing how this information _could_ be used by an API consumer if they did not understand the link between the two operations. This is especially valuable _across Path Items_ where logical groupings by operation do not exist. + +It should be noted that API consumers are under no obligation to follow Links as doing so should be based on understanding its effect. The API consumer also needs to be aware of any constraints that may be applied, such as permission to access a given Operation. + +## Summary + +In this topic we've learned that: + +- There has long been a need to indicate relationships between the operations of an API. +- The Link object provides the means to indicate a relationship at design time. +- The relationship can be to an operation in the same OAD or a remote OAD. +- Links are expressed using Runtime Expressions that resolve to dynamic values at runtime. diff --git a/specification/media-types.md b/specification/media-types.md index 2ac5ff4..203fb0f 100644 --- a/specification/media-types.md +++ b/specification/media-types.md @@ -2,7 +2,7 @@ layout: default title: Sequential Media Types parent: The OpenAPI Specification Explained -nav_order: 11 +nav_order: 14 --- # Sequential Media Types diff --git a/specification/parameters.md b/specification/parameters.md index 6de19ac..0e9eb34 100644 --- a/specification/parameters.md +++ b/specification/parameters.md @@ -2,7 +2,7 @@ layout: default title: Parameters and Payload of an Operation parent: The OpenAPI Specification Explained -nav_order: 4 +nav_order: 5 --- # Parameters and Payload of an Operation diff --git a/specification/security.md b/specification/security.md index 3c81209..21ccf44 100644 --- a/specification/security.md +++ b/specification/security.md @@ -2,7 +2,7 @@ layout: default title: Describing API Security parent: The OpenAPI Specification Explained -nav_order: 8 +nav_order: 9 --- # Describing API Security @@ -16,7 +16,7 @@ OpenAPI provides the [Security Scheme Object](https://spec.openapis.org/oas/late
Security Scheme objects are referenced as Security Requirements, either globally or by an Operation.
-A Security Requirement declared for a given Operation takes precedence over global Security Requirements. A number of security mechanisms are supported. Each mechanism is indicated using the `type` property, which is shown in the examples below. +A Security Requirement declared for a given Operation takes precedence over global Security Requirements. Several security mechanisms are supported. Each mechanism is indicated using the `type` property, which is shown in the examples below. There are currently five supported security types, namely: @@ -204,7 +204,7 @@ paths: The final Security Scheme type is OpenID Connect, which provides information for [OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html). -OpenID Connect Core is obviously an OAuth 2.0 profile and is supported by some of properties the OAuth Flow Object. However, OpenID Connect is generally more complex than plain OAuth 2.0 and given OpenID Connect Discovery provides a machine-readable format at the discovery endpoint it makes sense to outsource this functionality entirely. +OpenID Connect Core is an OAuth 2.0 profile and is supported by some of the properties of the OAuth Flow Object. However, OpenID Connect is generally more complex than plain OAuth 2.0 and given OpenID Connect Discovery provides a machine-readable format at the discovery endpoint it makes sense to outsource this functionality entirely. Specifying OpenID Connect is therefore straightforward in that you provide the discovery endpoint in the property `openIdConnectUrl`: @@ -236,7 +236,7 @@ This approach allows OpenAPI to provide _just enough_ information for humans and ## Summary -In this page we've learnt that: +In this page we've learned that: - API security can be described in OpenAPI. - Security properties must be described using a Security Scheme object. diff --git a/specification/servers.md b/specification/servers.md index 135b610..13f8cea 100644 --- a/specification/servers.md +++ b/specification/servers.md @@ -2,7 +2,7 @@ layout: default title: API Servers parent: The OpenAPI Specification Explained -nav_order: 7 +nav_order: 8 --- # API Servers diff --git a/specification/tags.md b/specification/tags.md index c72c540..4edaa9f 100644 --- a/specification/tags.md +++ b/specification/tags.md @@ -2,7 +2,7 @@ layout: default title: Enhanced Tags parent: The OpenAPI Specification Explained -nav_order: 9 +nav_order: 10 --- # Enhanced Tags diff --git a/specification/webhooks.md b/specification/webhooks.md new file mode 100644 index 0000000..3f3f5be --- /dev/null +++ b/specification/webhooks.md @@ -0,0 +1,49 @@ +--- +layout: default +title: Providing Webhooks +parent: The OpenAPI Specification Explained +nav_order: 12 +--- + +# Providing Webhooks + +Webhooks provide bi-directional communications from the API provider to the API consumer to support out-of-band events that do not fit into the pattern of REST APIs or Callbacks. Webhooks describe a pattern where an API consumer registers in advance to receive requests at a URL of their choosing. When an event occurs to trigger a webhook, the server sends a request containing the event data to the registered URL, and the consumer acknowledges it. Webhooks fulfill similar purposes to Callbacks in that they support transmitting information about asynchronous or long-running communications, but without the requirement to implement polling patterns which many API providers find onerous to support. + +Implementing Webhooks typically involves a registration step, which can be defined using regular Operations and allows API consumers to only consume the events they want to receive. [GitHub](https://docs.github.com/en/webhooks/using-webhooks/creating-webhooks) provides a great example of an API provider who makes extensive use of Webhooks and the means to register for specification events. + +API providers still need to define the shape of a given webhook, however. They can do this using the `webhooks` property which is found at the root of the [OpenAPI document](https://spec.openapis.org/oas/v3.1.0#openapi-object). + +
+ +
Webhooks are implemented at the root of the OpenAPI object
+
+ +A webhook is, in fact, simply a Path Item Object and can support one or more Operation objects. The key difference is that Webhooks are not encapsulated by a Paths Object, as the API consumer registers (via an out-of-band mechanism) to receive the webhook at a URL of their choosing. Webhooks therefore describe a template, expressed as a Path Item, for API consumers to follow in how they implement the webhook and validate incoming events. + +Our Tic Tac Toe example includes a webhook that communicates the status of the board. This is not linked to a specific Operation, but given asynchronous behaviors are supported it is implied that this may be received as a result of the API consumer receiving a 202 HTTP return code: + +```yaml +webhooks: + markStatus: + post: + summary: Status of mark operation + description: Provides the status of the mark operation on completion + operationId: markOperationWebhook + responses: + "200": + description: Mark operation has completed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/status" +``` + +The timing and periodicity of events sent over a webhook are typically defined outside of the OAD and described in an API provider's documentation. That said, OpenAPI provides a convenient mechanism for describing webhooks alongside the description of the APIs. + +## Summary + +In this topic we've learned that: + +- Webhooks are a style of API that facilitates bi-directional communication between API providers and API consumers. +- OpenAPI provides the means for API providers to describe their webhooks for API consumers to use as a template for implementation. +- Webhooks in an OAD use Path Items but are not allied to a specific path through a Paths object.