ra-core
Version:
Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React
110 lines (97 loc) • 3.72 kB
text/typescript
import HttpError from './HttpError';
import { stringify } from 'query-string';
export interface Options extends RequestInit {
user?: {
authenticated?: boolean;
token?: string;
};
}
export const createHeadersFromOptions = (options: Options): Headers => {
const requestHeaders = (options.headers ||
new Headers({
Accept: 'application/json',
})) as Headers;
const hasBody = options && options.body;
const isContentTypeSet = requestHeaders.has('Content-Type');
const isGetMethod = !options?.method || options?.method === 'GET';
const isFormData = options?.body instanceof FormData;
const shouldSetContentType =
hasBody && !isContentTypeSet && !isGetMethod && !isFormData;
if (shouldSetContentType) {
requestHeaders.set('Content-Type', 'application/json');
}
if (options.user && options.user.authenticated && options.user.token) {
requestHeaders.set('Authorization', options.user.token);
}
return requestHeaders;
};
/**
* Utility function to make HTTP calls. It's similar to the HTML5 `fetch()`, except it handles JSON decoding and HTTP error codes automatically.
*
* @param url the URL to call
* @param options the options to pass to the HTTP call
* @param options.user the user object, used for the Authorization header
* @param options.user.token the token to pass as the Authorization header
* @param options.user.authenticated whether the user is authenticated or not (the Authorization header will be set only if this is true)
* @param options.headers the headers to pass to the HTTP call
*
* @returns {Promise} the Promise for a response object containing the following properties:
* - status: the HTTP status code
* - headers: the HTTP headers
* - body: the response body
* - json: the response body parsed as JSON
*/
export const fetchJson = (url, options: Options = {}) => {
const requestHeaders = createHeadersFromOptions(options);
return fetch(url, { ...options, headers: requestHeaders })
.then(response =>
response.text().then(text => ({
status: response.status,
statusText: response.statusText,
headers: response.headers,
body: text,
}))
)
.then(({ status, statusText, headers, body }) => {
let json;
try {
json = JSON.parse(body);
} catch (e) {
// not json, no big deal
}
if (status < 200 || status >= 300) {
return Promise.reject(
new HttpError(
(json && json.message) || statusText,
status,
json
)
);
}
return Promise.resolve({ status, headers, body, json });
});
};
export const queryParameters = stringify;
const isValidObject = value => {
if (!value) {
return false;
}
const isArray = Array.isArray(value);
const isBuffer = typeof Buffer !== 'undefined' && Buffer.isBuffer(value);
const isObject =
Object.prototype.toString.call(value) === '[object Object]';
const hasKeys = !!Object.keys(value).length;
return !isArray && !isBuffer && isObject && hasKeys;
};
export const flattenObject = (value: any, path: string[] = []) => {
if (isValidObject(value)) {
return Object.assign(
{},
...Object.keys(value).map(key =>
flattenObject(value[key], path.concat([key]))
)
);
} else {
return path.length ? { [path.join('.')]: value } : value;
}
};