914686bc5e
This commit brings in support for multiple versions of the Matrix
specification.
A Matrix server is compromised of a number of individually versioned API
endpoints, for example:
/_matrix/client/v3/createRoom
/_matrix/client/v3/rooms/:roomid/joined_members
/_matrix/media/v1/create
The combination of a large number of these individually versioned
endpoints forms a Matrix Specification version.
For example:
* the /_matrix/media/v1/create endpoint was created for version 1.7 of the
specification, and does not exist in earlier versions.
* in the future a new behaviour or parameter may be created for the
`createRoom` endpoint and a new endpoint created at:
/_matrix/client/v4/createRoom
A single server can support multiple versions of the Matrix
specification. The server declares the versions of the specification
that it supports using a non-versioned endpoint at
`/_matrix/client/versions`.
As a Matrix client, Moodle should:
* query the server version endpoint
* determine the combination of mutually supported Matrix specification
versions
* create a client instance of the highest-supported version of the
specification.
For example, if Moodle (Matrix client) and a remote server have the
following support:
```
Moodle: 1.1 1.2 1.3 1.4 1.5 1.6 1.7
Server: r0 1.1 1.2 1.3 1.4 1.5 1.6
```
The versions in common are 1.1 through 1.6, and version 1.6 would be
chosen.
To avoid duplication and allow for support of future features more
easily, the Moodle client is written as:
* a set of classes named `v1p1` through `v1p7` (currently) which extend
the `matrix_client` abstract class; and
* a set if PHP traits which provide the implementation for individual
versioned endpoints.
Each client version then imports any relevant traits which are present
in that version of the Matrix Specification. For example versions 1.1 to
1.6 do _not_ have the `/_matrix/media/v1/create` endpoint so they do not
import this trait. This trait was introduced in version 1.7, so the
trait is included from that version onwards.
In the future, if an endpoint is created which conflicts with an
existing endpoint, then it would be easy to create a new client version
which uses the existing common traits, and adds the new trait.
Each endpoint is written using a `command` class which extends the
Guzzle implementation of the PSR-7 Request interface. This command
class adds support for easy creation of:
* path parameters within the URI
* query parameters
* body parameters
This is done to avoid complex patterns of Request creation which are
repeated for every client endpoint.
190 lines
5.8 KiB
PHP
190 lines
5.8 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
namespace communication_matrix\local;
|
|
|
|
use communication_matrix\matrix_client;
|
|
use GuzzleHttp\Psr7\Request;
|
|
use OutOfRangeException;
|
|
|
|
/**
|
|
* A command to be sent to the Matrix server.
|
|
*
|
|
* This class is a wrapper around the PSR-7 Request Interface implementation provided by Guzzle.
|
|
*
|
|
* It takes a set of common parameters and configurations and turns them into a Request that can be called against a live server.
|
|
*
|
|
* @package communication_matrix
|
|
* @copyright Andrew Nicols <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class command extends Request {
|
|
/** @var array $command The raw command data */
|
|
/** @var array|null $params The parameters passed into the command */
|
|
/** @var bool $sendasjson Whether to send params as JSON */
|
|
/** @var bool $requireauthorization Whether authorization is required for this request */
|
|
/** @var bool $ignorehttperrors Whether to ignore HTTP Errors */
|
|
/** @var array $query Any query parameters to set on the URL */
|
|
|
|
/** @var array|null Any parameters not used in the URI which are to be passed to the server via body or query params */
|
|
protected array $remainingparams = [];
|
|
|
|
/**
|
|
* Create a new Command.
|
|
*
|
|
* @param matrix_client $client The URL for this method
|
|
* @param string $method (GET|POST|PUT|DELETE)
|
|
* @param string $endpoint The URL
|
|
* @param array $params Any parameters to pass
|
|
* @param array $query Any query parameters to set on the URL
|
|
* @param bool $ignorehttperrors Whether to ignore HTTP Errors
|
|
* @param bool $requireauthorization Whether authorization is required for this request
|
|
* @param bool $sendasjson Whether to send params as JSON
|
|
*/
|
|
public function __construct(
|
|
protected matrix_client $client,
|
|
string $method,
|
|
string $endpoint,
|
|
protected array $params = [],
|
|
protected array $query = [],
|
|
protected bool $ignorehttperrors = false,
|
|
protected bool $requireauthorization = true,
|
|
protected bool $sendasjson = true,
|
|
) {
|
|
foreach ($params as $name => $value) {
|
|
if ($name[0] === ':') {
|
|
if (preg_match("/{$name}\\b/", $endpoint) !== 1) {
|
|
throw new OutOfRangeException("Parameter not found in URL '{$name}'");
|
|
}
|
|
|
|
$endpoint = preg_replace("/{$name}\\b/", urlencode($value), $endpoint);
|
|
unset($params[$name]);
|
|
}
|
|
}
|
|
|
|
// Store the modified params.
|
|
$this->remainingparams = $params;
|
|
|
|
if (str_contains($endpoint, '/:')) {
|
|
throw new OutOfRangeException("URL contains untranslated parameters '{$endpoint}'");
|
|
}
|
|
|
|
// Process the required headers.
|
|
$headers = [
|
|
'Content-Type' => 'application/json',
|
|
];
|
|
|
|
if ($this->require_authorization()) {
|
|
$headers['Authorization'] = 'Bearer ' . $this->client->get_token();
|
|
}
|
|
|
|
// Construct the final request.
|
|
parent::__construct(
|
|
$method,
|
|
$this->get_url($endpoint),
|
|
$headers,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the URL of the endpoint on the server.
|
|
*
|
|
* @param string $endpoint
|
|
* @return string
|
|
*/
|
|
protected function get_url(string $endpoint): string {
|
|
return sprintf(
|
|
"%s/%s",
|
|
$this->client->get_server_url(),
|
|
$endpoint,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get all parameters, including those set in the URL.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_all_params(): array {
|
|
return $this->params;
|
|
}
|
|
|
|
/**
|
|
* Get the parameters provided to the command which are not used in the URL.
|
|
*
|
|
* These are typically passed to the server as query or body parameters instead.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_remaining_params(): array {
|
|
return $this->remainingparams;
|
|
}
|
|
|
|
/**
|
|
* Get the Guzzle options to pass into the request.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_options(): array {
|
|
$options = [];
|
|
|
|
if (count($this->query)) {
|
|
$options['query'] = $this->query;
|
|
}
|
|
|
|
if ($this->should_send_params_as_json()) {
|
|
$options['json'] = $this->get_remaining_params();
|
|
}
|
|
|
|
if ($this->should_ignore_http_errors()) {
|
|
$options['http_errors'] = false;
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* Whether authorization is required.
|
|
*
|
|
* Based on the 'authorization' attribute set in a raw command.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function require_authorization(): bool {
|
|
return $this->requireauthorization;
|
|
}
|
|
|
|
/**
|
|
* Whether to ignore http errors on the response.
|
|
*
|
|
* Based on the 'ignore_http_errors' attribute set in a raw command.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function should_ignore_http_errors(): bool {
|
|
return $this->ignorehttperrors;
|
|
}
|
|
|
|
/**
|
|
* Whether to send remaining parameters as JSON.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function should_send_params_as_json(): bool {
|
|
return $this->sendasjson;
|
|
}
|
|
}
|