js/models/Request/Request.js
import axios from 'axios/dist/axios.min.js';
import ErrorManager from '~/helpers/ErrorManager';
import Data from '~/models/Data';
// Get axios setup to intercept
axios.interceptors.response.use((response) => response, (error) => {
ErrorManager.unhandled(error);
return Promise.reject(error);
});
/**
* @typedef {Object} HTTPMethod
* @property {string} GET
* @property {string} PET
* @property {string} POST
* @property {string} DELETE
*/
export const HTTPMethod = {
GET: 'GET',
POST: 'POST',
DELETE: 'DELETE',
PUT: 'PUT'
}
/**
* Performs a request for data.
*/
export default class Request {
/**
* Formats the request object and returns an object of Request<T>'s T.
* @param {Object} data - Data (JSON?) from the request.
* @return {Object} formatted object.
*/
format(data) { return data; }
/**
* Performs the request
* @param {Object} [o={}] - Options
* @param {boolean} [o.format=true] - Set to false to not format
* @return {Promise} resolves to format of object. See return type of
* {@link Request#format}
*/
async run({ formatted = true } = {}) {
let response;
try {
let pendingRequest = axios.request({
method: this._method,
url: this._path,
data: this._data,
params: this._params,
headers: this._headers,
responseType: this._responseType
}, {
cancelToken: this._cancelToken.token
});
this._pendingRequest = pendingRequest;
response = await pendingRequest;
} catch(error) {
if (axios.isCancel(error)) {
this._canceled = true;
return null;
} else {
throw error;
}
} finally {
this._done = true;
}
if (formatted) {
return this.format(response.data);
} else {
return response.data;
}
}
/**
* Cancels the request. Does not work with pagination
*/
cancel() {
this._pendingRequest?.cancel(this._cancelToken);
}
/**
* Creates request given path. Provide options **as object**
* @param {Object} requestData - Object describing request. Bare
* mininmum info is providing path.
* @param {?string} requestData.host - If cross origin, provide. Specifying will mean NO CSRF token passed
* @param {string} requestData.path - Path of request
* @param {string} requestData.auth - Authorization header
* @param {any} requestData.data - Any data to send as part of request.
* @param {Object} requestData.formData Automatically converted to form data.
* @param {?string} requestData.contentType - Content type header
* @param {?Object} requestData.headers - Additional request headers.
* @param {HTTPMethod} [requestData.method=get] - Method request type
*/
constructor({
host = null,
path,
auth,
data,
params,
formData,
contentType,
responseType,
headers = {},
method = HTTPMethod.GET
}) {
/**
* @protected
* @type {string}
*/
this._path = `${host || ""}${path}`;
this._method = method;
this._params = params;
this._responseType = responseType;
this._pendingRequest = null;
this._canceled = false;
this._done = false;
this._cancelToken = axios.CancelToken.source();
if (formData) {
let formDataInstance = new FormData();
for (const [key, value] of Object.entries(formData)) {
if (value !== null) {
formDataInstance.append(key, value);
}
}
this._data = formDataInstance;
} else {
this._data = data;
}
this._headers = headers;
if (contentType) this._headers['Content-Type'] = contentType;
if (auth) this._headers['Authorization'] = auth;
if (host === null) this._headers['X-CSRF-Token'] = Data.shared.envValueForKey('CSRF');
}
}