/**
*
* ## Ajax operations
*
* Just syntax sugar over the standard **fetch** API.
*
* @namespace
*
*/
kiss.ajax = {
timeout: 60000,
// Default headers
headers: {
"Accept": "application/json",
"Content-Type": "application/json; charset=UTF-8"
},
// If the host is set, it will be prepended to each request URL
host: "",
/**
* Set the host for the requests.
* You can use this method to set the base URL for the requests.
*
* @param {string} host
*
* @example
* kiss.ajax.setHost("https://api.example.com:3000")
*/
setHost(host = "") {
kiss.ajax.host = host
},
/**
* Set the request headers
*
* @param {object} headers
*
* @example
* kiss.ajax.setHeaders({
* "Accept": "application/json",
* "Content-Type": "application/json; charset=UTF-8"
* })
*/
setHeaders(headers) {
kiss.ajax.headers = headers
},
/**
* Encapsulate the Fetch API to automate:
* - Content-Type header
* - Authorization header (Bearer) using the Json Web Token provided by kiss.session
* - Body parsing
* - Timeout management (default to 60000 ms)
* - Automatically sets the "boundary" parameter for multipart/form-data content
* - Process HTTP error codes:
* . 401 redirects to login page
* . 403 (forbidden) sends a notification
* . 498 (token expired) tries to get a new token using the refresh token, and redirects to login if failed
*
* @async
* @param {object} params - A single object containing the following:
* @param {string} [params.host] - Optional host to prepend to the URL in replacement of the default host
* @param {string} params.url - Url to request
* @param {string} params.method - get, post, put, patch, delete, options - Default to get
* @param {object} params.accept - Accept header
* @param {object} params.contentType - Content Type header - Default to application/json; charset=UTF-8
* @param {object} params.authorization - Authorization header
* @param {object} params.accessControlAllowOrigin - Access Control Allow Origin
* @param {object} params.accessControlAllowHeaders - Access Control Allow Headers
* @param {object|string} params.body - body for post, put and patch requests
* @param {number} params.timeout - in milliseconds. Throw an error in timeout exceeded.
* @param {boolean} params.showLoading - If true, show the loading spinner while loading - Default to false
* @returns Request's result, or false if it failed
*
* @example
* // Posting with simple JSON:
* kiss.ajax.request({
* url: YOUR_URL,
* method: "post",
* accept: "application/json",
* contentType: "application/json; charset=UTF-8",
* body: JSON.stringify({
* foo: "bar",
* hello: "world"
* }),
* timeout: 500
* })
* .then(data => {
* console.log(data)
* })
* .catch(err => {
* console.log(err)
* })
*
* // Posting with basic authentication and application/x-www-form-urlencoded:
* kiss.ajax.request({
* url: YOUR_URL,
* method: "post",
* contentType: "application/x-www-form-urlencoded; charset=UTF-8",
* authorization: "Basic " + btoa(YOUR_LOGIN + ":" + YOUR_PASSWORD),
* body: "foo=bar&hello=world"
* })
*/
async request(params) {
log(`kiss.ajax - request - ${params.method || "GET"}: ${params.url}`)
let options = {
method: params?.method?.toUpperCase() || "GET",
headers: kiss.ajax.headers
}
// Inject authorization header with the active token
// kissjs keeps the token in the localStorage until a logout is triggered
const token = kiss.session.getToken()
if (token) options.headers["Authorization"] = "Bearer " + token
if (params.accept) options.headers["Accept"] = params.accept
if (params.authorization) options.headers["Authorization"] = params.authorization
if (params.accessControlAllowOrigin) options.headers["Access-Control-Allow-Origin"] = params.accessControlAllowOrigin
if (params.accessControlAllowHeaders) options.headers["Access-Control-Allow-Headers"] = params.accessControlAllowHeaders
if (params.body) options.body = params.body
// Adjust content type
if (params.contentType) {
if (params.contentType == "multipart/form-data") {
// For multipart/form-data, we delete the content type to force the browser
// to infer the content type and set the "boundary" paramater automatically
delete options.headers["Content-Type"]
} else {
options.headers["Content-Type"] = params.contentType
}
} else {
// Default to application/json and UTF-8 encoding
options.headers["Content-Type"] = "application/json; charset=UTF-8"
}
// Manage timeout
const timeout = params.timeout || kiss.ajax.timeout
const abortController = new AbortController()
options.signal = abortController.signal
setTimeout(() => abortController.abort(), timeout)
let loadingId
if (params.showLoading) {
loadingId = kiss.loadingSpinner.show()
}
// Build url using default host or the one provided in the params
// Having a default host will point every request to the same server
const url = (params.host || params.host === "") ? params.host + params.url : kiss.ajax.host + params.url
return fetch(url, options)
.then(async response => {
if (params.showLoading) {
loadingId = kiss.loadingSpinner.hide(loadingId)
}
switch (response.status) {
case 401:
// Unauthorized requests are redirected to the login page
kiss.session.showLogin()
return false
case 498:
// Prevent loops for invalid tokens
if (kiss.global.ajaxRetries >= kiss.global.ajaxMaxRetries) {
kiss.global.ajaxRetries = 0
return false
}
kiss.global.ajaxRetries++
// Means the token to request the server is expired.
// Sends a request to refresh the token
const newToken = await kiss.session.getNewToken()
if (newToken) {
// Retry the original request
return await kiss.ajax.request(params)
}
break
case 403:
// Means the access to the resource is forbidden
createNotification(txtTitleCase("#not authorized"))
default:
return response.json().then(data => {
return data
}).catch(err => {
// The response is not JSON
return response
})
}
})
.catch(err => {
if (err.name == "AbortError") {
log("kiss.ajax - request - Timeout!", 4, err)
createNotification(txtTitleCase("#error slow connection"))
} else {
log("kiss.ajax - request - Error:", 4, err)
log("kiss.ajax - The original request was:", 4, params)
}
if (params.showLoading) {
loadingId = kiss.loadingSpinner.hide(loadingId)
}
return false
})
},
/**
* Adjust the request timeout
*
* @param {number} timeout - in milliseconds
*/
setTimeout(timeout) {
if (typeof timeout === "number") kiss.ajax.timeout = timeout
}
}
;
Source