Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document GraphQL #3704

Merged
merged 7 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .circleci/template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -435,9 +435,15 @@ jobs:
docs_build_deploy:
parallelism: 1
docker:
- image: cimg/python:3.9.0
- image: cimg/python:3.9.0-node
steps:
- checkout
- run:
name: Build GraphQL static docs
command: |
npm install --prefix=$HOME/.local --global spectaql
npx spectaql -t doc/graphql-api -f admin-graphql-doc.html doc/graphql-api/Admin-GraphQL_spectaql.yml
npx spectaql -C -J -t doc/graphql-api -f user-graphql-doc.html doc/graphql-api/User-GraphQL_spectaql.yml
- run:
name: Test that docs build
command: |
Expand Down
84 changes: 84 additions & 0 deletions doc/configuration/listen.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ There are the following options for each of the HTTP listeners:

* `mod_bosh` - for [BOSH](https://xmpp.org/extensions/xep-0124.html) connections,
* `mod_websockets` - for [WebSocket](https://tools.ietf.org/html/rfc6455) connections,
* `mongoose_graphql_cowboy_handler` - for GraphQL API,
* `mongoose_api_admin`, `mongoose_api_client`(obsolete), `mongoose_client_api`, `mongoose_domain_handler`, `mongoose_api` - for REST API.

These types are described below in more detail.
Expand Down Expand Up @@ -492,6 +493,36 @@ Maximum allowed incoming stanza size.
This subsection enables external component connections over WebSockets.
See the [service](#xmpp-components-listenservice) listener section for details.

### Handler types: GraphQL API - `mongoose_graphql_cowboy_handler`

For more information about the API, see the [Admin interface](../graphql-api/Admin-GraphQL.md) and [User interface](../graphql-api/User-GraphQL.md) documentation.
The following options are supported for this handler:

#### `listen.http.handlers.mongoose_graphql_cowboy_handler.schema_endpoint`
* **Syntax:** string, one of `"admin"`, `"domain_admin"`, `"user"`
* **Default:** not set
Premwoik marked this conversation as resolved.
Show resolved Hide resolved
* **Example:** `schema_endpoint = "admin"`

Specifies the schema endpoint:

* `admin` - Endpoint with the admin commands. A global admin has permission to execute all commands. See the recommended configuration - [Example 5](#example-5-admin-graphql-api).
* `domain_admin` - Endpoint with the admin commands. A domain admin has permission to execute only commands with the owned domain. See the recommended configuration - [Example 6](#example-6-domain-admin-graphql-api).
* `user` - Endpoint with the user commands. Used to manage the authorized user. See the recommended configuration - [Example 7](#example-7-user-graphql-api).

#### `listen.http.handlers.mongoose_graphql_cowboy_handler.username` - only for `admin`
* **Syntax:** string
* **Default:** not set
* **Example:** `username = "admin"`

When set, enables authentication for the admin API, otherwise it is disabled. Requires setting `password`.

#### `listen.http.handlers.mongoose_graphql_cowboy_handler.password` - only for `admin`
* **Syntax:** string
* **Default:** not set
* **Example:** `password = "secret"`

Required to enable authentication for the admin API.

### Handler types: REST API - Admin - `mongoose_api_admin`

The recommended configuration is shown in [Example 2](#example-2-admin-api) below.
Expand Down Expand Up @@ -679,3 +710,56 @@ REST API for domain management.
username = "admin"
password = "secret"
```

#### Example 5. Admin GraphQL API

GraphQL API for administration, the listener is bound to 127.0.0.1 for increased security. The number of acceptors and connections is specified (reduced).

```toml
[[listen.http]]
ip_address = "127.0.0.1"
port = 8088
transport.num_acceptors = 5
transport.max_connections = 10

[[listen.http.handlers.mongoose_graphql_cowboy_handler]]
host = "localhost"
path = "/api/graphql"
schema_endpoint = "admin"
username = "admin"
password = "secret"
```

#### Example 6. Domain Admin GraphQL API

GraphQL API for the domain admin.

```toml
[[listen.http]]
ip_address = "0.0.0.0"
port = 5041
transport.num_acceptors = 10
transport.max_connections = 1024

[[listen.http.handlers.mongoose_graphql_cowboy_handler]]
host = "_"
path = "/api/graphql"
schema_endpoint = "domain_admin"
```

#### Example 7. User GraphQL API

GraphQL API for the user.

```toml
[[listen.http]]
ip_address = "0.0.0.0"
port = 5061
transport.num_acceptors = 10
transport.max_connections = 1024

[[listen.http.handlers.mongoose_graphql_cowboy_handler]]
host = "_"
path = "/api/graphql"
schema_endpoint = "user"
```
65 changes: 65 additions & 0 deletions doc/graphql-api/Admin-GraphQL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# MongooseIM's GraphQL API for the administration
Premwoik marked this conversation as resolved.
Show resolved Hide resolved

The new GraphQL admin API contains all the commands available through the REST API, and the vast majority of the CTL commands. Only commands that wouldn't have worked well with GraphQL style have been omitted.
Premwoik marked this conversation as resolved.
Show resolved Hide resolved

We can distinguish two levels of the administration. A global admin (has access to all commands), and the admin per domain (has access only to the own domain). Each of them is handled by a different endpoint. Please see the configuration [Listen](../../configuration/listen/#handler-types-graphql-api-mongoose_graphql_cowboy_handler) section for more details.

There is only one schema for both admin types. Admin per domain simply has no permissions to execute global commands or commands with not owned domain. The API documentation clearly says which commands are global.

## Domain per admin configuration
Premwoik marked this conversation as resolved.
Show resolved Hide resolved

Out of the box, domains are created a with disabled admin account. Admin per domain can be enabled only by the global admin with the command
Premwoik marked this conversation as resolved.
Show resolved Hide resolved
<a href="../admin-graphql-doc.html#definition-DomainAdminMutation" target="_blank" rel="noopener noreferrer">mutation.domains.setDomainPassword</a>. Afterward, the domain admin can change the password with the same command.

The admin per domain can be disabled by the global admin with the command <a href="../admin-graphql-doc.html#definition-DomainAdminMutation" target="_blank" rel="noopener noreferrer">mutation.domains.removeDomainPassword</a>.

## Authentication

MongooseIM uses *Basic Authentication* as an authentication method for the GraphQL API.

*Basic authentication* is a simple authentication scheme built into the HTTP protocol.
Each HTTP request to the GraphQL API has to contain the Authorization header
with the word `Basic` followed by a space and a base64-encoded string.

### Global admin endpoint

The authentication for global admin is optional because this endpoint shouldn't be exposed outside. The credentials set in the handler section in the config enables the authentication. Please see the [GraphQL handler](../configuration/listen.md#handler-types-graphql-api-mongoose_graphql_cowboy_handler) section for more details.

The base64-encoded string should have form
Premwoik marked this conversation as resolved.
Show resolved Hide resolved
`LOGIN:PASSWORD`, where:

- `LOGIN` is the login set in the config,
- `PASSWORD` is the password set in the config.

### Domain admin endpoint

The authorization as a domain admin the base64-encoded string should have form
Premwoik marked this conversation as resolved.
Show resolved Hide resolved
`admin@DOMAIN:PASSWORD`, where:

- `DOMAIN` is the domain to authorize,
- `PASSWORD` is the password for the given domain.

## GraphiQL

GraphiQL is the GraphQL integrated development environment (IDE). It allows to experiment with API and run queries with ease. The GraphiQL page is automatically served with each GraphQL endpoint. For example:

`http://localhost:5551/api/graphql`

Open the above address in your browser and try to use it.

### Authorization

Executing some of the queries requires authorization. Just add the following JSON into the header tab. Remember to update the credentials.

```json
{
"Authorization": "Basic YWxpY2VAbG9jYWxob3N0OnNlY3JldA=="
}
```

## Static documentation

<a style="float: right; padding: 5px" href="../admin-graphql-doc.html" target="_blank" rel="noopener noreferrer">Open GraphQL documentation as a full page</a>

<iframe src="../admin-graphql-doc.html"
height="800" width="800" style="border: 1px solid black;"></iframe>
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
# Config file needed to generate static documentation from GraphQL using SpectaQL

spectaql:
themeDir: default

introspection:
url: http://localhost:5551/api/graphql
schemaFile:
- priv/graphql/schemas/admin/**/*.gql
- priv/graphql/schemas/global/**/*.gql

info:
title: MongooseIM GraphQL API Reference
description: A static documentation of the MongooseIM GraphQL API
description: A static documentation of the MongooseIM GraphQL Admin API
contact:
name: API Support
url: https://github.com/esl/MongooseIM
email: mongoose-im@erlang-solutions.com
license:
name: GPL-2.0
url: https://github.com/esl/MongooseIM/blob/master/COPYING
url: https://github.com/esl/MongooseIM/blob/master/COPYIN
Premwoik marked this conversation as resolved.
Show resolved Hide resolved
Premwoik marked this conversation as resolved.
Show resolved Hide resolved

servers:
- url: http://localhost:5551/api/graphql
Expand Down
47 changes: 47 additions & 0 deletions doc/graphql-api/User-GraphQL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# MongooseIM's REST API for the user
Premwoik marked this conversation as resolved.
Show resolved Hide resolved

The new GraphQL user API contains all commands from the client REST API and provides plenty of news. Multiple commands previously available only for the admin have their counterparts for the user.
Premwoik marked this conversation as resolved.
Show resolved Hide resolved

## Authentication

MongooseIM uses *Basic Authentication* as an authentication method for the GraphQL API.
Premwoik marked this conversation as resolved.
Show resolved Hide resolved

*Basic authentication* is a simple authentication scheme built into the HTTP protocol.
Each HTTP request to the client REST API has to contain the Authorization header
with the word `Basic` followed by a space and a base64-encoded string
`username@host:password`, where:

- `username@host` is the user's *bare JID*,
- `password` is the password used to register the user's account.

For example, to authorize as `alice@localhost` with the password `secret`, the
client would send a header:

```
Authorization: Basic YWxpY2VAbG9jYWxob3N0OnNlY3JldA==
```

## GraphiQL

GraphiQL is the GraphQL integrated development environment (IDE). It allows to experiment with API and run queries with ease. The GraphiQL page is automatically served with each GraphQL endpoint. For example:

`http://localhost:5561/api/graphql`

Open the above address in your browser and try to use it.

### Authorization

Executing some of the queries requires authorization. Just add the following JSON into the header tab. Remember to update the credentials.

```json
{
"Authorization": "Basic YWxpY2VAbG9jYWxob3N0OnNlY3JldA=="
}
```

## Static documentation

<a style="float: right; padding: 5px" href="../user-graphql-doc.html" target="_blank" rel="noopener noreferrer">Open GraphQL documentation as a full page</a>

<iframe src="../user-graphql-doc.html"
height="800" width="800" style="border: 1px solid black;"></iframe>
25 changes: 25 additions & 0 deletions doc/graphql-api/User-GraphQL_spectaql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Config file needed to generate static documentation from GraphQL using SpectaQL

spectaql:
themeDir: default

introspection:
schemaFile:
- priv/graphql/schemas/user/**/*.gql
- priv/graphql/schemas/global/**/*.gql

info:
title: MongooseIM GraphQL API Reference
description: A static documentation of the MongooseIM GraphQL User API
contact:
name: API Support
url: https://github.com/esl/MongooseIM
email: mongoose-im@erlang-solutions.com
license:
name: GPL-2.0
url: https://github.com/esl/MongooseIM/blob/master/COPYIN
Premwoik marked this conversation as resolved.
Show resolved Hide resolved

servers:
- url: http://localhost:5561/api/graphql
description: Dev mim1
production: false
3 changes: 3 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ nav:
- 'Metrics backend': 'rest-api/Metrics-backend.md'
- 'Administration backend': 'rest-api/Administration-backend.md'
- 'Dynamic domains': 'rest-api/Dynamic-domains.md'
- 'GraphQL API':
- 'User': 'graphql-api/User-GraphQL.md'
- 'Admin': 'graphql-api/Admin-GraphQL.md'
- 'Operation and Maintenance':
- 'GDPR considerations': 'operation-and-maintenance/gdpr-considerations.md'
- 'Cluster management considerations': 'operation-and-maintenance/Cluster-management-considerations.md'
Expand Down
4 changes: 2 additions & 2 deletions priv/graphql/schemas/admin/account.gql
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ type UserPayload{
"Check password correctness payload"
type CheckPasswordPayload{
"Status of the password correctness"
correct: Bool!
correct: Boolean!
"Result message"
message: String!
}

"Check user existence payload"
type CheckUserPayload{
"Status of the user existence"
exist: Bool!
exist: Boolean!
"Result message"
message: String!
}
12 changes: 6 additions & 6 deletions priv/graphql/schemas/admin/domain.gql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type DomainAdminQuery @protected{
"Get all enabled domains by hostType"
"Get all enabled domains by hostType. Only for global admin"
domainsByHostType(hostType: String!): [String!]
@protected(type: GLOBAL)
"Get information about the domain"
Expand All @@ -8,22 +8,22 @@ type DomainAdminQuery @protected{
}

type DomainAdminMutation @protected{
"Add new domain"
"Add new domain. Only for global admin"
addDomain(domain: String!, hostType: String!): Domain
@protected(type: GLOBAL)
"Remove domain"
"Remove domain. Only for global admin"
removeDomain(domain: String!, hostType: String!): RemoveDomainPayload
@protected(type: GLOBAL)
"Enable domain"
"Enable domain. Only for global admin"
enableDomain(domain: String!): Domain
@protected(type: GLOBAL)
"Disable domain"
"Disable domain. Only for global admin"
disableDomain(domain: String!): Domain
@protected(type: GLOBAL)
"Create or update domain admin password"
setDomainPassword(domain: String!, password: String!): String
@protected(type: DOMAIN, args: ["domain"])
"Delete domain admin password"
"Delete domain admin password. Only for global admin"
deleteDomainPassword(domain: String!): String
@protected(type: GLOBAL)
}
Expand Down
2 changes: 1 addition & 1 deletion priv/graphql/schemas/admin/inbox.gql
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type InboxAdminMutation @protected{
flushGlobalBin(
"Required to identify the DB backend"
hostType: String!,
"Remove older than given days or all if null"
"Remove older than given days or all if null. Only for global admin"
days: PosInt
): Int @protected(type: GLOBAL)
}
2 changes: 1 addition & 1 deletion priv/graphql/schemas/admin/stanza.gql
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type StanzaAdminMutation @protected{
"Send a headline message to a local or remote bare or full JID"
sendMessageHeadLine(from: JID!, to: JID!, subject: String, body: String): SendStanzaPayload
@protected(type: DOMAIN, args: ["from"])
"Send an arbitrary stanza"
"Send an arbitrary stanza. Only for global admin"
sendStanza(stanza: Stanza): SendStanzaPayload
@protected(type: GLOBAL)
}
Loading