/
request.js
159 lines (146 loc) · 5.24 KB
/
request.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import axios from 'axios';
import isArray from 'lodash/isArray';
import reduce from 'lodash/reduce';
import replace from 'lodash/replace';
/**
* Convert api params to query string
*
* @param {Object} params Params to convert
* @return {String}
*/
export const toQueryString = (params) =>
// Use lodash's reduce function to iterate over each key-value pair in the params object
reduce(
params,
(result, value, key) => {
// If either the key or value is undefined, return the result as is
if (value === undefined || key === undefined) {
return result;
}
// If the value is an array and it has elements
if (isArray(value) && value.length > 0) {
// Use lodash's reduce function to iterate over each item in the value array
const resultString = reduce(
value,
(result, valueItem) => {
// Replace all occurrences of '#' with '%23' in the valueItem
valueItem = replace(valueItem, new RegExp('#', 'g'), '%23');
// Replace all occurrences of apostrophes with '%27' in the valueItem
valueItem = replace(valueItem, new RegExp('[\'’]', 'g'), '%27');
// Append the key-value pair to the result string, separated by '&' if result is not empty
return `${result}${result === '' ? '' : '&'}${key}=${valueItem}`;
},
'',
);
// Append the resultString to the result, separated by '?' if result is empty, otherwise '&'
return `${result}${result === '' ? '?' : '&'}${resultString}`;
}
// If the value is not an array, replace all occurrences of '#' with '%23' and apostrophes with '%27' in the value
value = replace(value, new RegExp('#', 'g'), '%23');
value = replace(value, new RegExp('[\'’]', 'g'), '%27');
// Append the key-value pair to the result, separated by '?' if result is empty, otherwise '&'
return `${result}${result === '' ? '?' : '&'}${key}=${value}`;
},
'',
);
/**
* Return headers for network request
*
* @param {Object} options
* @return {Object} headers
*/
async function getRequestHeaders(options = {}) {
// Define default headers
const headers = {
'Content-Type': 'application/json',
// Spread operator is used to include any additional headers passed in options
...options.headers,
};
// Return the headers object
return headers;
}
/**
* Make a request and throw an error if response code is not in successful range.
*
* @param {String} url The url of the request.
* @param {Object} options
* @param {String} options.method The method for the request.
* @param {Object} options.headers The headers to send with the request.
* @param {Object} options.body The body to send with the request.
*/
async function makeRequest(url, options = {}) {
let response;
const requestHeaders = await getRequestHeaders(options);
const requestOptions = {
url,
method: options.method || 'GET',
headers: requestHeaders,
data: options.body,
cancelToken: options.cancelToken,
};
// If form data is supplied, do not stringify the body and
// remove the Content-Type header so that the browser can set it itself.
if (options.formData) {
requestOptions.headers['Content-Type'] = 'multipart/form-data';
}
if (options.formData) {
response = await fetch(url, { body: options.body, ...requestOptions });
if (response.status < 200 || response.status > 299) {
throw response;
}
const parseResponse = await response.json();
response = { data: parseResponse };
}
else {
response = await axios(requestOptions);
}
// Throw an error if the status code is not a successful status code.
if (response.status < 200 || response.status > 299) {
throw response;
}
// Otherwise, return the response.
return response;
}
/**
* Make a request with the ability to refresh tokens in the event of a 401.
*
* @param {String} url The url of the request.
* @param {Object} options
* @param {Number} retry Retry count
* @param {String} options.method The method for the request.
* @param {Object} options.headers The headers to send with the request.
* @param {Object} options.body The body to send with the request.
*/
export async function apiRequest(url, options = {}) {
try {
// Make request with current tokens.
const response = await makeRequest(url, options);
// Should just return if no-content response
if (response.status === 204) {
return true;
}
return response.data;
}
catch (error) {
const response = error.response || error || {};
const getEndpoint = (url) =>
url.substring(
url.length,
url.indexOf('?') > -1 ? url.indexOf('?') : undefined,
);
const endpoint = url.includes(getEndpoint(url))
? getEndpoint(url)
: 'external endpoint';
/* eslint-disable-next-line no-underscore-dangle */
const errorMessage = `Received ${response.status} from ${error?.request?._method} request to ${endpoint}`;
console.log(`${errorMessage}`, {
url,
status: response.status,
error: response?.data?.error,
message: response?.data?.message,
method: options.method,
headers: options.headers,
});
throw error;
}
}