mirror of
https://github.com/matrix-org/matrix-spec
synced 2026-04-03 01:34:10 +02:00
Merge remote-tracking branch 'origin/main' into dbkr/msc3981
This commit is contained in:
commit
7da80112f0
|
|
@ -0,0 +1 @@
|
||||||
|
Clarify that the `/login` and `/register` endpoints should fail when using the `m.login.application_service` login type without a valid `as_token`.
|
||||||
1
changelogs/client_server/newsfragments/1735.feature
Normal file
1
changelogs/client_server/newsfragments/1735.feature
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add support for multi-stream VoIP, as per [MSC3077](https://github.com/matrix-org/matrix-spec-proposals/pull/3077).
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Fix various typos throughout the specification.
|
||||||
1
changelogs/internal/newsfragments/1718.clarification
Normal file
1
changelogs/internal/newsfragments/1718.clarification
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add some clarifications around implementation requirements for MSCs
|
||||||
1
changelogs/internal/newsfragments/1745.clarification
Normal file
1
changelogs/internal/newsfragments/1745.clarification
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Factor out all the common parameters of the various `/relations` apis.
|
||||||
1
changelogs/internal/newsfragments/1751.clarification
Normal file
1
changelogs/internal/newsfragments/1751.clarification
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add support for `$ref` URIs containing fragments in OpenAPI definitions and JSON schemas.
|
||||||
|
|
@ -436,6 +436,12 @@ an application service-defined namespace will receive the same
|
||||||
`M_EXCLUSIVE` error code, but only if the application service has
|
`M_EXCLUSIVE` error code, but only if the application service has
|
||||||
defined the namespace as `exclusive`.
|
defined the namespace as `exclusive`.
|
||||||
|
|
||||||
|
If `/register` or `/login` is called with the `m.login.application_service`
|
||||||
|
login type, but without a valid `as_token`, the endpoints will return an error
|
||||||
|
with the `M_MISSING_TOKEN` or `M_UNKNOWN_TOKEN` error code and 401 as the HTTP
|
||||||
|
status code. This is the same behavior as invalid auth in the client-server API
|
||||||
|
(see [Using access tokens](/client-server-api/#using-access-tokens)).
|
||||||
|
|
||||||
#### Pinging
|
#### Pinging
|
||||||
|
|
||||||
{{% added-in v="1.7" %}}
|
{{% added-in v="1.7" %}}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ This module allows a Matrix homeserver to delegate user authentication
|
||||||
to an external authentication server supporting one of these protocols.
|
to an external authentication server supporting one of these protocols.
|
||||||
In this process, there are three systems involved:
|
In this process, there are three systems involved:
|
||||||
|
|
||||||
- A Matrix client, using the APIs defined this specification, which
|
- A Matrix client, using the APIs defined in this specification, which
|
||||||
is seeking to authenticate a user to a Matrix homeserver.
|
is seeking to authenticate a user to a Matrix homeserver.
|
||||||
- A Matrix homeserver, implementing the APIs defined in this
|
- A Matrix homeserver, implementing the APIs defined in this
|
||||||
specification, but which is delegating user authentication to the
|
specification, but which is delegating user authentication to the
|
||||||
|
|
|
||||||
|
|
@ -171,18 +171,31 @@ In response to an incoming invite, a client may do one of several things:
|
||||||
|
|
||||||
##### Streams
|
##### Streams
|
||||||
|
|
||||||
Clients are expected to send one stream with one track of kind `audio` (creating a
|
Clients may send more than one stream in a VoIP call. The streams should be
|
||||||
voice call). They can optionally send a second track in the same stream of kind
|
differentiated by including metadata in the [`m.call.invite`](/client-server-api/#mcallinvite),
|
||||||
`video` (creating a video call).
|
[`m.call.answer`](/client-server-api/#mcallanswer) and [`m.call.negotiate`](/client-server-api/#mcallnegotiate)
|
||||||
|
events, using the `sdp_stream_metadata` property.
|
||||||
|
|
||||||
Clients implementing this specification use the first stream and will ignore
|
`sdp_stream_metadata` maps from the `id` of a stream in the session description,
|
||||||
any streamless tracks. Note that in the JavaScript WebRTC API, this means
|
to metadata about that stream. Currently only one property is defined for the
|
||||||
`addTrack()` must be passed two parameters: a track and a stream, not just a
|
metadata. This is `purpose`, which should be a string indicating the purpose of
|
||||||
track, and in a video call the stream must be the same for both audio and video
|
the stream. The following `purpose`s are defined:
|
||||||
track.
|
|
||||||
|
|
||||||
A client may send other streams and tracks but the behaviour of the other party
|
* `m.usermedia` - stream that contains the webcam and/or microphone tracks
|
||||||
with respect to presenting such streams and tracks is undefined.
|
* `m.screenshare` - stream with the screen-sharing tracks
|
||||||
|
|
||||||
|
If `sdp_stream_metadata` is present and an incoming stream is not listed in it,
|
||||||
|
the stream should be ignored. If a stream has a `purpose` of an unknown type, it
|
||||||
|
should also be ignored.
|
||||||
|
|
||||||
|
For backwards compatibility, if `sdp_stream_metadata` is not present in the
|
||||||
|
initial [`m.call.invite`](/client-server-api/#mcallinvite) or [`m.call.answer`](/client-server-api/#mcallanswer)
|
||||||
|
event sent by the other party, the client should assume that this property is
|
||||||
|
not supported by the other party. It means that multiple streams cannot be
|
||||||
|
differentiated: the client should only use the first incoming stream and
|
||||||
|
shouldn't send more than one stream.
|
||||||
|
|
||||||
|
Clients implementing this specification should ignore any streamless tracks.
|
||||||
|
|
||||||
##### Invitees
|
##### Invitees
|
||||||
The `invitee` field should be added whenever the call is intended for one
|
The `invitee` field should be added whenever the call is intended for one
|
||||||
|
|
|
||||||
|
|
@ -380,9 +380,18 @@ As part of the proposal process the Spec Core Team will require evidence
|
||||||
of the MSC working in order for it to move into FCP. This can usually be
|
of the MSC working in order for it to move into FCP. This can usually be
|
||||||
a branch/pull request to whichever implementation of choice that proves
|
a branch/pull request to whichever implementation of choice that proves
|
||||||
the MSC works in practice, though in some cases the MSC itself will be
|
the MSC works in practice, though in some cases the MSC itself will be
|
||||||
small enough to be considered proven. Where it's unclear if an MSC will
|
small enough to be considered proven. Implementations do not need to be
|
||||||
require an implementation proof, ask in
|
merged or released, but must be of sufficient quality to show that the
|
||||||
[\#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org).
|
MSC works. Where it's unclear if an MSC will require an implementation
|
||||||
|
proof, ask in [\#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org).
|
||||||
|
Proposals may require both server-side and client-side implementations.
|
||||||
|
|
||||||
|
Proposals that have not yet been implemented will have the
|
||||||
|
`needs-implementation` label. After an implementation has been made, add a
|
||||||
|
comment in the GitHub issue indicating so. After an implementation has been
|
||||||
|
made, we will check it to verify that it implements the MSC. Proposals that
|
||||||
|
have implementations that have not yet been checked will have the
|
||||||
|
`implementation-needs-checking` label.
|
||||||
|
|
||||||
### Early release of an MSC/idea
|
### Early release of an MSC/idea
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,89 +33,13 @@ paths:
|
||||||
security:
|
security:
|
||||||
- accessToken: []
|
- accessToken: []
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- $ref: '#/components/parameters/roomId'
|
||||||
name: roomId
|
- $ref: '#/components/parameters/eventId'
|
||||||
description: The ID of the room containing the parent event.
|
- $ref: '#/components/parameters/from'
|
||||||
required: true
|
- $ref: '#/components/parameters/to'
|
||||||
example: "!636q39766251:matrix.org"
|
- $ref: '#/components/parameters/limit'
|
||||||
schema:
|
- $ref: '#/components/parameters/dir'
|
||||||
type: string
|
- $ref: '#/components/parameters/recurse'
|
||||||
- in: path
|
|
||||||
name: eventId
|
|
||||||
description: The ID of the parent event whose child events are to be returned.
|
|
||||||
required: true
|
|
||||||
example: $asfDuShaf7Gafaw
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: from
|
|
||||||
description: |-
|
|
||||||
The pagination token to start returning results from. If not supplied, results
|
|
||||||
start at the most recent topological event known to the server.
|
|
||||||
|
|
||||||
Can be a `next_batch` or `prev_batch` token from a previous call, or a returned
|
|
||||||
`start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages),
|
|
||||||
or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync).
|
|
||||||
required: false
|
|
||||||
example: page2_token
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: to
|
|
||||||
description: |-
|
|
||||||
The pagination token to stop returning results at. If not supplied, results
|
|
||||||
continue up to `limit` or until there are no more events.
|
|
||||||
|
|
||||||
Like `from`, this can be a previous token from a prior call to this endpoint
|
|
||||||
or from `/messages` or `/sync`.
|
|
||||||
required: false
|
|
||||||
example: page3_token
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: limit
|
|
||||||
description: |-
|
|
||||||
The maximum number of results to return in a single `chunk`. The server can
|
|
||||||
and should apply a maximum value to this parameter to avoid large responses.
|
|
||||||
|
|
||||||
Similarly, the server should apply a default value when not supplied.
|
|
||||||
required: false
|
|
||||||
example: 20
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
- in: query
|
|
||||||
name: dir
|
|
||||||
x-addedInMatrixVersion: "1.4"
|
|
||||||
description: |-
|
|
||||||
Optional (default `b`) direction to return events from. If this is set to `f`, events
|
|
||||||
will be returned in chronological order starting at `from`. If it
|
|
||||||
is set to `b`, events will be returned in *reverse* chronological
|
|
||||||
order, again starting at `from`.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- b
|
|
||||||
- f
|
|
||||||
- in: query
|
|
||||||
name: recurse
|
|
||||||
x-addedInMatrixVersion: "1.10"
|
|
||||||
required: false
|
|
||||||
description: |-
|
|
||||||
Whether to additionally include events which only relate indirectly to the
|
|
||||||
given event, ie. events related to the root events via one or more direct relationships.
|
|
||||||
|
|
||||||
If set to `false`, only events which have direct a relation with the given
|
|
||||||
event will be included.
|
|
||||||
|
|
||||||
If set to `true`, all events which relate to the given event, or relate to
|
|
||||||
events that relate to the given event, will be included.
|
|
||||||
|
|
||||||
It is recommended that at least 3 levels of relationships are traversed.
|
|
||||||
Implementations may perform more but should be careful to not infinitely recurse.
|
|
||||||
|
|
||||||
The default value is `false`.
|
|
||||||
schema:
|
|
||||||
type: boolean
|
|
||||||
responses:
|
responses:
|
||||||
# note: this endpoint deliberately does not support rate limiting, therefore a
|
# note: this endpoint deliberately does not support rate limiting, therefore a
|
||||||
# 429 error response is not included.
|
# 429 error response is not included.
|
||||||
|
|
@ -127,66 +51,24 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
allOf:
|
||||||
properties:
|
- $ref: '#/components/schemas/response'
|
||||||
chunk:
|
- type: object
|
||||||
title: ChildEventsChunk
|
properties:
|
||||||
type: array
|
chunk:
|
||||||
description: The child events of the requested event, ordered topologically
|
title: ChildEventsChunk
|
||||||
most-recent first.
|
type: array
|
||||||
items:
|
description: The child events of the requested event, ordered topologically
|
||||||
allOf:
|
most-recent first.
|
||||||
- $ref: definitions/client_event.yaml
|
items:
|
||||||
next_batch:
|
$ref: definitions/client_event.yaml
|
||||||
type: string
|
required:
|
||||||
description: |-
|
- chunk
|
||||||
An opaque string representing a pagination token. The absence of this token
|
|
||||||
means there are no more results to fetch and the client should stop paginating.
|
|
||||||
prev_batch:
|
|
||||||
type: string
|
|
||||||
description: |-
|
|
||||||
An opaque string representing a pagination token. The absence of this token
|
|
||||||
means this is the start of the result set, i.e. this is the first batch/page.
|
|
||||||
recursion_depth:
|
|
||||||
type: integer
|
|
||||||
description: |-
|
|
||||||
If the `recurse` parameter was supplied by the client, this response field is
|
|
||||||
mandatory and gives the actual depth to which the server recursed. The the client
|
|
||||||
did not specify the `recurse` parameter, this field must be absent.
|
|
||||||
required:
|
|
||||||
- chunk
|
|
||||||
examples:
|
examples:
|
||||||
response:
|
response:
|
||||||
value: {
|
$ref: '#/components/examples/response'
|
||||||
"chunk": [
|
|
||||||
{
|
|
||||||
"room_id": "!636q39766251:matrix.org",
|
|
||||||
"$ref": "../../event-schemas/examples/m.room.message$m.text.yaml",
|
|
||||||
"content": {
|
|
||||||
"m.relates_to": {
|
|
||||||
"rel_type": "org.example.my_relation",
|
|
||||||
"event_id": "$asfDuShaf7Gafaw"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"next_batch": "page2_token",
|
|
||||||
"prev_batch": "page1_token"
|
|
||||||
}
|
|
||||||
"404":
|
"404":
|
||||||
description: |-
|
$ref: '#/components/responses/404'
|
||||||
The parent event was not found or the user does not have permission to read
|
|
||||||
this event (it might be contained in history that is not accessible to the user).
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: definitions/errors/error.yaml
|
|
||||||
examples:
|
|
||||||
response:
|
|
||||||
value: {
|
|
||||||
"errcode": "M_NOT_FOUND",
|
|
||||||
"error": "Event not found."
|
|
||||||
}
|
|
||||||
tags:
|
tags:
|
||||||
- Event relationships
|
- Event relationships
|
||||||
# The same as above, with added `/{relType}`
|
# The same as above, with added `/{relType}`
|
||||||
|
|
@ -208,97 +90,14 @@ paths:
|
||||||
security:
|
security:
|
||||||
- accessToken: []
|
- accessToken: []
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- $ref: '#/components/parameters/roomId'
|
||||||
name: roomId
|
- $ref: '#/components/parameters/eventId'
|
||||||
description: The ID of the room containing the parent event.
|
- $ref: '#/components/parameters/relType'
|
||||||
required: true
|
- $ref: '#/components/parameters/from'
|
||||||
example: "!636q39766251:matrix.org"
|
- $ref: '#/components/parameters/to'
|
||||||
schema:
|
- $ref: '#/components/parameters/limit'
|
||||||
type: string
|
- $ref: '#/components/parameters/dir'
|
||||||
- in: path
|
- $ref: '#/components/parameters/recurse'
|
||||||
name: eventId
|
|
||||||
description: The ID of the parent event whose child events are to be returned.
|
|
||||||
required: true
|
|
||||||
example: $asfDuShaf7Gafaw
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: path
|
|
||||||
name: relType
|
|
||||||
description: The [relationship type](/client-server-api/#relationship-types) to
|
|
||||||
search for.
|
|
||||||
required: true
|
|
||||||
example: org.example.my_relation
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: from
|
|
||||||
description: |-
|
|
||||||
The pagination token to start returning results from. If not supplied, results
|
|
||||||
start at the most recent topological event known to the server.
|
|
||||||
|
|
||||||
Can be a `next_batch` or `prev_batch` token from a previous call, or a returned
|
|
||||||
`start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages),
|
|
||||||
or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync).
|
|
||||||
required: false
|
|
||||||
example: page2_token
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: to
|
|
||||||
description: |-
|
|
||||||
The pagination token to stop returning results at. If not supplied, results
|
|
||||||
continue up to `limit` or until there are no more events.
|
|
||||||
|
|
||||||
Like `from`, this can be a previous token from a prior call to this endpoint
|
|
||||||
or from `/messages` or `/sync`.
|
|
||||||
required: false
|
|
||||||
example: page3_token
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: limit
|
|
||||||
description: |-
|
|
||||||
The maximum number of results to return in a single `chunk`. The server can
|
|
||||||
and should apply a maximum value to this parameter to avoid large responses.
|
|
||||||
|
|
||||||
Similarly, the server should apply a default value when not supplied.
|
|
||||||
required: false
|
|
||||||
example: 20
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
- in: query
|
|
||||||
name: dir
|
|
||||||
x-addedInMatrixVersion: "1.4"
|
|
||||||
description: |-
|
|
||||||
Optional (default `b`) direction to return events from. If this is set to `f`, events
|
|
||||||
will be returned in chronological order starting at `from`. If it
|
|
||||||
is set to `b`, events will be returned in *reverse* chronological
|
|
||||||
order, again starting at `from`.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- b
|
|
||||||
- f
|
|
||||||
- in: query
|
|
||||||
name: recurse
|
|
||||||
x-addedInMatrixVersion: "1.10"
|
|
||||||
required: false
|
|
||||||
description: |-
|
|
||||||
Whether to additionally include events which only relate indirectly to the
|
|
||||||
given event, ie. events related to the root events via one or more direct relationships.
|
|
||||||
|
|
||||||
If set to `false`, only events which have direct a relation with the given
|
|
||||||
event will be included.
|
|
||||||
|
|
||||||
If set to `true`, all events which relate to the given event, or relate to
|
|
||||||
events that relate to the given event, will be included.
|
|
||||||
|
|
||||||
It is recommended that at least 3 levels of relationships are traversed.
|
|
||||||
Implementations may perform more but should be careful to not infinitely recurse.
|
|
||||||
|
|
||||||
The default value is `false`.
|
|
||||||
schema:
|
|
||||||
type: boolean
|
|
||||||
responses:
|
responses:
|
||||||
# note: this endpoint deliberately does not support rate limiting, therefore a
|
# note: this endpoint deliberately does not support rate limiting, therefore a
|
||||||
# 429 error response is not included.
|
# 429 error response is not included.
|
||||||
|
|
@ -310,68 +109,26 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
allOf:
|
||||||
properties:
|
- $ref: '#/components/schemas/response'
|
||||||
chunk:
|
- type: object
|
||||||
title: ChildEventsChunk
|
properties:
|
||||||
type: array
|
chunk:
|
||||||
description: |-
|
title: ChildEventsChunk
|
||||||
The child events of the requested event, ordered topologically
|
type: array
|
||||||
most-recent first. The events returned will match the `relType`
|
description: |-
|
||||||
supplied in the URL.
|
The child events of the requested event, ordered topologically
|
||||||
items:
|
most-recent first. The events returned will match the `relType`
|
||||||
allOf:
|
supplied in the URL.
|
||||||
- $ref: definitions/client_event.yaml
|
items:
|
||||||
next_batch:
|
$ref: definitions/client_event.yaml
|
||||||
type: string
|
required:
|
||||||
description: |-
|
- chunk
|
||||||
An opaque string representing a pagination token. The absence of this token
|
|
||||||
means there are no more results to fetch and the client should stop paginating.
|
|
||||||
prev_batch:
|
|
||||||
type: string
|
|
||||||
description: |-
|
|
||||||
An opaque string representing a pagination token. The absence of this token
|
|
||||||
means this is the start of the result set, i.e. this is the first batch/page.
|
|
||||||
recursion_depth:
|
|
||||||
type: integer
|
|
||||||
description: |-
|
|
||||||
If the `recurse` parameter was supplied by the client, this response field is
|
|
||||||
mandatory and gives the actual depth to which the server recursed. The the client
|
|
||||||
did not specify the `recurse` parameter, this field must be absent.
|
|
||||||
required:
|
|
||||||
- chunk
|
|
||||||
examples:
|
examples:
|
||||||
response:
|
response:
|
||||||
value: {
|
$ref: '#/components/examples/response'
|
||||||
"chunk": [
|
|
||||||
{
|
|
||||||
"room_id": "!636q39766251:matrix.org",
|
|
||||||
"$ref": "../../event-schemas/examples/m.room.message$m.text.yaml",
|
|
||||||
"content": {
|
|
||||||
"m.relates_to": {
|
|
||||||
"rel_type": "org.example.my_relation",
|
|
||||||
"event_id": "$asfDuShaf7Gafaw"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"next_batch": "page2_token",
|
|
||||||
"prev_batch": "page1_token"
|
|
||||||
}
|
|
||||||
"404":
|
"404":
|
||||||
description: |-
|
$ref: '#/components/responses/404'
|
||||||
The parent event was not found or the user does not have permission to read
|
|
||||||
this event (it might be contained in history that is not accessible to the user).
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: definitions/errors/error.yaml
|
|
||||||
examples:
|
|
||||||
response:
|
|
||||||
value: {
|
|
||||||
"errcode": "M_NOT_FOUND",
|
|
||||||
"error": "Event not found."
|
|
||||||
}
|
|
||||||
tags:
|
tags:
|
||||||
- Event relationships
|
- Event relationships
|
||||||
# The same as above, with added `/{eventType}`
|
# The same as above, with added `/{eventType}`
|
||||||
|
|
@ -394,28 +151,9 @@ paths:
|
||||||
security:
|
security:
|
||||||
- accessToken: []
|
- accessToken: []
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- $ref: '#/components/parameters/roomId'
|
||||||
name: roomId
|
- $ref: '#/components/parameters/eventId'
|
||||||
description: The ID of the room containing the parent event.
|
- $ref: '#/components/parameters/relType'
|
||||||
required: true
|
|
||||||
example: "!636q39766251:matrix.org"
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: path
|
|
||||||
name: eventId
|
|
||||||
description: The ID of the parent event whose child events are to be returned.
|
|
||||||
required: true
|
|
||||||
example: $asfDuShaf7Gafaw
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: path
|
|
||||||
name: relType
|
|
||||||
description: The [relationship type](/client-server-api/#relationship-types) to
|
|
||||||
search for.
|
|
||||||
required: true
|
|
||||||
example: org.example.my_relation
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: path
|
- in: path
|
||||||
name: eventType
|
name: eventType
|
||||||
description: |-
|
description: |-
|
||||||
|
|
@ -427,75 +165,11 @@ paths:
|
||||||
example: m.room.message
|
example: m.room.message
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
- in: query
|
- $ref: '#/components/parameters/from'
|
||||||
name: from
|
- $ref: '#/components/parameters/to'
|
||||||
description: |-
|
- $ref: '#/components/parameters/limit'
|
||||||
The pagination token to start returning results from. If not supplied, results
|
- $ref: '#/components/parameters/dir'
|
||||||
start at the most recent topological event known to the server.
|
- $ref: '#/components/parameters/recurse'
|
||||||
|
|
||||||
Can be a `next_batch` or `prev_batch` token from a previous call, or a returned
|
|
||||||
`start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages),
|
|
||||||
or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync).
|
|
||||||
required: false
|
|
||||||
example: page2_token
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: to
|
|
||||||
description: |-
|
|
||||||
The pagination token to stop returning results at. If not supplied, results
|
|
||||||
continue up to `limit` or until there are no more events.
|
|
||||||
|
|
||||||
Like `from`, this can be a previous token from a prior call to this endpoint
|
|
||||||
or from `/messages` or `/sync`.
|
|
||||||
required: false
|
|
||||||
example: page3_token
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
- in: query
|
|
||||||
name: limit
|
|
||||||
description: |-
|
|
||||||
The maximum number of results to return in a single `chunk`. The server can
|
|
||||||
and should apply a maximum value to this parameter to avoid large responses.
|
|
||||||
|
|
||||||
Similarly, the server should apply a default value when not supplied.
|
|
||||||
required: false
|
|
||||||
example: 20
|
|
||||||
schema:
|
|
||||||
type: integer
|
|
||||||
- in: query
|
|
||||||
name: dir
|
|
||||||
x-addedInMatrixVersion: "1.4"
|
|
||||||
description: |-
|
|
||||||
Optional (default `b`) direction to return events from. If this is set to `f`, events
|
|
||||||
will be returned in chronological order starting at `from`. If it
|
|
||||||
is set to `b`, events will be returned in *reverse* chronological
|
|
||||||
order, again starting at `from`.
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- b
|
|
||||||
- f
|
|
||||||
- in: query
|
|
||||||
name: recurse
|
|
||||||
x-addedInMatrixVersion: "1.10"
|
|
||||||
required: false
|
|
||||||
description: |-
|
|
||||||
Whether to additionally include events which only relate indirectly to the
|
|
||||||
given event, ie. events related to the root events via one or more direct relationships.
|
|
||||||
|
|
||||||
If set to `false`, only events which have direct a relation with the given
|
|
||||||
event will be included.
|
|
||||||
|
|
||||||
If set to `true`, all events which relate to the given event, or relate to
|
|
||||||
events that relate to the given event, will be included.
|
|
||||||
|
|
||||||
It is recommended that at least 3 levels of relationships are traversed.
|
|
||||||
Implementations may perform more but should be careful to not infinitely recurse.
|
|
||||||
|
|
||||||
The default value is `false`.
|
|
||||||
schema:
|
|
||||||
type: boolean
|
|
||||||
responses:
|
responses:
|
||||||
# note: this endpoint deliberately does not support rate limiting, therefore a
|
# note: this endpoint deliberately does not support rate limiting, therefore a
|
||||||
# 429 error response is not included.
|
# 429 error response is not included.
|
||||||
|
|
@ -507,68 +181,26 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
allOf:
|
||||||
properties:
|
- $ref: '#/components/schemas/response'
|
||||||
chunk:
|
- type: object
|
||||||
title: ChildEventsChunk
|
properties:
|
||||||
type: array
|
chunk:
|
||||||
description: |-
|
title: ChildEventsChunk
|
||||||
The child events of the requested event, ordered topologically most-recent
|
type: array
|
||||||
first. The events returned will match the `relType` and `eventType` supplied
|
description: |-
|
||||||
in the URL.
|
The child events of the requested event, ordered topologically most-recent
|
||||||
items:
|
first. The events returned will match the `relType` and `eventType` supplied
|
||||||
allOf:
|
in the URL.
|
||||||
- $ref: definitions/client_event.yaml
|
items:
|
||||||
next_batch:
|
$ref: definitions/client_event.yaml
|
||||||
type: string
|
required:
|
||||||
description: |-
|
- chunk
|
||||||
An opaque string representing a pagination token. The absence of this token
|
|
||||||
means there are no more results to fetch and the client should stop paginating.
|
|
||||||
prev_batch:
|
|
||||||
type: string
|
|
||||||
description: |-
|
|
||||||
An opaque string representing a pagination token. The absence of this token
|
|
||||||
means this is the start of the result set, i.e. this is the first batch/page.
|
|
||||||
recursion_depth:
|
|
||||||
type: integer
|
|
||||||
description: |-
|
|
||||||
If the `recurse` parameter was supplied by the client, this response field is
|
|
||||||
mandatory and gives the actual depth to which the server recursed. The the client
|
|
||||||
did not specify the `recurse` parameter, this field must be absent.
|
|
||||||
required:
|
|
||||||
- chunk
|
|
||||||
examples:
|
examples:
|
||||||
response:
|
response:
|
||||||
value: {
|
$ref: '#/components/examples/response'
|
||||||
"chunk": [
|
|
||||||
{
|
|
||||||
"room_id": "!636q39766251:matrix.org",
|
|
||||||
"$ref": "../../event-schemas/examples/m.room.message$m.text.yaml",
|
|
||||||
"content": {
|
|
||||||
"m.relates_to": {
|
|
||||||
"rel_type": "org.example.my_relation",
|
|
||||||
"event_id": "$asfDuShaf7Gafaw"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"next_batch": "page2_token",
|
|
||||||
"prev_batch": "page1_token"
|
|
||||||
}
|
|
||||||
"404":
|
"404":
|
||||||
description: |-
|
$ref: '#/components/responses/404'
|
||||||
The parent event was not found or the user does not have permission to read
|
|
||||||
this event (it might be contained in history that is not accessible to the user).
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: definitions/errors/error.yaml
|
|
||||||
examples:
|
|
||||||
response:
|
|
||||||
value: {
|
|
||||||
"errcode": "M_NOT_FOUND",
|
|
||||||
"error": "Event not found."
|
|
||||||
}
|
|
||||||
tags:
|
tags:
|
||||||
- Event relationships
|
- Event relationships
|
||||||
servers:
|
servers:
|
||||||
|
|
@ -586,3 +218,156 @@ servers:
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
$ref: definitions/security.yaml
|
$ref: definitions/security.yaml
|
||||||
|
parameters:
|
||||||
|
roomId:
|
||||||
|
in: path
|
||||||
|
name: roomId
|
||||||
|
description: The ID of the room containing the parent event.
|
||||||
|
required: true
|
||||||
|
example: "!636q39766251:matrix.org"
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
eventId:
|
||||||
|
in: path
|
||||||
|
name: eventId
|
||||||
|
description: The ID of the parent event whose child events are to be returned.
|
||||||
|
required: true
|
||||||
|
example: $asfDuShaf7Gafaw
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
from:
|
||||||
|
in: query
|
||||||
|
name: from
|
||||||
|
description: |-
|
||||||
|
The pagination token to start returning results from. If not supplied, results
|
||||||
|
start at the most recent topological event known to the server.
|
||||||
|
|
||||||
|
Can be a `next_batch` or `prev_batch` token from a previous call, or a returned
|
||||||
|
`start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages),
|
||||||
|
or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync).
|
||||||
|
required: false
|
||||||
|
example: page2_token
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
to:
|
||||||
|
in: query
|
||||||
|
name: to
|
||||||
|
description: |-
|
||||||
|
The pagination token to stop returning results at. If not supplied, results
|
||||||
|
continue up to `limit` or until there are no more events.
|
||||||
|
|
||||||
|
Like `from`, this can be a previous token from a prior call to this endpoint
|
||||||
|
or from `/messages` or `/sync`.
|
||||||
|
required: false
|
||||||
|
example: page3_token
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
limit:
|
||||||
|
in: query
|
||||||
|
name: limit
|
||||||
|
description: |-
|
||||||
|
The maximum number of results to return in a single `chunk`. The server can
|
||||||
|
and should apply a maximum value to this parameter to avoid large responses.
|
||||||
|
|
||||||
|
Similarly, the server should apply a default value when not supplied.
|
||||||
|
required: false
|
||||||
|
example: 20
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
dir:
|
||||||
|
in: query
|
||||||
|
name: dir
|
||||||
|
x-addedInMatrixVersion: "1.4"
|
||||||
|
description: |-
|
||||||
|
Optional (default `b`) direction to return events from. If this is set to `f`, events
|
||||||
|
will be returned in chronological order starting at `from`. If it
|
||||||
|
is set to `b`, events will be returned in *reverse* chronological
|
||||||
|
order, again starting at `from`.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- b
|
||||||
|
- f
|
||||||
|
relType:
|
||||||
|
in: path
|
||||||
|
name: relType
|
||||||
|
description: The [relationship type](/client-server-api/#relationship-types) to
|
||||||
|
search for.
|
||||||
|
required: true
|
||||||
|
example: org.example.my_relation
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
recurse:
|
||||||
|
in: query
|
||||||
|
name: recurse
|
||||||
|
x-addedInMatrixVersion: "1.10"
|
||||||
|
required: false
|
||||||
|
description: |-
|
||||||
|
Whether to additionally include events which only relate indirectly to the
|
||||||
|
given event, ie. events related to the root events via one or more direct relationships.
|
||||||
|
|
||||||
|
If set to `false`, only events which have direct a relation with the given
|
||||||
|
event will be included.
|
||||||
|
|
||||||
|
If set to `true`, all events which relate to the given event, or relate to
|
||||||
|
events that relate to the given event, will be included.
|
||||||
|
|
||||||
|
It is recommended that at least 3 levels of relationships are traversed.
|
||||||
|
Implementations may perform more but should be careful to not infinitely recurse.
|
||||||
|
|
||||||
|
The default value is `false`.
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
schemas:
|
||||||
|
response:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
next_batch:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
An opaque string representing a pagination token. The absence of this token
|
||||||
|
means there are no more results to fetch and the client should stop paginating.
|
||||||
|
prev_batch:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
An opaque string representing a pagination token. The absence of this token
|
||||||
|
means this is the start of the result set, i.e. this is the first batch/page.
|
||||||
|
recursion_depth:
|
||||||
|
type: integer
|
||||||
|
description: |-
|
||||||
|
If the `recurse` parameter was supplied by the client, this response field is
|
||||||
|
mandatory and gives the actual depth to which the server recursed. The the client
|
||||||
|
did not specify the `recurse` parameter, this field must be absent.
|
||||||
|
responses:
|
||||||
|
"404":
|
||||||
|
description: |-
|
||||||
|
The parent event was not found or the user does not have permission to read
|
||||||
|
this event (it might be contained in history that is not accessible to the user).
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: definitions/errors/error.yaml
|
||||||
|
examples:
|
||||||
|
response:
|
||||||
|
value: {
|
||||||
|
"errcode": "M_NOT_FOUND",
|
||||||
|
"error": "Event not found."
|
||||||
|
}
|
||||||
|
examples:
|
||||||
|
response:
|
||||||
|
value: {
|
||||||
|
"chunk": [
|
||||||
|
{
|
||||||
|
"room_id": "!636q39766251:matrix.org",
|
||||||
|
"$ref": "../../event-schemas/examples/m.room.message$m.text.yaml",
|
||||||
|
"content": {
|
||||||
|
"m.relates_to": {
|
||||||
|
"rel_type": "org.example.my_relation",
|
||||||
|
"event_id": "$asfDuShaf7Gafaw"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"next_batch": "page2_token",
|
||||||
|
"prev_batch": "page1_token"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,14 @@
|
||||||
"answer": {
|
"answer": {
|
||||||
"type" : "answer",
|
"type" : "answer",
|
||||||
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
||||||
|
},
|
||||||
|
"sdp_stream_metadata": {
|
||||||
|
"271828182845": {
|
||||||
|
"purpose": "m.screenshare"
|
||||||
|
},
|
||||||
|
"314159265358": {
|
||||||
|
"purpose": "m.usermedia"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,14 @@
|
||||||
"offer": {
|
"offer": {
|
||||||
"type" : "offer",
|
"type" : "offer",
|
||||||
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
||||||
|
},
|
||||||
|
"sdp_stream_metadata": {
|
||||||
|
"271828182845": {
|
||||||
|
"purpose": "m.screenshare"
|
||||||
|
},
|
||||||
|
"314159265358": {
|
||||||
|
"purpose": "m.usermedia"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,14 @@
|
||||||
"description": {
|
"description": {
|
||||||
"type" : "offer",
|
"type" : "offer",
|
||||||
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
||||||
|
},
|
||||||
|
"sdp_stream_metadata": {
|
||||||
|
"271828182845": {
|
||||||
|
"purpose": "m.screenshare"
|
||||||
|
},
|
||||||
|
"314159265358": {
|
||||||
|
"purpose": "m.usermedia"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
type: object
|
||||||
|
x-addedInMatrixVersion: "1.10"
|
||||||
|
description: |-
|
||||||
|
Metadata describing the [streams](/client-server-api/#streams) that will be
|
||||||
|
sent.
|
||||||
|
|
||||||
|
This is a map of stream ID to metadata about the stream.
|
||||||
|
additionalProperties:
|
||||||
|
type: object
|
||||||
|
title: StreamMetadata
|
||||||
|
description: Metadata describing a stream.
|
||||||
|
properties:
|
||||||
|
purpose:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- m.usermedia
|
||||||
|
- m.screenshare
|
||||||
|
description: |-
|
||||||
|
The purpose of the stream.
|
||||||
|
|
||||||
|
The possible values are:
|
||||||
|
|
||||||
|
* `m.usermedia`: Stream that contains the webcam and/or microphone
|
||||||
|
tracks.
|
||||||
|
* `m.screenshare`: Stream with the screen-sharing tracks.
|
||||||
|
required:
|
||||||
|
- purpose
|
||||||
|
|
@ -27,6 +27,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["type", "sdp"]
|
"required": ["type", "sdp"]
|
||||||
|
},
|
||||||
|
"sdp_stream_metadata": {
|
||||||
|
"$ref": "components/sdp_stream_metadata.yaml"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["answer"]
|
"required": ["answer"]
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,10 @@
|
||||||
"invitee": {
|
"invitee": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The ID of the user being called. If omitted, any user in the room can answer.",
|
"description": "The ID of the user being called. If omitted, any user in the room can answer.",
|
||||||
"x-addedInMatrixVersion": "1.7",
|
"x-addedInMatrixVersion": "1.7"
|
||||||
|
},
|
||||||
|
"sdp_stream_metadata": {
|
||||||
|
"$ref": "components/sdp_stream_metadata.yaml"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["offer", "lifetime"]
|
"required": ["offer", "lifetime"]
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,8 @@ properties:
|
||||||
type: integer
|
type: integer
|
||||||
description: The time in milliseconds that the negotiation is valid for.
|
description: The time in milliseconds that the negotiation is valid for.
|
||||||
Once the negotiation age exceeds this value, clients should discard it.
|
Once the negotiation age exceeds this value, clients should discard it.
|
||||||
|
sdp_stream_metadata:
|
||||||
|
$ref: components/sdp_stream_metadata.yaml
|
||||||
required:
|
required:
|
||||||
- description
|
- description
|
||||||
- lifetime
|
- lifetime
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
{{/*
|
{{/*
|
||||||
|
|
||||||
Renders an event example. Resolves `$ref`s, serializes as JSON, and ensures
|
Renders an event example. Resolves `$ref`s, serializes as JSON, and ensures
|
||||||
that it can be included in HTML.
|
that it can be included in HTML.
|
||||||
|
|
||||||
This partial is called with the example event object as its context.
|
Parameters:
|
||||||
|
|
||||||
|
* `schema`: the schema of the example
|
||||||
|
* `name`: the name of the example
|
||||||
|
|
||||||
*/}}
|
*/}}
|
||||||
|
|
||||||
{{ $example_content := partial "json-schema/resolve-refs" (dict "schema" . "path" "event-schemas/examples") }}
|
{{ $path := delimit (slice "event-schemas/examples" .name) "/" }}
|
||||||
|
|
||||||
|
{{ $example_content := partial "json-schema/resolve-refs" (dict "schema" .schema "path" $path) }}
|
||||||
{{ $example_json := jsonify (dict "indent" " ") $example_content }}
|
{{ $example_json := jsonify (dict "indent" " ") $example_content }}
|
||||||
{{ $example_json = replace $example_json "\\u003c" "<" }}
|
{{ $example_json = replace $example_json "\\u003c" "<" }}
|
||||||
{{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
|
{{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@
|
||||||
*/}}
|
*/}}
|
||||||
{{ if $desired_example_name }}
|
{{ if $desired_example_name }}
|
||||||
{{ if eq $example_name $desired_example_name }}
|
{{ if eq $example_name $desired_example_name }}
|
||||||
{{ partial "events/example" $example }}
|
{{ partial "events/example" (dict "schema" $example "name" $example_name) }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{/*
|
{{/*
|
||||||
If `$desired_example_name` is not given, we will include any
|
If `$desired_example_name` is not given, we will include any
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
the event name includes a "$".
|
the event name includes a "$".
|
||||||
*/}}
|
*/}}
|
||||||
{{ else if eq $event_name $example_name }}
|
{{ else if eq $event_name $example_name }}
|
||||||
{{ partial "events/example" $example }}
|
{{ partial "events/example" (dict "schema" $example "name" $example_name) }}
|
||||||
{{/*
|
{{/*
|
||||||
If `$desired_example_name` is not given, we will include any
|
If `$desired_example_name` is not given, we will include any
|
||||||
examples whose first part (before "$") matches the event name
|
examples whose first part (before "$") matches the event name
|
||||||
|
|
@ -96,7 +96,7 @@
|
||||||
{{ $pieces := split $example_name "$" }}
|
{{ $pieces := split $example_name "$" }}
|
||||||
{{ $example_base_name := index $pieces 0 }}
|
{{ $example_base_name := index $pieces 0 }}
|
||||||
{{ if eq $event_name $example_base_name }}
|
{{ if eq $event_name $example_base_name }}
|
||||||
{{ partial "events/example" $example }}
|
{{ partial "events/example" (dict "schema" $example "name" $example_name) }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
{{/*
|
{{/*
|
||||||
|
|
||||||
Resolves the `$ref` JSON schema keyword, by recursively replacing
|
Resolves the `$ref` JSON schema keyword, by recursively replacing
|
||||||
it with the object it points to.
|
it with the object it points to, given:
|
||||||
|
|
||||||
|
* `schema`: the schema where the references should be resolved
|
||||||
|
* `path`: the path of the file containing the schema
|
||||||
|
|
||||||
This template uses [`Scratch`](https://gohugo.io/functions/scratch/)
|
This template uses [`Scratch`](https://gohugo.io/functions/scratch/)
|
||||||
rather than a normal `dict` because with `dict` you can't replace key values:
|
rather than a normal `dict` because with `dict` you can't replace key values:
|
||||||
|
|
@ -20,8 +23,12 @@
|
||||||
{{ $scratch.Set "result_map" dict }}
|
{{ $scratch.Set "result_map" dict }}
|
||||||
|
|
||||||
{{ $ref_value := index $schema "$ref"}}
|
{{ $ref_value := index $schema "$ref"}}
|
||||||
{{ if $ref_value}}
|
{{ if $ref_value }}
|
||||||
{{ $full_path := path.Join $path $ref_value }}
|
{{ $uri := urls.Parse $path }}
|
||||||
|
{{ $ref_uri := urls.Parse $ref_value }}
|
||||||
|
{{ $full_uri := $uri.ResolveReference $ref_uri }}
|
||||||
|
|
||||||
|
{{ $full_path := strings.TrimPrefix "/" $full_uri.Path }}
|
||||||
{{/*
|
{{/*
|
||||||
Apparently Hugo doesn't give us a nice way to split the extension off a filename.
|
Apparently Hugo doesn't give us a nice way to split the extension off a filename.
|
||||||
*/}}
|
*/}}
|
||||||
|
|
@ -30,11 +37,18 @@
|
||||||
|
|
||||||
{{ $ref := index site.Data $pieces }}
|
{{ $ref := index site.Data $pieces }}
|
||||||
|
|
||||||
|
{{/* If there is a fragment, follow the JSON Pointer */}}
|
||||||
|
{{ if $full_uri.Fragment }}
|
||||||
|
{{ $fragment := strings.TrimPrefix "/" $full_uri.Fragment }}
|
||||||
|
{{ $pieces := split $fragment "/" }}
|
||||||
|
{{ $ref = index $ref $pieces }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ $new_path := (path.Split $full_path).Dir}}
|
{{ $new_path := (path.Split $full_path).Dir}}
|
||||||
{{ $result_map := partial "json-schema/resolve-refs" (dict "schema" $ref "path" $new_path)}}
|
{{ $result_map := partial "json-schema/resolve-refs" (dict "schema" $ref "path" $new_path)}}
|
||||||
{{ if $result_map}}
|
{{ if $result_map}}
|
||||||
{{ $scratch.Set "result_map" $result_map }}
|
{{ $scratch.Set "result_map" $result_map }}
|
||||||
{{end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
* `parameters`: OpenAPI data specifying the parameters
|
* `parameters`: OpenAPI data specifying the parameters
|
||||||
* `type`: the type of parameters to render: "header, ""path", "query"
|
* `type`: the type of parameters to render: "header, ""path", "query"
|
||||||
* `caption`: caption to use for the table
|
* `caption`: caption to use for the table
|
||||||
|
* `path`: the path where this definition was found, to enable us to resolve "$ref"
|
||||||
|
|
||||||
This template renders a single table containing parameters of the given type.
|
This template renders a single table containing parameters of the given type.
|
||||||
|
|
||||||
|
|
@ -13,7 +14,9 @@
|
||||||
{{ $parameters := .parameters }}
|
{{ $parameters := .parameters }}
|
||||||
{{ $type := .type }}
|
{{ $type := .type }}
|
||||||
{{ $caption := .caption }}
|
{{ $caption := .caption }}
|
||||||
|
{{ $path := .path }}
|
||||||
|
|
||||||
|
{{ $parameters = partial "json-schema/resolve-refs" (dict "schema" $parameters "path" $path) }}
|
||||||
{{ $parameters_of_type := where $parameters "in" $type }}
|
{{ $parameters_of_type := where $parameters "in" $type }}
|
||||||
|
|
||||||
{{ with $parameters_of_type }}
|
{{ with $parameters_of_type }}
|
||||||
|
|
@ -32,5 +35,4 @@
|
||||||
|
|
||||||
{{/* and render the parameters */}}
|
{{/* and render the parameters */}}
|
||||||
{{ partial "openapi/render-object-table" (dict "title" $caption "properties" $param_dict) }}
|
{{ partial "openapi/render-object-table" (dict "title" $caption "properties" $param_dict) }}
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@
|
||||||
{{ if $parameters }}
|
{{ if $parameters }}
|
||||||
<h3>Request parameters</h3>
|
<h3>Request parameters</h3>
|
||||||
|
|
||||||
{{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "header" "caption" "header parameters") }}
|
{{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "header" "caption" "header parameters" "path" .path) }}
|
||||||
{{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "path" "caption" "path parameters") }}
|
{{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "path" "caption" "path parameters" "path" .path) }}
|
||||||
{{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "query" "caption" "query parameters") }}
|
{{ partial "openapi/render-parameters" (dict "parameters" $parameters "type" "query" "caption" "query parameters" "path" .path) }}
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@
|
||||||
<th class="col-status-description">Description</th>
|
<th class="col-status-description">Description</th>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
|
{{ $responses = partial "json-schema/resolve-refs" (dict "schema" $responses "path" $path) }}
|
||||||
|
|
||||||
{{ range $code, $response := $responses }}
|
{{ range $code, $response := $responses }}
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -49,8 +51,7 @@
|
||||||
Display the JSON schemas
|
Display the JSON schemas
|
||||||
*/}}
|
*/}}
|
||||||
|
|
||||||
{{ $schema := partial "json-schema/resolve-refs" (dict "schema" $json_body.schema "path" $path) }}
|
{{ $schema := partial "json-schema/resolve-allof" $json_body.schema }}
|
||||||
{{ $schema := partial "json-schema/resolve-allof" $schema }}
|
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
All this is to work out how to express the content of the response
|
All this is to work out how to express the content of the response
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,6 @@
|
||||||
{{ errorf "site data %s not found" $path }}
|
{{ errorf "site data %s not found" $path }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{/* The base path, which we use to resolve $ref, omits the last component */}}
|
|
||||||
{{ $pieces = first (sub (len $pieces) 1) $pieces}}
|
|
||||||
{{ $path = delimit $pieces "/" }}
|
|
||||||
|
|
||||||
{{/* Resolve $ref and allOf */}}
|
{{/* Resolve $ref and allOf */}}
|
||||||
{{ $definition = partial "json-schema/resolve-refs" (dict "schema" $definition "path" $path) }}
|
{{ $definition = partial "json-schema/resolve-refs" (dict "schema" $definition "path" $path) }}
|
||||||
{{ $definition = partial "json-schema/resolve-allof" $definition }}
|
{{ $definition = partial "json-schema/resolve-allof" $definition }}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
*/}}
|
*/}}
|
||||||
|
|
||||||
{{ $event_data := index .Site.Data "event-schemas" "schema" .Params.event }}
|
{{ $event_data := index .Site.Data "event-schemas" "schema" .Params.event }}
|
||||||
{{ $path := "event-schemas/schema" }}
|
{{ $path := delimit (slice "event-schemas/schema" .Params.event) "/" }}
|
||||||
|
|
||||||
{{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
|
{{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
|
||||||
{{ $event_data := partial "json-schema/resolve-allof" $event_data }}
|
{{ $event_data := partial "json-schema/resolve-allof" $event_data }}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,6 @@
|
||||||
|
|
||||||
{{ $api_data := index .Site.Data.api .Params.spec .Params.api }}
|
{{ $api_data := index .Site.Data.api .Params.spec .Params.api }}
|
||||||
{{ $base_url := (index $api_data.servers 0).variables.basePath.default }}
|
{{ $base_url := (index $api_data.servers 0).variables.basePath.default }}
|
||||||
{{ $path := delimit (slice "api" $spec) "/" }}
|
{{ $path := delimit (slice "api" $spec $api) "/" }}
|
||||||
|
|
||||||
{{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url "path" $path) }}
|
{{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url "path" $path) }}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
*/}}
|
*/}}
|
||||||
|
|
||||||
{{ $path := "event-schemas/schema" }}
|
|
||||||
{{ $compact := false }}
|
{{ $compact := false }}
|
||||||
|
|
||||||
{{/*
|
{{/*
|
||||||
|
|
@ -40,6 +39,7 @@
|
||||||
{{ range $msgtypes }}
|
{{ range $msgtypes }}
|
||||||
|
|
||||||
{{ $event_data := index $site_data "event-schemas" "schema" . }}
|
{{ $event_data := index $site_data "event-schemas" "schema" . }}
|
||||||
|
{{ $path := delimit (slice "event-schemas/schema" .) "/" }}
|
||||||
{{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
|
{{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
|
||||||
{{ $event_data := partial "json-schema/resolve-allof" $event_data }}
|
{{ $event_data := partial "json-schema/resolve-allof" $event_data }}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import helpers
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
@ -48,51 +49,16 @@ except ImportError as e:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def load_file(path):
|
|
||||||
print("Loading reference: %s" % path)
|
|
||||||
if not path.startswith("file://"):
|
|
||||||
raise Exception("Bad ref: %s" % (path,))
|
|
||||||
path = path[len("file://"):]
|
|
||||||
with open(path, "r") as f:
|
|
||||||
if path.endswith(".json"):
|
|
||||||
return json.load(f)
|
|
||||||
else:
|
|
||||||
# We have to assume it's YAML because some of the YAML examples
|
|
||||||
# do not have file extensions.
|
|
||||||
return yaml.safe_load(f)
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_references(path, schema):
|
|
||||||
if isinstance(schema, dict):
|
|
||||||
# do $ref first
|
|
||||||
if '$ref' in schema:
|
|
||||||
value = schema['$ref']
|
|
||||||
path = os.path.abspath(os.path.join(os.path.dirname(path), value))
|
|
||||||
ref = load_file("file://" + path)
|
|
||||||
result = resolve_references(path, ref)
|
|
||||||
del schema['$ref']
|
|
||||||
else:
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
for key, value in schema.items():
|
|
||||||
result[key] = resolve_references(path, value)
|
|
||||||
return result
|
|
||||||
elif isinstance(schema, list):
|
|
||||||
return [resolve_references(path, value) for value in schema]
|
|
||||||
else:
|
|
||||||
return schema
|
|
||||||
|
|
||||||
|
|
||||||
def check_example_file(examplepath, schemapath):
|
def check_example_file(examplepath, schemapath):
|
||||||
with open(examplepath) as f:
|
with open(examplepath) as f:
|
||||||
example = resolve_references(examplepath, json.load(f))
|
example = helpers.resolve_references(examplepath, json.load(f))
|
||||||
|
|
||||||
with open(schemapath) as f:
|
with open(schemapath) as f:
|
||||||
schema = yaml.safe_load(f)
|
schema = yaml.safe_load(f)
|
||||||
|
|
||||||
fileurl = "file://" + os.path.abspath(schemapath)
|
fileurl = "file://" + os.path.abspath(schemapath)
|
||||||
schema["id"] = fileurl
|
schema["id"] = fileurl
|
||||||
resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": load_file})
|
resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": helpers.load_file_from_uri})
|
||||||
|
|
||||||
print ("Checking schema for: %r %r" % (examplepath, schemapath))
|
print ("Checking schema for: %r %r" % (examplepath, schemapath))
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import helpers
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
@ -68,22 +69,10 @@ class SchemaDirReport:
|
||||||
self.files += other_report.files
|
self.files += other_report.files
|
||||||
self.errors += other_report.errors
|
self.errors += other_report.errors
|
||||||
|
|
||||||
def load_file(path):
|
|
||||||
if not path.startswith("file://"):
|
|
||||||
raise Exception(f"Bad ref: {path}")
|
|
||||||
path = path[len("file://"):]
|
|
||||||
with open(path, "r") as f:
|
|
||||||
if path.endswith(".json"):
|
|
||||||
return json.load(f)
|
|
||||||
else:
|
|
||||||
# We have to assume it's YAML because some of the YAML examples
|
|
||||||
# do not have file extensions.
|
|
||||||
return yaml.safe_load(f)
|
|
||||||
|
|
||||||
def check_example(path, schema, example):
|
def check_example(path, schema, example):
|
||||||
# URI with scheme is necessary to make RefResolver work.
|
# URI with scheme is necessary to make RefResolver work.
|
||||||
fileurl = "file://" + os.path.abspath(path)
|
fileurl = "file://" + os.path.abspath(path)
|
||||||
resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": load_file})
|
resolver = jsonschema.RefResolver(fileurl, schema, handlers={"file": helpers.load_file_from_uri})
|
||||||
validator = jsonschema.Draft202012Validator(schema, resolver)
|
validator = jsonschema.Draft202012Validator(schema, resolver)
|
||||||
|
|
||||||
validator.validate(example)
|
validator.validate(example)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import helpers
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
@ -49,9 +50,7 @@ except ImportError as e:
|
||||||
|
|
||||||
|
|
||||||
def check_schema(filepath, example, schema):
|
def check_schema(filepath, example, schema):
|
||||||
example = resolve_references(filepath, example)
|
resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": helpers.load_file_from_uri})
|
||||||
schema = resolve_references(filepath, schema)
|
|
||||||
resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_file})
|
|
||||||
validator = jsonschema.Draft202012Validator(schema, resolver)
|
validator = jsonschema.Draft202012Validator(schema, resolver)
|
||||||
validator.validate(example)
|
validator.validate(example)
|
||||||
|
|
||||||
|
|
@ -120,6 +119,8 @@ def check_openapi_file(filepath):
|
||||||
with open(filepath) as f:
|
with open(filepath) as f:
|
||||||
openapi = yaml.safe_load(f)
|
openapi = yaml.safe_load(f)
|
||||||
|
|
||||||
|
openapi = helpers.resolve_references(filepath, openapi)
|
||||||
|
|
||||||
openapi_version = openapi.get('openapi')
|
openapi_version = openapi.get('openapi')
|
||||||
if not openapi_version:
|
if not openapi_version:
|
||||||
# This is not an OpenAPI file, skip.
|
# This is not an OpenAPI file, skip.
|
||||||
|
|
@ -149,64 +150,6 @@ def check_openapi_file(filepath):
|
||||||
check_response(filepath, request, code, json_response)
|
check_response(filepath, request, code, json_response)
|
||||||
|
|
||||||
|
|
||||||
def resolve_references(path, schema):
|
|
||||||
"""Recurse through a given schema until we find a $ref key. Upon doing so,
|
|
||||||
check that the referenced file exists, then load it up and check all of the
|
|
||||||
references in that file. Continue on until we've hit all dead ends.
|
|
||||||
|
|
||||||
$ref values are deleted from schemas as they are validated, to prevent
|
|
||||||
duplicate work.
|
|
||||||
"""
|
|
||||||
if isinstance(schema, dict):
|
|
||||||
# do $ref first
|
|
||||||
if '$ref' in schema:
|
|
||||||
# Pull the referenced filepath from the schema
|
|
||||||
referenced_file = schema['$ref']
|
|
||||||
|
|
||||||
# Referenced filepaths are relative, so take the current path's
|
|
||||||
# directory and append the relative, referenced path to it.
|
|
||||||
inner_path = os.path.join(os.path.dirname(path), referenced_file)
|
|
||||||
|
|
||||||
# Then convert the path (which may contiain '../') into a
|
|
||||||
# normalised, absolute path
|
|
||||||
inner_path = os.path.abspath(inner_path)
|
|
||||||
|
|
||||||
# Load the referenced file
|
|
||||||
ref = load_file("file://" + inner_path)
|
|
||||||
|
|
||||||
# Check that the references in *this* file are valid
|
|
||||||
result = resolve_references(inner_path, ref)
|
|
||||||
|
|
||||||
# They were valid, and so were the sub-references. Delete
|
|
||||||
# the reference here to ensure we don't pass over it again
|
|
||||||
# when checking other files
|
|
||||||
del schema['$ref']
|
|
||||||
else:
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
for key, value in schema.items():
|
|
||||||
result[key] = resolve_references(path, value)
|
|
||||||
return result
|
|
||||||
elif isinstance(schema, list):
|
|
||||||
return [resolve_references(path, value) for value in schema]
|
|
||||||
else:
|
|
||||||
return schema
|
|
||||||
|
|
||||||
|
|
||||||
def load_file(path):
|
|
||||||
print("Loading reference: %s" % path)
|
|
||||||
if not path.startswith("file://"):
|
|
||||||
raise Exception("Bad ref: %s" % (path,))
|
|
||||||
path = path[len("file://"):]
|
|
||||||
with open(path, "r") as f:
|
|
||||||
if path.endswith(".json"):
|
|
||||||
return json.load(f)
|
|
||||||
else:
|
|
||||||
# We have to assume it's YAML because some of the YAML examples
|
|
||||||
# do not have file extensions.
|
|
||||||
return yaml.safe_load(f)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# Get the directory that this script is residing in
|
# Get the directory that this script is residing in
|
||||||
script_directory = os.path.dirname(os.path.realpath(__file__))
|
script_directory = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import errno
|
import errno
|
||||||
|
import helpers
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
|
|
@ -31,34 +32,6 @@ import yaml
|
||||||
scripts_dir = os.path.dirname(os.path.abspath(__file__))
|
scripts_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
api_dir = os.path.join(os.path.dirname(scripts_dir), "data", "api")
|
api_dir = os.path.join(os.path.dirname(scripts_dir), "data", "api")
|
||||||
|
|
||||||
def resolve_references(path, schema):
|
|
||||||
if isinstance(schema, dict):
|
|
||||||
# do $ref first
|
|
||||||
if '$ref' in schema:
|
|
||||||
value = schema['$ref']
|
|
||||||
previous_path = path
|
|
||||||
path = os.path.join(os.path.dirname(path), value)
|
|
||||||
try:
|
|
||||||
with open(path, encoding="utf-8") as f:
|
|
||||||
ref = yaml.safe_load(f)
|
|
||||||
result = resolve_references(path, ref)
|
|
||||||
del schema['$ref']
|
|
||||||
path = previous_path
|
|
||||||
except FileNotFoundError:
|
|
||||||
print("Resolving {}".format(schema))
|
|
||||||
print("File not found: {}".format(path))
|
|
||||||
result = {}
|
|
||||||
else:
|
|
||||||
result = {}
|
|
||||||
|
|
||||||
for key, value in schema.items():
|
|
||||||
result[key] = resolve_references(path, value)
|
|
||||||
return result
|
|
||||||
elif isinstance(schema, list):
|
|
||||||
return [resolve_references(path, value) for value in schema]
|
|
||||||
else:
|
|
||||||
return schema
|
|
||||||
|
|
||||||
def prefix_absolute_path_references(text, base_url):
|
def prefix_absolute_path_references(text, base_url):
|
||||||
"""Adds base_url to absolute-path references.
|
"""Adds base_url to absolute-path references.
|
||||||
|
|
||||||
|
|
@ -176,7 +149,7 @@ for filename in os.listdir(selected_api_dir):
|
||||||
print("Reading OpenAPI: %s" % filepath)
|
print("Reading OpenAPI: %s" % filepath)
|
||||||
with open(filepath, "r") as f:
|
with open(filepath, "r") as f:
|
||||||
api = yaml.safe_load(f.read())
|
api = yaml.safe_load(f.read())
|
||||||
api = resolve_references(filepath, api)
|
api = helpers.resolve_references(filepath, api)
|
||||||
|
|
||||||
basePath = api['servers'][0]['variables']['basePath']['default']
|
basePath = api['servers'][0]['variables']['basePath']['default']
|
||||||
for path, methods in api["paths"].items():
|
for path, methods in api["paths"].items():
|
||||||
|
|
|
||||||
87
scripts/helpers.py
Executable file
87
scripts/helpers.py
Executable file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Helpers to resolve $ref recursively in OpenAPI and JSON schemas.
|
||||||
|
|
||||||
|
# Copyright 2016 OpenMarket Ltd
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import urllib.parse
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
def resolve_references(path, schema):
|
||||||
|
"""Recurse through a given schema until we find a $ref key. Upon doing so,
|
||||||
|
check that the referenced file exists, then load it up and check all of the
|
||||||
|
references in that file. Continue on until we've hit all dead ends.
|
||||||
|
|
||||||
|
$ref values are deleted from schemas as they are validated, to prevent
|
||||||
|
duplicate work.
|
||||||
|
"""
|
||||||
|
if isinstance(schema, dict):
|
||||||
|
# do $ref first
|
||||||
|
if '$ref' in schema:
|
||||||
|
# Pull the referenced URI from the schema
|
||||||
|
ref_uri = schema['$ref']
|
||||||
|
|
||||||
|
# Join the referenced URI with the URI of the file, to resolve
|
||||||
|
# relative URIs
|
||||||
|
full_ref_uri = urllib.parse.urljoin("file://" + path, ref_uri)
|
||||||
|
|
||||||
|
# Separate the fragment.
|
||||||
|
(full_ref_uri, fragment) = urllib.parse.urldefrag(full_ref_uri)
|
||||||
|
|
||||||
|
# Load the referenced file
|
||||||
|
ref = load_file_from_uri(full_ref_uri)
|
||||||
|
|
||||||
|
if fragment:
|
||||||
|
# The fragment should be a JSON Pointer
|
||||||
|
keys = fragment.strip('/').split('/')
|
||||||
|
for key in keys:
|
||||||
|
ref = ref[key]
|
||||||
|
|
||||||
|
# Check that the references in *this* file are valid
|
||||||
|
result = resolve_references(urllib.parse.urlsplit(full_ref_uri).path, ref)
|
||||||
|
|
||||||
|
# They were valid, and so were the sub-references. Delete
|
||||||
|
# the reference here to ensure we don't pass over it again
|
||||||
|
# when checking other files
|
||||||
|
del schema['$ref']
|
||||||
|
else:
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for key, value in schema.items():
|
||||||
|
result[key] = resolve_references(path, value)
|
||||||
|
return result
|
||||||
|
elif isinstance(schema, list):
|
||||||
|
return [resolve_references(path, value) for value in schema]
|
||||||
|
else:
|
||||||
|
return schema
|
||||||
|
|
||||||
|
|
||||||
|
def load_file_from_uri(path):
|
||||||
|
"""Load a JSON or YAML file from a file:// URI.
|
||||||
|
"""
|
||||||
|
print("Loading reference: %s" % path)
|
||||||
|
if not path.startswith("file://"):
|
||||||
|
raise Exception("Bad ref: %s" % (path,))
|
||||||
|
path = path[len("file://"):]
|
||||||
|
with open(path, "r") as f:
|
||||||
|
if path.endswith(".json"):
|
||||||
|
return json.load(f)
|
||||||
|
else:
|
||||||
|
# We have to assume it's YAML because some of the YAML examples
|
||||||
|
# do not have file extensions.
|
||||||
|
return yaml.safe_load(f)
|
||||||
Loading…
Reference in a new issue