This file describes a proposal for a new design of "links" support for OpenAPI v4 (Moonwalk)
There are three key problems with the OpenAPI v3 support for links that this proposal hopes to address:
There are some occasions where a response header or body parameter contains a complete URL, e.g. the "Location" header returned in a 201 response from POST, or the "nextLink" property in the response of a pageable OData list operation. OpenAPI 3 links do not support this scenario.
OpenAPI 3 links cannot specify a field within the request body as the target of a link -- only individual parameters or the entire request body.
OpenAPI 3 only defines "forward" links -- from source to target. Links are defined in the 'response' object and specify how data in the response maps to parameters in a subsequent request. There is no provision for a parameter to point back to a prior operation as the source of data for the parameter.
While it is possible to specify all links in a "forward" fashion, this is cumbersome since data from a single
operation could be the source for many subsequent operations. For example, the "id" returned in a POST response
could supply the value for the "id" path parameter of a GET, PATCH, and DELETE for the same resource and for
any child resources. If the path parameter could "point back" to the post response, this could be specified just
once, in a parameter defined in components.parameters
, which would be far more concise.
The Link Object will be extended to support links from fields that contain URLs and links to specific request body properties.
To support "backward links", a links
field will be added to the Operation Object to specify operations
that can link to this operation, and two new fields will be added to the Link Object
to specify the source operationId or source operationRef.
The Link Object of OpenAPI v3 will be modfied/extended as follows:
- A new
url
field will be added - The
requestBody
field will be redefined to support links to fields within the request body - New fields
sourceId
andsourceRef
will be added to identify the source operation
A url
field will be added to the Link Object.
Field Name | Type | Description |
---|---|---|
url | Any | {expression} | A constant or an expression to be evaluated and passed to the linked operation. The value should be a URL for the linked operation. Applications may extract values for path or query parameters present in this URL to use as values for these parameters in the linked operation. |
A requestBody
field of the Link Object will be redefined as follows.
Field Name | Type | Description |
---|---|---|
requestBody | Map[string , Any | {expression}] |
A map representing values to pass to an operation, as specified with operationId or identified via operationRef, for use in the request body. The key is the JSON path of a request body property in the linked operation, and the value can be a constant or an expression to be evaluated and passed to the linked operation. |
Note that this new structure is a generalization of the requestBody field in OpenAPI v3, since "/" can be used to specify the entire request body of the linked operation.
Two new fields, sourceId
and sourceRef
will be added to the Link Object to identify the operation that is the
source of information for a parameter or request body.
Field Name | Type | Description |
---|---|---|
sourceRef | string |
A relative or absolute URI reference to an OAS operation. This field is mutually exclusive of the sourceId field, and MUST point to an Operation Object. Relative operationRef values MAY be used to locate an existing Operation Object in the OpenAPI definition. |
sourceId | string |
The name of an existing, resolvable OAS operation, as defined with a unique operationId . This field is mutually exclusive of the sourceRef field. |
The sourceRef
and sourceId
fields identify the operation that is the source of the linked data.
A Link Object may contain both a sourceRef
or sourceId
AND an operationRef
or operationId
, which allows the
same Link Object to be referenced from both a Response Object and from Parameter or Request Body Objects.
A links
property will be added to the Operation Object with the following definition.
Field Name | Type | Description |
---|---|---|
links | Map[string , Link Object | Reference Object] |
A map of operations links that can be the source of values for this operation's parameters and/or request body properties. The key of the map is a short name for the link, following the naming constraints of the names for Component Objects. |
The new links design is not compatible with the OpenAPI v3 links structure but it is expected that tooling could translate an OpenAPI v3 definition into OpenAPI v4 with little (ideally no) loss of information.
paths:
/users:
post:
responses:
'201':
description: Created
headers:
Location:
description: The URL of the User that was created
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/User'
links:
location:
operationId: getUser
# The URL in the location header contains the value for id path parameter
url: $response.header.location
/users/{id}:
get:
operationId: getUser
parameters:
- name: id
in: path
required: true
schema:
type: string
paths:
/users:
get:
operationId: listUsers
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
value:
type: array
items:
$ref: '#/components/schemas/User'
nextLink:
description: A URL to retrieve the next page of users
type: string
links:
nextLink:
operationId: listUsers
# The URL in the location header contains the value for id path parameter
url: $response.body#/nextLink
The follow example is a simplification of the real-world "ConfigurationStores_RegenerateKey" operation of the Azure App Configuration resource-manager service.
paths:
/listKeys:
post:
description: Lists the access keys for the service.
operationId: ListKeys
responses:
'200':
description: Success
content:
application/json:
schema:
description: The result of a request to list API keys.
type: object
properties:
value:
type: array
items:
description: An API key used for authenticating with the service.
type: object
properties:
id:
description: The key ID.
type: string
name:
description: A name for the key describing its usage.
type: string
links:
keyId:
operationId: RegenerateKey
# The id field of the first key can be set as id property of the request body of RegenerateKey
requestBody:
'/id': '$.response/body#value[0].id'
/regenerateKey:
post:
description: Regenerates an access key for the service.
operationId: RegenerateKey
requestBody:
content:
application/json:
schema:
description: The parameters used to regenerate an API key.
type: object
properties:
id:
description: The id of the key to regenerate.
type: string
description: The parameters for regenerating an access key.
required: true
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/ApiKey'
Rather than add a links
field to the operation, we could add links
to individual parameters and the response body,
but this seems unnecessary, since the Link Object explicitly names the parameters and response body properties that
it can supply values for.
One advantage of allowing a backlink on the parameter object is that it could be specified on a parameter definition in components.parameters
and then used by any operation that references this parameter. With links specified on the operation, the link will need to be added on every consuming operation.
A futher consideration is that Parameter Objects may be going away in OpenAPI v4, in favor of parameters defined using JSON schema. This would require extending the OpenAPI dialect of JSON schema to add a links
attribute. But we don't want to support links everywhere in JSON schema -- just for parameters (and maybe request bodies), so this might be tricky.
The current structure for the links
field in the OpenAPI v3 Response Object, and proposed here for the Operation Object,
is a Map[string, Link Object \| Reference Object]
, where the keys of this map are the "short name for the link". This short name has no real purpose in the API definition and just adds verbosity. A better structure would be [Link Object \| Reference Object]
-- the equivalent of the values of the current map structure.
I didn't make this change to minimize the changes from the current structure, but it is appealing (to me at least) and perhaps we should reconsider.
One case that is not addressed by this proposal is extracting paired items from a list in the response.
A common case in Azure is a list operation for resources, where each entry in the list contains 1) an id
field, and 2) an etag
field. We'd like to be able to define a link that would say "You can call delete for a resource using the id and etag fields from any entry in the response of the list resources operation".
We should try to also address the "for any entry in the list" case, for example with a wildcard array index
[*]
:Extracting paired items could then be achieved by using both
[*].id
and[*].etag
in a link object.