2017-09-19 13:32:04 -07:00
|
|
|
import fetch from 'fetch';
|
2018-02-28 16:31:37 -08:00
|
|
|
import { apiErrorStatusMessage } from 'wherehows-web/constants/errors/errors';
|
|
|
|
import { ApiError, throwIfApiError } from 'wherehows-web/utils/api/errors/errors';
|
2018-07-19 16:18:06 -07:00
|
|
|
import { isNotFoundApiError } from 'wherehows-web/utils/api/shared';
|
2017-09-19 13:32:04 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Describes the attributes on the fetch configuration object
|
|
|
|
*/
|
2018-07-19 16:18:06 -07:00
|
|
|
interface IFetchConfig {
|
2017-09-19 13:32:04 -07:00
|
|
|
url: string;
|
2017-10-18 17:38:51 -07:00
|
|
|
headers?: { [key: string]: string } | Headers;
|
|
|
|
data?: object;
|
2017-09-19 13:32:04 -07:00
|
|
|
}
|
|
|
|
|
2018-01-18 18:52:42 -08:00
|
|
|
/**
|
2018-02-21 14:41:05 -08:00
|
|
|
* Describes the available options on an option bag to be passed into a fetch call
|
2018-01-18 18:52:42 -08:00
|
|
|
* @interface IFetchOptions
|
|
|
|
*/
|
|
|
|
interface IFetchOptions {
|
|
|
|
method?: string;
|
|
|
|
body?: any;
|
|
|
|
headers?: object | Headers;
|
|
|
|
credentials?: RequestCredentials;
|
|
|
|
}
|
|
|
|
|
2017-10-18 17:38:51 -07:00
|
|
|
/**
|
2017-11-27 00:51:20 -08:00
|
|
|
* Augments the user supplied headers with the default accept and content-type headers
|
2018-07-19 16:18:06 -07:00
|
|
|
* @param {IFetchConfig.headers} headers
|
2017-10-18 17:38:51 -07:00
|
|
|
*/
|
2018-07-19 16:18:06 -07:00
|
|
|
const withBaseFetchHeaders = (headers: IFetchConfig['headers']): { headers: IFetchConfig['headers'] } => ({
|
2018-01-18 15:21:34 -08:00
|
|
|
headers: Object.assign(
|
|
|
|
{
|
|
|
|
Accept: 'application/json',
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
},
|
|
|
|
headers
|
|
|
|
)
|
2017-10-18 17:38:51 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
2017-11-27 00:51:20 -08:00
|
|
|
* Sends a HTTP request and resolves with the JSON response
|
|
|
|
* @template T
|
|
|
|
* @param {string} url the url for the endpoint to request a response from
|
2018-02-21 14:41:05 -08:00
|
|
|
* @param {object} fetchConfig
|
|
|
|
* @returns {Promise<T>}
|
2017-10-18 17:38:51 -07:00
|
|
|
*/
|
2018-01-18 18:52:42 -08:00
|
|
|
const json = <T>(url: string = '', fetchConfig: IFetchOptions = {}): Promise<T> =>
|
2018-02-21 14:41:05 -08:00
|
|
|
fetch(url, fetchConfig).then<T>(response => throwIfApiError(response));
|
2017-10-18 17:38:51 -07:00
|
|
|
|
2017-09-19 13:32:04 -07:00
|
|
|
/**
|
|
|
|
* Conveniently gets a JSON response using the fetch api
|
2017-11-27 00:51:20 -08:00
|
|
|
* @template T
|
2018-07-19 16:18:06 -07:00
|
|
|
* @param {IFetchConfig} config
|
2017-09-19 13:32:04 -07:00
|
|
|
* @return {Promise<T>}
|
|
|
|
*/
|
2018-07-19 16:18:06 -07:00
|
|
|
const getJSON = <T>(config: IFetchConfig): Promise<T> => {
|
2018-01-18 15:21:34 -08:00
|
|
|
const fetchConfig = { ...withBaseFetchHeaders(config.headers), method: 'GET' };
|
2017-10-18 17:38:51 -07:00
|
|
|
|
|
|
|
return json<T>(config.url, fetchConfig);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2017-11-27 00:51:20 -08:00
|
|
|
* Initiates a POST request using the Fetch api
|
|
|
|
* @template T
|
2018-07-19 16:18:06 -07:00
|
|
|
* @param {IFetchConfig} config
|
2018-02-21 14:41:05 -08:00
|
|
|
* @returns {Promise<T>}
|
2017-10-18 17:38:51 -07:00
|
|
|
*/
|
2018-07-19 16:18:06 -07:00
|
|
|
const postJSON = <T>(config: IFetchConfig): Promise<T> => {
|
2017-11-30 10:33:07 -08:00
|
|
|
const requestBody = config.data ? { body: JSON.stringify(config.data) } : {};
|
2017-10-18 17:38:51 -07:00
|
|
|
const fetchConfig = Object.assign(
|
2017-11-30 10:33:07 -08:00
|
|
|
requestBody,
|
2017-10-18 17:38:51 -07:00
|
|
|
config.data && { body: JSON.stringify(config.data) },
|
2018-01-18 15:21:34 -08:00
|
|
|
withBaseFetchHeaders(config.headers),
|
2017-10-18 17:38:51 -07:00
|
|
|
{ method: 'POST' }
|
|
|
|
);
|
|
|
|
|
|
|
|
return json<T>(config.url, fetchConfig);
|
|
|
|
};
|
|
|
|
|
2017-11-27 00:51:20 -08:00
|
|
|
/**
|
|
|
|
* Initiates a DELETE request using the Fetch api
|
|
|
|
* @template T
|
2018-07-19 16:18:06 -07:00
|
|
|
* @param {IFetchConfig} config
|
2017-11-27 00:51:20 -08:00
|
|
|
* @return {Promise<T>}
|
|
|
|
*/
|
2018-07-19 16:18:06 -07:00
|
|
|
const deleteJSON = <T>(config: IFetchConfig): Promise<T> => {
|
2017-11-30 10:33:07 -08:00
|
|
|
const requestBody = config.data ? { body: JSON.stringify(config.data) } : {};
|
2018-01-18 15:21:34 -08:00
|
|
|
const fetchConfig = Object.assign(requestBody, withBaseFetchHeaders(config.headers), { method: 'DELETE' });
|
2017-10-18 17:38:51 -07:00
|
|
|
|
|
|
|
return json<T>(config.url, fetchConfig);
|
|
|
|
};
|
|
|
|
|
2017-11-27 00:51:20 -08:00
|
|
|
/**
|
|
|
|
* Initiates a PUT request using the Fetch api
|
|
|
|
* @template T
|
2018-07-19 16:18:06 -07:00
|
|
|
* @param {IFetchConfig} config
|
2017-11-27 00:51:20 -08:00
|
|
|
* @return {Promise<T>}
|
|
|
|
*/
|
2018-07-19 16:18:06 -07:00
|
|
|
const putJSON = <T>(config: IFetchConfig): Promise<T> => {
|
2017-11-30 10:33:07 -08:00
|
|
|
const requestBody = config.data ? { body: JSON.stringify(config.data) } : {};
|
|
|
|
|
2018-01-18 15:21:34 -08:00
|
|
|
const fetchConfig = Object.assign(requestBody, withBaseFetchHeaders(config.headers), { method: 'PUT' });
|
2017-09-19 13:32:04 -07:00
|
|
|
|
2017-10-18 17:38:51 -07:00
|
|
|
return json<T>(config.url, fetchConfig);
|
2017-09-19 13:32:04 -07:00
|
|
|
};
|
|
|
|
|
2017-09-20 14:25:27 -07:00
|
|
|
/**
|
|
|
|
* Requests the headers from a resource endpoint
|
2018-07-19 16:18:06 -07:00
|
|
|
* @param {IFetchConfig} config
|
2017-11-27 00:51:20 -08:00
|
|
|
* @return {Promise<Headers>}
|
2017-09-20 14:25:27 -07:00
|
|
|
*/
|
2018-07-19 16:18:06 -07:00
|
|
|
const getHeaders = async (config: IFetchConfig): Promise<Headers> => {
|
2017-09-20 14:25:27 -07:00
|
|
|
const fetchConfig = {
|
2018-01-18 15:21:34 -08:00
|
|
|
...withBaseFetchHeaders(config.headers),
|
2017-10-18 17:38:51 -07:00
|
|
|
method: 'HEAD'
|
2017-09-20 14:25:27 -07:00
|
|
|
};
|
2018-02-28 16:31:37 -08:00
|
|
|
const response = await fetch(config.url, fetchConfig);
|
|
|
|
const { ok, headers, status } = response;
|
2017-09-20 14:25:27 -07:00
|
|
|
|
|
|
|
if (ok) {
|
2017-09-20 16:10:52 -07:00
|
|
|
return headers;
|
2017-09-20 14:25:27 -07:00
|
|
|
}
|
|
|
|
|
2018-02-28 16:31:37 -08:00
|
|
|
throw new ApiError(status, apiErrorStatusMessage(status));
|
2017-09-20 14:25:27 -07:00
|
|
|
};
|
|
|
|
|
2018-07-19 16:18:06 -07:00
|
|
|
/**
|
|
|
|
* Wraps an api request or Promise that resolves a value, if the promise rejects with an
|
|
|
|
* @link ApiError and
|
|
|
|
* @link ApiResponseStatus.NotFound
|
|
|
|
* then the default value is returned then resolve with the default value
|
|
|
|
* @param {Promise<T>} request the request or promise to wrap
|
|
|
|
* @param {T} defaultValue resolved value if request throws ApiResponseStatus.NotFound
|
|
|
|
* @return {Promise<T>}
|
|
|
|
*/
|
|
|
|
const returnDefaultIfNotFound = async <T>(request: Promise<T>, defaultValue: T): Promise<T> => {
|
|
|
|
try {
|
|
|
|
return await request;
|
|
|
|
} catch (e) {
|
|
|
|
if (isNotFoundApiError(e)) {
|
|
|
|
return defaultValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export { getJSON, postJSON, deleteJSON, putJSON, getHeaders, returnDefaultIfNotFound };
|