Last active
March 10, 2020 01:31
-
-
Save andrewnicols/fd0054e03e9b905737dd8b049c80b139 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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/>. | |
/** | |
* XHR Processing for Moodle to call standard AJAX-enabled Moodle Web Services. | |
* | |
* @module core/request | |
* @package core | |
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk> | |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
* @since 3.9 | |
*/ | |
import Config from 'core/config'; | |
import Log from 'core/log'; | |
import Pending from 'core/pending'; | |
import Url from 'core/url'; | |
let pageIsUnloading = false; | |
/** | |
* Register the unload events. | |
* | |
* These are used to prevent unload XHR failures. | |
* | |
* The presence of a beforeunload event prevents caching of the page content, therefore it should be removed during the | |
* unload. | |
*/ | |
let registerUnloadEvents = () => { | |
const beforeUnloadHandler = () => { | |
pageIsUnloading = true; | |
window.removeEventListener('beforeunload', beforeUnloadHandler); | |
}; | |
window.addEventListener("beforeunload", beforeUnloadHandler); | |
registerUnloadEvents = () => { | |
return; | |
}; | |
}; | |
/** | |
* Get the script configuration, including the URL and type. | |
* | |
* @param {Bool} requireLogin | |
* @param {String[]} methods List of methods | |
* @param {String} cacheKey | |
* @returns {Object} | |
*/ | |
const getScriptConfig = (requireLogin, methods, cacheKey) => { | |
const script = requireLogin ? 'service.php' : 'service-nologin.php'; | |
const urlParams = {}; | |
if (methods.length <= 5) { | |
urlParams.info = methods.sort().join(); | |
} else { | |
urlParams.info = `${methods.length}-method-calls`; | |
} | |
let type = 'POST'; | |
if (requireLogin) { | |
urlParams.sesskey = Config.sesskey; | |
} else if (cacheKey) { | |
type = 'GET'; | |
} | |
const urlData = Object.entries(urlParams).sort().map(data => `${data[0]}=${data[1]}`).join('&'); | |
return { | |
url: `${Config.wwwroot}/lib/ajax/${script}?${urlData}`, | |
type, | |
}; | |
}; | |
/** | |
* Perform a single XHR. | |
* | |
* @param {String} methodName | |
* @param {Object} methodArgs | |
* @param {Bool} requireLogin | |
* @param {Object} Additional parameters | |
* @param {Object}.{String} cacheKey | |
* @param {Object}.{Number} timeout | |
* @param {Object}.{Bool} updateSession | |
* @returns {Promise} | |
*/ | |
export const fetch = ( | |
methodName, | |
methodArgs = {}, | |
requireLogin = true, | |
{ | |
cacheKey = null, | |
timeout = 0, | |
updateSession = true, | |
} = {} | |
) => { | |
// Register unload events. | |
// This prevents promises from being rejected, and notifications therefore being shown, because the page is navigated away. | |
registerUnloadEvents(); | |
const pendingPromise = new Pending(`core/request:fetch:${methodName}`); | |
return new Promise((resolve, reject) => { | |
const { | |
url, | |
type, | |
} = getScriptConfig( | |
requireLogin, | |
[methodName], | |
cacheKey = Math.max(parseInt(cacheKey), 0) || null | |
); | |
const xhr = new XMLHttpRequest(); | |
xhr.open(type, url); | |
xhr.timeout = timeout; | |
// Configure the load listener which is triggered when the XHR completes. | |
xhr.addEventListener('load', () => { | |
// The response is placed into the `xhr.responseText` variable. | |
const responses = JSON.parse(xhr.responseText); | |
if (!responses || !responses.length) { | |
return reject(new Error('No response data provided')); | |
} | |
const response = responses[0]; | |
if (response.error) { | |
return reject(response.exception); | |
} else { | |
return resolve(response.data); | |
} | |
}); | |
// Configure the error listener, which is triggered when the XHR fails. | |
xhr.addEventListener('error', () => { | |
if (pageIsUnloading) { | |
// No need to trigger an error because we are already navigating. | |
Log.error("Page unloaded."); | |
Log.error(xhr.statusText); | |
} else { | |
reject(xhr.statusText); | |
} | |
}); | |
// Perform the request. | |
xhr.send(JSON.stringify([{ | |
args: methodArgs, | |
index: 0, | |
methodname: methodName, | |
nosessionupdate: !updateSession, | |
}])); | |
}) | |
.then(result => { | |
pendingPromise.resolve(); | |
return result; | |
}) | |
.catch(failure => { | |
pendingPromise.resolve(); | |
if (failure && failure.errorcode) { | |
if (failure.errorcode === "servicerequireslogin" && updateSession) { | |
// Redirect to the login page. | |
window.location = Url.relativeUrl("/login/index.php"); | |
} | |
} | |
return Promise.reject(failure); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment