2016-06-14 16:53:18 +02:00
|
|
|
import { request as baseRequest, sanitize } from 'js-utility-belt/es6';
|
2015-06-05 11:06:36 +02:00
|
|
|
|
2016-06-14 16:53:18 +02:00
|
|
|
import { makeCsrfHeader } from './csrf';
|
|
|
|
import { resolveUrl } from './url_resolver';
|
2015-07-24 13:44:28 +02:00
|
|
|
|
2015-06-01 14:22:04 +02:00
|
|
|
|
2016-06-14 16:53:18 +02:00
|
|
|
const DEFAULT_REQUEST_CONFIG = {
|
|
|
|
credentials: 'include',
|
|
|
|
headers: {
|
|
|
|
'Accept': 'application/json',
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Small wrapper around js-utility-belt's request that provides default settings, url mapping, and
|
|
|
|
* response handling.
|
|
|
|
*/
|
|
|
|
export default function request(url, config) {
|
|
|
|
// Load default fetch configuration and remove any falsy query parameters
|
|
|
|
const requestConfig = Object.assign({}, DEFAULT_REQUEST_CONFIG, config, config && {
|
|
|
|
query: config.query && sanitize(config.query)
|
|
|
|
});
|
|
|
|
|
|
|
|
// Add CSRF token
|
|
|
|
Object.assign(requestConfig.headers, makeCsrfHeader());
|
|
|
|
|
|
|
|
// Resolve url and send request
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
resolve(resolveUrl(url));
|
|
|
|
})
|
|
|
|
.then((apiUrl) => (
|
|
|
|
baseRequest(apiUrl, requestConfig)
|
|
|
|
// Catch any errors resulting from baseRequest first
|
2016-06-15 14:46:35 +02:00
|
|
|
.catch((err) => {
|
|
|
|
if (err == null) {
|
2016-06-14 16:53:18 +02:00
|
|
|
throw new Error(`For: ${apiUrl} - Server did not respond to the request. ` +
|
|
|
|
'(Not even displayed a 500)');
|
2016-06-15 14:46:35 +02:00
|
|
|
} else if (err instanceof Response) {
|
|
|
|
const res = err;
|
|
|
|
let responseErr = new Error(
|
|
|
|
`${res.status} - ${res.statusText} - on URL: ${res.url}`
|
|
|
|
);
|
2016-06-14 16:53:18 +02:00
|
|
|
|
|
|
|
// Try to parse the response body to see if we added more descriptive errors
|
|
|
|
// before rejecting with the error above.
|
|
|
|
return res
|
|
|
|
.json()
|
|
|
|
.then((body) => {
|
|
|
|
if (body && Array.isArray(body.errors) && body.errors.length) {
|
2016-06-15 14:46:35 +02:00
|
|
|
responseErr = new Error(body.errors.pop());
|
2016-06-14 16:53:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ES6 promises don't have a .finally() clause so we fake that here
|
|
|
|
// by forcing the .catch() clause to run
|
|
|
|
return Promise.reject();
|
|
|
|
})
|
2016-06-15 14:46:35 +02:00
|
|
|
// If parsing the response body throws, just rethrow the original
|
|
|
|
// response error
|
|
|
|
.catch(() => { throw responseErr; });
|
|
|
|
} else {
|
|
|
|
// Just rethrow the error since it's not a Response
|
|
|
|
throw err;
|
2016-06-14 16:53:18 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
// Handle successful requests
|
|
|
|
.then((res) => res
|
|
|
|
.json()
|
|
|
|
.then((body) => {
|
|
|
|
if (body) {
|
|
|
|
let error;
|
|
|
|
|
|
|
|
if (body.errors) {
|
|
|
|
error = new Error('Form Error');
|
|
|
|
error.json = body;
|
|
|
|
} else if (body.detail) {
|
|
|
|
error = Error(body.detail);
|
|
|
|
} else if ('success' in body && !body.success) {
|
|
|
|
const { status, statusText, type, url: resUrl } = res;
|
2015-12-14 14:54:57 +01:00
|
|
|
|
2016-06-14 16:53:18 +02:00
|
|
|
error = new Error('Client Request Error');
|
|
|
|
error.json = { body, status, statusText, type, url: resUrl };
|
|
|
|
}
|
2015-12-14 14:54:57 +01:00
|
|
|
|
2016-06-14 16:53:18 +02:00
|
|
|
if (error) {
|
|
|
|
throw error;
|
2015-12-14 14:54:57 +01:00
|
|
|
} else {
|
2016-06-14 16:53:18 +02:00
|
|
|
return body;
|
2015-12-14 14:54:57 +01:00
|
|
|
}
|
2015-07-17 16:44:24 +02:00
|
|
|
} else {
|
2016-06-14 16:53:18 +02:00
|
|
|
return {};
|
2015-07-17 16:44:24 +02:00
|
|
|
}
|
2016-06-14 16:53:18 +02:00
|
|
|
})
|
|
|
|
)
|
|
|
|
))
|
|
|
|
// Log any errors and rethrow
|
|
|
|
.catch((err) => {
|
|
|
|
console.error(err);
|
2015-07-14 16:29:01 +02:00
|
|
|
throw err;
|
2015-06-01 14:22:04 +02:00
|
|
|
});
|
|
|
|
}
|