346 lines
9.4 KiB
JavaScript
346 lines
9.4 KiB
JavaScript
// 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/>.
|
|
|
|
/**
|
|
* The core/fetch module allows you to make web service requests to the Moodle API.
|
|
*
|
|
* @module core/fetch
|
|
* @copyright Andrew Lyons <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @example <caption>Perform a single GET request</caption>
|
|
* import Fetch from 'core/fetch';
|
|
*
|
|
* const result = Fetch.performGet('mod_example', 'animals', { params: { type: 'mammal' } });
|
|
*
|
|
* result.then((response) => {
|
|
* // Do something with the Response object.
|
|
* })
|
|
* .catch((error) => {
|
|
* // Handle the error
|
|
* });
|
|
*/
|
|
|
|
import Cfg from 'core/config';
|
|
import PendingPromise from './pending';
|
|
|
|
/**
|
|
* A wrapper around the Request, including a Promise that is resolved when the request is complete.
|
|
*
|
|
* @class RequestWrapper
|
|
* @private
|
|
*/
|
|
class RequestWrapper {
|
|
/** @var {Request} */
|
|
#request = null;
|
|
|
|
/** @var {Promise} */
|
|
#promise = null;
|
|
|
|
/** @var {Function} */
|
|
#resolve = null;
|
|
|
|
/** @var {Function} */
|
|
#reject = null;
|
|
|
|
/**
|
|
* Create a new RequestWrapper.
|
|
*
|
|
* @param {Request} request The request object that is wrapped
|
|
*/
|
|
constructor(request) {
|
|
this.#request = request;
|
|
this.#promise = new Promise((resolve, reject) => {
|
|
this.#resolve = resolve;
|
|
this.#reject = reject;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the wrapped Request.
|
|
*
|
|
* @returns {Request}
|
|
* @private
|
|
*/
|
|
get request() {
|
|
return this.#request;
|
|
}
|
|
|
|
/**
|
|
* Get the Promise link to this request.
|
|
*
|
|
* @return {Promise}
|
|
* @private
|
|
*/
|
|
get promise() {
|
|
return this.#promise;
|
|
}
|
|
|
|
/**
|
|
* Handle the response from the request.
|
|
*
|
|
* @param {Response} response
|
|
* @private
|
|
*/
|
|
handleResponse(response) {
|
|
if (response.ok) {
|
|
this.#resolve(response);
|
|
} else {
|
|
this.#reject(response.statusText);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A class to handle requests to the Moodle REST API.
|
|
*
|
|
* @class Fetch
|
|
*/
|
|
export default class Fetch {
|
|
/**
|
|
* Make a single request to the Moodle API.
|
|
*
|
|
* @param {string} component The frankenstyle component name
|
|
* @param {string} action The component action to perform
|
|
* @param {object} params
|
|
* @param {object} [params.params = {}] The parameters to pass to the API
|
|
* @param {string|Object|FormData} [params.body = null] The HTTP method to use
|
|
* @param {string} [params.method = "GET"] The HTTP method to use
|
|
* @returns {Promise<Response>} A promise that resolves to the Response object for the request
|
|
*/
|
|
static async request(
|
|
component,
|
|
action,
|
|
{
|
|
params = {},
|
|
body = null,
|
|
method = 'GET',
|
|
} = {},
|
|
) {
|
|
const pending = new PendingPromise(`Requesting ${component}/${action} with ${method}`);
|
|
const requestWrapper = Fetch.#getRequest(
|
|
Fetch.#normaliseComponent(component),
|
|
action,
|
|
{ params, method, body },
|
|
);
|
|
const result = await fetch(requestWrapper.request);
|
|
|
|
pending.resolve();
|
|
|
|
requestWrapper.handleResponse(result);
|
|
|
|
return requestWrapper.promise;
|
|
}
|
|
|
|
/**
|
|
* Make a request to the Moodle API.
|
|
*
|
|
* @param {string} component The frankenstyle component name
|
|
* @param {string} action The component action to perform
|
|
* @param {object} params
|
|
* @param {object} [params.params = {}] The parameters to pass to the API
|
|
* @returns {Promise<Response>} A promise that resolves to the Response object for the request
|
|
*/
|
|
static performGet(
|
|
component,
|
|
action,
|
|
{
|
|
params = {},
|
|
} = {},
|
|
) {
|
|
return this.request(
|
|
component,
|
|
action,
|
|
{ params, method: 'GET' },
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Make a request to the Moodle API.
|
|
*
|
|
* @param {string} component The frankenstyle component name
|
|
* @param {string} action The component action to perform
|
|
* @param {object} params
|
|
* @param {object} [params.params = {}] The parameters to pass to the API
|
|
* @returns {Promise<Response>} A promise that resolves to the Response object for the request
|
|
*/
|
|
static performHead(
|
|
component,
|
|
action,
|
|
{
|
|
params = {},
|
|
} = {},
|
|
) {
|
|
return this.request(
|
|
component,
|
|
action,
|
|
{ params, method: 'HEAD' },
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Make a request to the Moodle API.
|
|
*
|
|
* @param {string} component The frankenstyle component name
|
|
* @param {string} action The component action to perform
|
|
* @param {object} params
|
|
* @param {string|Object|FormData} params.body The HTTP method to use
|
|
* @returns {Promise<Response>} A promise that resolves to the Response object for the request
|
|
*/
|
|
static performPost(
|
|
component,
|
|
action,
|
|
{
|
|
body,
|
|
} = {},
|
|
) {
|
|
return this.request(
|
|
component,
|
|
action,
|
|
{ body, method: 'POST' },
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Make a request to the Moodle API.
|
|
*
|
|
* @param {string} component The frankenstyle component name
|
|
* @param {string} action The component action to perform
|
|
* @param {object} params
|
|
* @param {string|Object|FormData} params.body The HTTP method to use
|
|
* @returns {Promise<Response>} A promise that resolves to the Response object for the request
|
|
*/
|
|
static performPut(
|
|
component,
|
|
action,
|
|
{
|
|
body,
|
|
} = {},
|
|
) {
|
|
return this.request(
|
|
component,
|
|
action,
|
|
{ body, method: 'PUT' },
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Make a PATCH request to the Moodle API.
|
|
*
|
|
* @param {string} component The frankenstyle component name
|
|
* @param {string} action The component action to perform
|
|
* @param {object} params
|
|
* @param {string|Object|FormData} params.body The HTTP method to use
|
|
* @returns {Promise<Response>} A promise that resolves to the Response object for the request
|
|
*/
|
|
static performPatch(
|
|
component,
|
|
action,
|
|
{
|
|
body,
|
|
} = {},
|
|
) {
|
|
return this.request(
|
|
component,
|
|
action,
|
|
{ body, method: 'PATCH' },
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Make a request to the Moodle API.
|
|
*
|
|
* @param {string} component The frankenstyle component name
|
|
* @param {string} action The component action to perform
|
|
* @param {object} params
|
|
* @param {object} [params.params = {}] The parameters to pass to the API
|
|
* @param {string|Object|FormData} [params.body = null] The HTTP method to use
|
|
* @returns {Promise<Response>} A promise that resolves to the Response object for the request
|
|
*/
|
|
static performDelete(
|
|
component,
|
|
action,
|
|
{
|
|
params = {},
|
|
body = null,
|
|
} = {},
|
|
) {
|
|
return this.request(
|
|
component,
|
|
action,
|
|
{
|
|
body,
|
|
params,
|
|
method: 'DELETE',
|
|
},
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Normalise the component name to remove the core_ prefix.
|
|
*
|
|
* @param {string} component
|
|
* @returns {string}
|
|
*/
|
|
static #normaliseComponent(component) {
|
|
return component.replace(/^core_/, '');
|
|
}
|
|
|
|
/**
|
|
* Get the Request for a given API request.
|
|
*
|
|
* @param {string} component The frankenstyle component name
|
|
* @param {string} endpoint The endpoint within the componet to call
|
|
* @param {object} params
|
|
* @param {object} [params.params = {}] The parameters to pass to the API
|
|
* @param {string|Object|FormData} [params.body = null] The HTTP method to use
|
|
* @param {string} [params.method = "GET"] The HTTP method to use
|
|
* @returns {RequestWrapper}
|
|
*/
|
|
static #getRequest(
|
|
component,
|
|
endpoint,
|
|
{
|
|
params = {},
|
|
body = null,
|
|
method = 'GET',
|
|
}
|
|
) {
|
|
const url = new URL(`${Cfg.apibase}/rest/v2/${component}/${endpoint}`);
|
|
const options = {
|
|
method,
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
};
|
|
|
|
Object.entries(params).forEach(([key, value]) => {
|
|
url.searchParams.append(key, value);
|
|
});
|
|
|
|
if (body) {
|
|
if (body instanceof FormData) {
|
|
options.body = body;
|
|
} else if (body instanceof Object) {
|
|
options.body = JSON.stringify(body);
|
|
} else {
|
|
options.body = body;
|
|
}
|
|
}
|
|
|
|
return new RequestWrapper(new Request(url, options));
|
|
}
|
|
}
|