Compare commits

...

12 commits

Author SHA1 Message Date
Andrew Morgan 665fb3d9b0
Merge 715e66fa00 into 7c39427d8b 2025-12-17 13:15:02 +00:00
Kévin Commaille 7c39427d8b
Spec device management for application services (#2267)
Some checks failed
Spec / 🔎 Validate OpenAPI specifications (push) Has been cancelled
Spec / 🔎 Check Event schema examples (push) Has been cancelled
Spec / 🔎 Check OpenAPI definitions examples (push) Has been cancelled
Spec / 🔎 Check JSON Schemas inline examples (push) Has been cancelled
Spec / ⚙️ Calculate baseURL for later jobs (push) Has been cancelled
Spec / 📢 Run towncrier for changelog (push) Has been cancelled
Spell Check / Spell Check with Typos (push) Has been cancelled
Spec / 🐍 Build OpenAPI definitions (push) Has been cancelled
Spec / 📖 Build the spec (push) Has been cancelled
Spec / 🔎 Validate generated HTML (push) Has been cancelled
Spec / 📖 Build the historical backup spec (push) Has been cancelled
As per MSC4190.

Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
2025-12-17 13:13:51 +00:00
Andrew Morgan 715e66fa00 Improve styling of the endpoints list
- Add a hover effect over endpoints
- Make the method bold to differentiate it from the endpoint itself
- Make cs module titles larger
2025-12-10 20:49:17 +00:00
Andrew Morgan f30e850bf1 Fix duplicate modules endpoints
I was seeing duplicate endpoints appearing under each module (typically
2 of each). This turned out to be due to the `render-operation` partial
being called multiple times (once when rendering the page, and another
when rendering the left-hand-side TOC).

We now check whether the endpoint has already been added to the list
before insertion, via a "seen" map (for quick lookup).
2025-12-10 20:34:16 +00:00
Andrew Morgan 468fcb3992 Group endpoints by module
Makes the list of endpoints easier to scan.
2025-12-05 19:06:44 +00:00
Andrew Morgan 2d0e4ea471 Source endpoints from CS API modules as well
Any endpoint in a CS API module was not getting listed. This is because they are stored in nested pages. We need to hoist them up to the outer page, such that they can be retrieved and rendered by our endpoints TOC partial.
2025-12-05 19:06:10 +00:00
Andrew Morgan 42a1bc370f newsfile 2025-12-05 15:57:28 +00:00
Andrew Morgan 17acd978a4 Display "(deprecated)" next to deprecated endpoints 2025-12-05 15:48:20 +00:00
Andrew Morgan 1213f81676 Move each spec's intro above the endpoints list
This just looks visually nicer. You have an introduction paragraph, then
the list of endpoints, then the spec.
2025-12-05 15:47:08 +00:00
Andrew Morgan f03730455d Place the endpoints list just under the description of each API
We needed to override Docsy's default page renderers to show it. Because
the Client-Server API uses a different render chain than the other
endpoint pages, we have to override two of Docsy's pages (`content.html` and `single.html`).

In order to reduce duplication, we then put the shared content into a
`spec-content.html` partial.
2025-12-05 15:46:16 +00:00
Andrew Morgan 56d48eb3a8 Add an endpoints table of contents
A simple list of endpoints that one can ctrl-f through.
2025-12-05 15:12:15 +00:00
Andrew Morgan 82a67a1fab Store each endpoint's metadata in a slice on Page
Such that we can later construct the table of contents with the data.
2025-12-05 15:11:17 +00:00
23 changed files with 451 additions and 112 deletions

View file

@ -257,6 +257,69 @@ Custom SCSS for the Matrix spec
}
.endpoints-toc {
summary {
cursor: pointer;
font-weight: $font-weight-bold;
font-size: 1.05rem;
margin-bottom: 0.5rem;
}
.endpoint-list {
list-style: none;
padding-left: 0;
margin: 0;
}
.endpoint-list li {
margin: 0.2rem 0;
}
.endpoint-list a {
text-decoration: none;
color: inherit;
padding: 0.05rem 0.25rem;
border-radius: 0.2rem;
&:hover {
background-color: $secondary-background;
}
}
.endpoint-list .http-api-method {
margin-right: 0.35rem;
font-weight: $font-weight-bold;
}
.endpoint-path {
font-family: $font-family-monospace;
color: $secondary;
}
.endpoint-deprecated {
color: $danger;
font-weight: $font-weight-bold;
margin-left: 0.35rem;
}
.endpoint-module {
&:not(:first-child) {
margin-top: 0.75rem;
}
}
.endpoint-module-title {
// font-weight: $font-weight-bold;
font-size: 1.20rem;
margin-bottom: 0.35rem;
}
}
.page-description {
margin-bottom: 1rem;
color: inherit;
}
/* Styles for alert boxes */
.alert {
&.note {
@ -581,4 +644,4 @@ dd {
.breadcrumb {
margin: 0;
}
}
}

View file

@ -0,0 +1 @@
Allow application services to manage devices and register users without the legacy authentication API, as per [MSC4190](https://github.com/matrix-org/matrix-spec-proposals/pull/4190).

View file

@ -0,0 +1 @@
Allow application services to manage devices and register users without the legacy authentication API, as per [MSC4190](https://github.com/matrix-org/matrix-spec-proposals/pull/4190).

View file

@ -0,0 +1 @@
Add a list of endpoints to the top of each spec page.

View file

@ -2,16 +2,14 @@
title: "Application Service API"
weight: 30
type: docs
description: |
The Matrix client-server API and server-server APIs provide a consistent,
self-contained federated messaging fabric but leave little room for custom
server-side behaviour such as gateways, filters, or extensible hooks. The
Application Service API defines a standard way to add this extensible
functionality, independent of the underlying homeserver implementation.
---
The Matrix client-server API and server-server APIs provide the means to
implement a consistent self-contained federated messaging fabric.
However, they provide limited means of implementing custom server-side
behaviour in Matrix (e.g. gateways, filters, extensible hooks etc). The
Application Service API (AS API) defines a standard API to allow such
extensible functionality to be implemented irrespective of the
underlying homeserver implementation.
## Application Services
Application services are passive and can only observe events from the
@ -428,6 +426,8 @@ imports and similar behaviour).
#### Server admin style permissions
{{% changed-in v="1.17" %}}
The homeserver needs to give the application service *full control* over
its namespace, both for users and for room aliases. This means that the
AS should be able to manage any users and room alias in its namespace. No additional API
@ -444,33 +444,59 @@ achieved by including the `as_token` on a `/register` request, along
with a login type of `m.login.application_service` to set the desired
user ID without a password.
POST /_matrix/client/v3/register
Authorization: Bearer YourApplicationServiceTokenHere
```http
POST /_matrix/client/v3/register
Authorization: Bearer YourApplicationServiceTokenHere
```
Content:
{
"type": "m.login.application_service",
"username": "_irc_example"
}
```json
{
"type": "m.login.application_service",
"username": "_irc_example"
}
```
Similarly, logging in as users needs API changes in order to allow the AS to
log in without needing the user's password. This is achieved by including the
`as_token` on a `/login` request, along with a login type of
`m.login.application_service`:
{{% boxes/note %}}
{{% added-in v="1.17" %}}
Servers MUST still allow application services to use the `/register` endpoint
with a login type of `m.login.application_service` even if they don't support
the [Legacy Authentication API](/client-server-api/#legacy-api).
In that case application services MUST set the `"inhibit_login": true` parameter
as they cannot use it to log in as users. If the `inhibit_login` parameter is
not set to `true`, the server MUST return a 400 HTTP status code with an
`M_APPSERVICE_LOGIN_UNSUPPORTED` error code.
{{% /boxes/note %}}
Similarly, logging in as users using the [Legacy authentication API](/client-server-api/#legacy-api)
needs API changes in order to allow the AS to log in without needing the user's
password. This is achieved by including the `as_token` on a `/login` request,
along with a login type of `m.login.application_service`:
{{% added-in v="1.2" %}}
POST /_matrix/client/v3/login
Authorization: Bearer YourApplicationServiceTokenHere
```http
POST /_matrix/client/v3/login
Authorization: Bearer YourApplicationServiceTokenHere
```
Content:
{
"type": "m.login.application_service",
"identifier": {
"type": "m.id.user",
"user": "_irc_example"
}
}
```json
{
"type": "m.login.application_service",
"identifier": {
"type": "m.id.user",
"user": "_irc_example"
}
}
```
{{% boxes/note %}}
{{% added-in v="1.17" %}}
Application services MUST NOT use the `/login` endpoint if the server doesn't
support the Legacy authentication API. If `/login` is called with the
`m.login.application_service` login type the server MUST return a 400 HTTP
status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code.
{{% /boxes/note %}}
Application services which attempt to create users or aliases *outside*
of their defined namespaces, or log in as users outside of their defined
@ -512,6 +538,38 @@ client-server endpoint.
{{% http-api spec="client-server" api="appservice_room_directory" %}}
#### Device management
{{% added-in v="1.17" %}}
Application services need to be able to create and delete devices to manage the
encryption for their users without having to rely on `/login`, which also
generates an access token for the user, and which might not be available for
homeservers that only support the [OAuth 2.0 API](/client-server-api/#oauth-20-api).
##### Creating devices
Application services can use the [`PUT /_matrix/client/v3/devices/{deviceId}`](/client-server-api/#put_matrixclientv3devicesdeviceid)
endpoint to create new devices.
##### Deleting devices
The following endpoints used to delete devices MUST NOT require [User-Interactive
Authentication](/client-server-api/#user-interactive-authentication-api) when
used by an application service:
* [`DELETE /_matrix/client/v3/devices/{deviceId}`](/client-server-api/#delete_matrixclientv3devicesdeviceid)
* [`POST /_matrix/client/v3/delete_devices`](/client-server-api/#post_matrixclientv3delete_devices)
#### Cross-signing
{{% added-in v="1.17" %}}
Appservices need to be able to verify themselves and replace their cross-signing
keys, so the [`POST /_matrix/client/v3/keys/device_signing/upload`](/client-server-api/#post_matrixclientv3keysdevice_signingupload)
endpoint MUST NOT require [User-Interactive Authentication](/client-server-api/#user-interactive-authentication-api)
when used by an application service, even if cross-signing keys already exist.
### Referencing messages from a third-party network
Application services should include an `external_url` in the `content`

View file

@ -2,14 +2,14 @@
title: "Client-Server API"
weight: 10
type: docs
description: |
The client-server API allows clients to send messages, control rooms and
synchronise conversation history. It is designed to support both lightweight
clients which store no state and lazy-load data from the server as required,
as well as heavyweight clients which maintain a full local persistent copy of
server state.
---
The client-server API allows clients to
send messages, control rooms and synchronise conversation history. It is
designed to support both lightweight clients which store no state and
lazy-load data from the server as required - as well as heavyweight
clients which maintain a full local persistent copy of server state.
## API Standards
{{% boxes/note %}}
@ -477,8 +477,7 @@ the API that was used to obtain their current access token.
{{% boxes/note %}}
Currently the OAuth 2.0 API doesn't cover all the use cases of the legacy API,
such as automated applications that cannot use a web browser, or
user management by [application services](application-service-api/#server-admin-style-permissions).
such as automated applications that cannot use a web browser.
{{% /boxes/note %}}
### Authentication API discovery
@ -502,6 +501,12 @@ user must do that directly in the homeserver's web UI. However, the client can
signal to the homeserver that the user wishes to create a new account with the
[`prompt=create`](#user-registration) parameter during authorization.
{{% boxes/note %}}
{{% added-in v="1.17" %}}
Application services can use the `/register` endpoint to create users regardless
of the authentication API supported by the homeserver.
{{% /boxes/note %}}
### Login
With the legacy API, a client can obtain an access token by using one of the
@ -1562,6 +1567,10 @@ If the access token does correspond to an appservice, but the user id does
not lie within its namespace then the homeserver will respond with an
errcode of `M_EXCLUSIVE`.
{{% added-in v="1.17" %}} If this login type is used and the server doesn't
support logging in via the Legacy authentication API, it MUST return a 400 HTTP
status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code.
##### Login Fallback
If a client does not recognize any or all login flows it can use the

View file

@ -2,17 +2,15 @@
title: "Identity Service API"
weight: 40
type: docs
description: |
The Matrix client-server and server-server APIs are largely expressed in
Matrix user identifiers. Sometimes it is useful to refer to users by other
(“third-party”) identifiers such as email addresses or phone numbers. The
Identity Service API describes how mappings between 3PIDs and Matrix user
IDs can be established, validated, and used; in practice this has been
applied to email addresses and phone numbers.
---
The Matrix client-server and server-server APIs are largely expressed in
Matrix user identifiers. From time to time, it is useful to refer to
users by other ("third-party") identifiers, or "3PID"s, e.g. their email
address or phone number. This Identity Service Specification describes
how mappings between third-party identifiers and Matrix user identifiers
can be established, validated, and used. This description technically
may apply to any 3PID, but in practice has only been applied
specifically to email addresses and phone numbers.
## General principles
The purpose of an identity server is to validate, store, and answer

View file

@ -2,12 +2,11 @@
title: "Push Gateway API"
weight: 50
type: docs
description: |
Clients may want to receive push notifications when events are received at the
homeserver. This is managed by a distinct entity called the Push Gateway.
---
Clients may want to receive push notifications when events are received
at the homeserver. This is managed by a distinct entity called the Push
Gateway.
## Overview
A client's homeserver forwards information about received events to the

View file

@ -2,49 +2,46 @@
title: "Server-Server API"
weight: 20
type: docs
description: |
Matrix homeservers use the Federation APIs (also known as server-server APIs)
to communicate with each other. Homeservers use these APIs to push messages in
real-time, retrieve historic messages, and query profile or presence
information about users on other servers. The APIs are implemented over HTTPS,
with authentication provided by public key signatures both at the TLS
transport layer and in HTTP Authorization headers.
There are three main kinds of communication that occur between
homeservers:
Persistent Data Units (PDUs):
These events are broadcast from one homeserver to any others that have
joined the same room (identified by Room ID). They are persisted in
long-term storage and record the history of messages and state for a
room.
Like email, it is the responsibility of the originating server of a PDU
to deliver that event to its recipient servers. However PDUs are signed
using the originating server's private key so that it is possible to
deliver them through third-party servers.
Ephemeral Data Units (EDUs):
These events are pushed between pairs of homeservers. They are not
persisted and are not part of the history of a room, nor does the
receiving homeserver have to reply to them.
Queries:
These are single request/response interactions between a given pair of
servers, initiated by one side sending an HTTPS GET request to obtain
some information, and responded by the other. They are not persisted and
contain no long-term significant history. They simply request a snapshot
state at the instant the query is made.
EDUs and PDUs are further wrapped in an envelope called a Transaction,
which is transferred from the origin to the destination homeserver using
an HTTPS PUT request.
---
Matrix homeservers use the Federation APIs (also known as server-server
APIs) to communicate with each other. Homeservers use these APIs to push
messages to each other in real-time, to retrieve historic messages from
each other, and to query profile and presence information about users on
each other's servers.
The APIs are implemented using HTTPS requests between each of the
servers. These HTTPS requests are strongly authenticated using public
key signatures at the TLS transport layer and using public key
signatures in HTTP Authorization headers at the HTTP layer.
There are three main kinds of communication that occur between
homeservers:
Persistent Data Units (PDUs):
These events are broadcast from one homeserver to any others that have
joined the same room (identified by Room ID). They are persisted in
long-term storage and record the history of messages and state for a
room.
Like email, it is the responsibility of the originating server of a PDU
to deliver that event to its recipient servers. However PDUs are signed
using the originating server's private key so that it is possible to
deliver them through third-party servers.
Ephemeral Data Units (EDUs):
These events are pushed between pairs of homeservers. They are not
persisted and are not part of the history of a room, nor does the
receiving homeserver have to reply to them.
Queries:
These are single request/response interactions between a given pair of
servers, initiated by one side sending an HTTPS GET request to obtain
some information, and responded by the other. They are not persisted and
contain no long-term significant history. They simply request a snapshot
state at the instant the query is made.
EDUs and PDUs are further wrapped in an envelope called a Transaction,
which is transferred from the origin to the destination homeserver using
an HTTPS PUT request.
## API standards
The mandatory baseline for server-server communication in Matrix is

View file

@ -21,13 +21,17 @@ paths:
x-addedInMatrixVersion: "1.1"
x-changedInMatrixVersion:
"1.11": UIA is not always required for this endpoint.
"1.17": |-
This endpoint no longer requires User-Interactive Authentication when used by an
application service.
summary: Upload cross-signing keys.
description: |-
Publishes cross-signing keys for the user.
This API endpoint uses the [User-Interactive Authentication API](/client-server-api/#user-interactive-authentication-api).
This API endpoint uses the [User-Interactive Authentication API](/client-server-api/#user-interactive-authentication-api),
except when used by an application service.
User-Interactive Authentication MUST be performed, except in these cases:
User-Interactive Authentication MUST be performed for regular clients, except in these cases:
- there is no existing cross-signing master key uploaded to the homeserver, OR
- there is an existing cross-signing master key and it exactly matches the
cross-signing master key provided in the request body. If there are any additional

View file

@ -87,8 +87,21 @@ paths:
tags:
- Device management
put:
summary: Update a device
description: Updates the metadata on the given device.
summary: Create or update a device
x-changedInMatrixVersion:
"1.17": The ability to create new devices was added.
description: |-
Updates the metadata on the given device, or creates a new device.
The ability to create new devices is only available to application
services: regular clients may only update existing devices.
When a new device was created, the homeserver MUST return a 201 HTTP
status code. It MUST return a 200 HTTP status code if a device was
updated.
This endpoint is rate-limited for device creation. Servers MAY use login
rate limits.
operationId: updateDevice
security:
- accessTokenQuery: []
@ -127,19 +140,34 @@ paths:
examples:
response:
value: {}
"201":
description: |-
The device was successfully created by the application service.
content:
application/json:
schema:
type: object
examples:
response:
value: {}
"404":
description: The current user has no device with the given ID.
tags:
- Device management
delete:
summary: Delete a device
x-changedInMatrixVersion:
"1.17": |-
This endpoint no longer requires User-Interactive Authentication when used by an
application service.
description: |-
This API endpoint uses the [User-Interactive Authentication API](/client-server-api/#user-interactive-authentication-api).
This API endpoint uses the [User-Interactive Authentication API](/client-server-api/#user-interactive-authentication-api),
except when used by an application service.
Deletes the given device, and invalidates any access token associated with it.
{{% boxes/warning %}}
Since this endpoint uses User-Interactive Authentication, it cannot be used when the access token was obtained
When this endpoint requires User-Interactive Authentication, it cannot be used when the access token was obtained
via the [OAuth 2.0 API](/client-server-api/#oauth-20-api).
{{% /boxes/warning %}}
operationId: deleteDevice
@ -190,13 +218,18 @@ paths:
/delete_devices:
post:
summary: Bulk deletion of devices
x-changedInMatrixVersion:
"1.17": |-
This endpoint no longer requires User-Interactive Authentication when used by an
application service.
description: |-
This API endpoint uses the [User-Interactive Authentication API](/client-server-api/#user-interactive-authentication-api).
This API endpoint uses the [User-Interactive Authentication API](/client-server-api/#user-interactive-authentication-api),
except when used by an application service.
Deletes the given devices, and invalidates any access token associated with them.
{{% boxes/warning %}}
Since this endpoint uses User-Interactive Authentication, it cannot be used when the access token was obtained
When this endpoint requires User-Interactive Authentication, it cannot be used when the access token was obtained
via the [OAuth 2.0 API](/client-server-api/#oauth-20-api).
{{% /boxes/warning %}}
operationId: deleteDevices

View file

@ -243,8 +243,13 @@ paths:
}
}
"400":
description: Part of the request was invalid. For example, the login type may
not be recognised.
description: |-
Part of the request was invalid. For example, the login type may not be recognised.
Specific error codes used with this status code include:
* {{% added-in v="1.17" %}} `M_APPSERVICE_LOGIN_UNSUPPORTED`: an application service
used the `m.login.application_service` type, but the server doesn't support logging
in via the Legacy authentication API.
content:
application/json:
schema:

View file

@ -60,6 +60,18 @@ paths:
Any user ID returned by this API must conform to the grammar given in the
[Matrix specification](/appendices/#user-identifiers).
{{% boxes/note %}}
{{% added-in v="1.17" %}}
Even if the server doesn't support the Legacy authentication API, it
MUST support this endpoint for application services to be able to
[create users](/application-service-api/#server-admin-style-permissions).
In that case application services MUST set the `"inhibit_login": true`
parameter as they cannot use it to log in as users. If the
`inhibit_login` parameter is not set to `true`, the server MUST return a
400 HTTP status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code.
{{% /boxes/note %}}
operationId: register
parameters:
- in: query
@ -203,6 +215,9 @@ paths:
* `M_INVALID_USERNAME` : The desired user ID is not a valid user name.
* `M_EXCLUSIVE` : The desired user ID is in the exclusive namespace
claimed by an application service.
* {{% added-in v="1.17" %}} `M_APPSERVICE_LOGIN_UNSUPPORTED`: an application service
used the `m.login.application_service` type without setting `inhibit_login` to `true`,
but the server doesn't support logging in via the Legacy authentication API.
These errors may be returned at any stage of the registration process,
including after authentication if the requested user ID was registered

39
data/cs_modules.yaml Normal file
View file

@ -0,0 +1,39 @@
account_data: "Client Config"
admin: "Server Administration"
content_repo: "Content repository"
device_management: "Device Management"
dm: "Direct Messaging"
end_to_end_encryption: "End-to-End Encryption"
event_annotations: "Event annotations and reactions"
event_context: "Event Context"
event_replacements: "Event replacements"
guest_access: "Guest Access"
history_visibility: "Room History Visibility"
ignore_users: "Ignoring Users"
instant_messaging: "Instant Messaging"
mentions: "User and room mentions"
moderation_policies: "Moderation policy lists"
openid: "OpenID"
presence: "Presence"
push: "Push Notifications"
read_markers: "Read and unread markers"
receipts: "Receipts"
reference_relations: "Reference relations"
report_content: "Reporting Content"
rich_replies: "Rich replies"
room_previews: "Room Previews"
room_upgrades: "Room Upgrades"
search: "Server Side Search"
secrets: "Secrets"
send_to_device: "Send-to-Device messaging"
server_acls: "Server Access Control Lists (ACLs) for rooms"
server_notices: "Server Notices"
spaces: "Spaces"
sso_login: "SSO client login/authentication"
stickers: "Sticker Messages"
tags: "Room Tagging"
third_party_invites: "Third-party invites"
third_party_networks: "Third-party Networks"
threading: "Threading"
typing_notifications: "Typing Notifications"
voip_events: "Voice over IP"

View file

@ -0,0 +1,42 @@
{{/* Minimal list of API endpoints for the current page. */}}
{{ $raw := .Scratch.Get "api_endpoints" }}
{{/* Normalize to a slice */}}
{{ $endpoints := slice }}
{{ if reflect.IsSlice $raw }}
{{ $endpoints = $raw }}
{{ else if reflect.IsMap $raw }}
{{ range $raw }}
{{ $endpoints = append $endpoints . }}
{{ end }}
{{ end }}
{{ if gt (len $endpoints) 0 }}
<div class="endpoints-toc mb-4">
<details>
<summary>List of Endpoints</summary>
{{/* Sort by module to group visually */}}
{{ $sorted := sort $endpoints "module" }}
{{ $current := "" }}
{{ range $sorted }}
{{ $mod := .module }}
{{/* Set a title for the base endpoints */}}
{{ if not $mod }}{{ $mod = "Required" }}{{ end }}
{{ if ne $mod $current }}
{{ if $current }}</ul></div>{{ end }}
<div class="endpoint-module">
<div class="endpoint-module-title">{{ $mod }}</div>
<ul class="endpoint-list">
{{ $current = $mod }}
{{ end }}
{{ $key := printf "%s|%s" .method .anchor }}
<li>
<a href="#{{ .anchor }}">
<span class="http-api-method">{{ .method }}</span>
<span class="endpoint-path">{{ .endpoint }}</span>
{{ if .deprecated }}<span class="endpoint-deprecated">(deprecated)</span>{{ end }}
</a>
</li>
{{ end }}
{{ if $current }}</ul></div>{{ end }}
</details>
</div>
{{ end }}

View file

@ -15,6 +15,8 @@
{{ $api_data := index .api_data }}
{{ $base_url := .base_url }}
{{ $anchor_base := .anchor_base }}
{{ $page := .page }}
{{ $module := .module }}
{{ range $path_name, $path_data := $api_data.paths }}
@ -26,28 +28,28 @@
{{ with $path_data.get }}
{{ $operation_params := merge $params (dict "method" "GET" "operation_data" . "anchor_base" $anchor_base) }}
{{ $operation_params := merge $params (dict "method" "GET" "operation_data" . "anchor_base" $anchor_base "page" $page "module" $module) }}
{{ partial "openapi/render-operation" $operation_params }}
{{ end }}
{{ with $path_data.post }}
{{ $operation_params := merge $params (dict "method" "POST" "operation_data" . "anchor_base" $anchor_base) }}
{{ $operation_params := merge $params (dict "method" "POST" "operation_data" . "anchor_base" $anchor_base "page" $page "module" $module) }}
{{ partial "openapi/render-operation" $operation_params }}
{{ end }}
{{ with $path_data.put }}
{{ $operation_params := merge $params (dict "method" "PUT" "operation_data" . "anchor_base" $anchor_base) }}
{{ $operation_params := merge $params (dict "method" "PUT" "operation_data" . "anchor_base" $anchor_base "page" $page "module" $module) }}
{{ partial "openapi/render-operation" $operation_params }}
{{ end }}
{{ with $path_data.delete }}
{{ $operation_params := merge $params (dict "method" "DELETE" "operation_data" . "anchor_base" $anchor_base) }}
{{ $operation_params := merge $params (dict "method" "DELETE" "operation_data" . "anchor_base" $anchor_base "page" $page "module" $module) }}
{{ partial "openapi/render-operation" $operation_params }}
{{ end }}

View file

@ -22,6 +22,8 @@
{{ $method := .method }}
{{ $endpoint := .endpoint }}
{{ $operation_data := .operation_data }}
{{ $page := .page }}
{{ $module := .module }}
{{ $anchor := "" }}
{{ if .anchor_base }}
@ -29,6 +31,27 @@
{{ end }}
{{ $anchor = printf "%s%s%s" $anchor (lower $method) (anchorize $endpoint) }}
{{/* Collect endpoints for the on-page endpoints TOC */}}
{{ if $page }}
{{/* Store each endpoint's metadata in a scratch variable */}}
{{ $entry := dict "anchor" $anchor "method" $method "endpoint" $endpoint "summary" $operation_data.summary "deprecated" $operation_data.deprecated "module" $module }}
{{ if not ($page.Scratch.Get "api_endpoints_seen") }}
{{ $page.Scratch.Set "api_endpoints_seen" dict }}
{{ end }}
{{/* Keep a map of seen endpoints. This is necessary as this partial may be
rendered multiple times for the same endpoint (e.g. in the TOC and
in the main content), leading to duplicates. */}}
{{ $seen := $page.Scratch.Get "api_endpoints_seen" }}
{{ $key := printf "%s|%s" $method $endpoint }}
{{ if not (index $seen $key) }}
{{ if not (reflect.IsSlice ($page.Scratch.Get "api_endpoints")) }}
{{ $page.Scratch.Set "api_endpoints" (slice) }}
{{ end }}
{{ $page.Scratch.Add "api_endpoints" (slice $entry) }}
{{ $page.Scratch.SetInMap "api_endpoints_seen" $key true }}
{{ end }}
{{ end }}
<section class="rendered-data">
<details {{ if not site.Params.ui.rendered_data_collapsed }}open{{ end }}>

View file

@ -0,0 +1,11 @@
{{- /* Shared render for spec pages: title, optional description, endpoints list, body, and last-mod info. */ -}}
<div class="td-content">
<h1>{{ .Title }}</h1>
{{ with .Params.description }}<p class="page-description">{{ . | markdownify }}</p>{{ end }}
{{ partial "endpoints-toc.html" . }}
{{ .Content }}
{{ partial "page-meta-lastmod.html" . }}
</div>

View file

@ -11,6 +11,34 @@
{{ with .Site.GetPage "client-server-api/modules" }}
{{ with .Resources.GetMatch (printf "%s%s" $name ".md") }}
{{/* Preserve previous scratch values so nested modules don't leak */}}
{{ $prevPage := .Scratch.Get "endpoint_page" }}
{{ $prevModule := .Scratch.Get "endpoint_module" }}
{{/* Allow endpoints rendered in the module to accumulate on the parent page */}}
{{ .Scratch.Set "endpoint_page" $.Page }}
{{/* Name the module for grouping in the endpoints list */}}
{{ $display := $.Get "title" }}
{{ if not $display }}
{{ $display = index $.Site.Data.cs_modules $name }}
{{ end }}
{{ if not $display }}
{{ $display = $name }}
{{ end }}
{{ .Scratch.Set "endpoint_module" $display }}
{{ .RenderShortcodes }}
{{/* Restore previous scratch values */}}
{{ if $prevPage }}
{{ .Scratch.Set "endpoint_page" $prevPage }}
{{ else }}
{{ .Scratch.Delete "endpoint_page" }}
{{ end }}
{{ if $prevModule }}
{{ .Scratch.Set "endpoint_module" $prevModule }}
{{ else }}
{{ .Scratch.Delete "endpoint_module" }}
{{ end }}
{{ end }}
{{ end }}

View file

@ -23,6 +23,13 @@
{{ $api := .Params.api}}
{{ $anchor_base := .Params.anchor_base}}
{{/* Allow an outer page to collect endpoints (used for modules). */}}
{{ $target_page := .Page }}
{{ with .Page.Scratch.Get "endpoint_page" }}
{{ $target_page = . }}
{{ end }}
{{ $module_name := .Page.Scratch.Get "endpoint_module" }}
{{ $api_data := index .Site.Data.api .Params.spec .Params.api }}
{{ $base_url := (index $api_data.servers 0).variables.basePath.default }}
{{ $path := delimit (slice "api" $spec $api) "/" }}
@ -30,4 +37,4 @@
{{ $api_data = partial "json-schema/resolve-refs" (dict "schema" $api_data "path" $path) }}
{{ $api_data = partial "json-schema/resolve-allof" $api_data }}
{{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url "anchor_base" $anchor_base) }}
{{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url "anchor_base" $anchor_base "page" $target_page "module" $module_name) }}

View file

@ -1,4 +1,3 @@
<div class="td-content">
<h1>{{ .Title }}</h1>
{{ .Content }}
</div>
{{ define "main" }}
{{ partial "spec-content.html" . }}
{{ end }}

View file

@ -7,7 +7,8 @@
{{ define "main" }}
<div class="td-content">
<h1>{{ .Title }}</h1>
{{ with .Params.description }}<div class="lead">{{ . | markdownify }}</div>{{ end }}
{{ with .Params.description }}<p class="page-description">{{ . | markdownify }}</p>{{ end }}
{{ partial "endpoints-toc.html" . }}
{{ .Content }}
</div>
{{ end }}

3
layouts/docs/single.html Normal file
View file

@ -0,0 +1,3 @@
{{ define "main" }}
{{ partial "spec-content.html" . }}
{{ end }}