2018-01-30 10:33:15 -05:00
|
|
|
|
const defaults = require('lodash.defaults');
|
|
|
|
|
const xhr = require('xhr');
|
2016-06-06 10:09:01 -04:00
|
|
|
|
|
2018-01-30 10:33:15 -05:00
|
|
|
|
const jar = require('./jar');
|
|
|
|
|
const log = require('./log');
|
|
|
|
|
const urlParams = require('./url-params');
|
2016-06-06 10:09:01 -04:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper method that constructs requests to the scratch api.
|
|
|
|
|
* Custom arguments:
|
|
|
|
|
* - useCsrf [boolean] – handles unique csrf token retrieval for POST requests. This prevents
|
|
|
|
|
* CSRF forgeries (see: https://www.squarefree.com/securitytips/web-developers.html#CSRF)
|
|
|
|
|
*
|
|
|
|
|
* It also takes in other arguments specified in the xhr library spec.
|
2019-01-07 11:29:14 -05:00
|
|
|
|
*
|
2018-01-30 10:33:15 -05:00
|
|
|
|
* @param {object} opts optional xhr args (see above)
|
|
|
|
|
* @param {Function} callback [description]
|
2016-06-06 10:09:01 -04:00
|
|
|
|
*/
|
2018-01-30 10:33:15 -05:00
|
|
|
|
module.exports = (opts, callback) => {
|
2016-08-11 14:55:20 -04:00
|
|
|
|
defaults(opts, {
|
2016-06-06 10:09:01 -04:00
|
|
|
|
host: process.env.API_HOST,
|
2016-08-11 14:54:13 -04:00
|
|
|
|
headers: {},
|
2016-06-16 17:24:31 -04:00
|
|
|
|
responseType: 'json',
|
2016-06-06 10:09:01 -04:00
|
|
|
|
useCsrf: false
|
|
|
|
|
});
|
|
|
|
|
|
2016-08-11 15:19:32 -04:00
|
|
|
|
if (opts.host === '') {
|
|
|
|
|
defaults(opts.headers, {
|
|
|
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-06 10:09:01 -04:00
|
|
|
|
opts.uri = opts.host + opts.uri;
|
|
|
|
|
|
|
|
|
|
if (opts.params) {
|
|
|
|
|
opts.uri = [opts.uri, urlParams(opts.params)]
|
|
|
|
|
.join(opts.uri.indexOf('?') === -1 ? '?' : '&');
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-16 17:24:31 -04:00
|
|
|
|
if (opts.formData) {
|
|
|
|
|
opts.body = urlParams(opts.formData);
|
|
|
|
|
opts.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-30 10:33:15 -05:00
|
|
|
|
const apiRequest = options => {
|
|
|
|
|
if (options.host !== '') {
|
2017-08-31 17:05:22 -04:00
|
|
|
|
if ('withCredentials' in new XMLHttpRequest()) {
|
2018-01-30 10:33:15 -05:00
|
|
|
|
options.useXDR = false;
|
2017-08-31 17:05:22 -04:00
|
|
|
|
} else {
|
|
|
|
|
// For IE < 10, we must use XDR for cross-domain requests. XDR does not support
|
|
|
|
|
// custom headers.
|
2018-01-30 10:33:15 -05:00
|
|
|
|
options.useXDR = true;
|
|
|
|
|
delete options.headers;
|
|
|
|
|
if (options.authentication) {
|
|
|
|
|
const authenticationParams = [`x-token=${options.authentication}`];
|
|
|
|
|
const parts = options.uri.split('?');
|
|
|
|
|
const qs = (parts[1] || '')
|
|
|
|
|
.split('&')
|
|
|
|
|
.concat(authenticationParams)
|
|
|
|
|
.join('&');
|
|
|
|
|
options.uri = `${parts[0]}?${qs}`;
|
2017-08-31 17:05:22 -04:00
|
|
|
|
}
|
2016-06-06 10:09:01 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-30 10:33:15 -05:00
|
|
|
|
xhr(options, (err, res, body) => {
|
2016-06-06 10:09:01 -04:00
|
|
|
|
if (err) log.error(err);
|
2018-01-30 10:33:15 -05:00
|
|
|
|
if (options.responseType === 'json' && typeof body === 'string') {
|
2016-07-03 15:54:37 -04:00
|
|
|
|
// IE doesn't parse responses as JSON without the json attribute,
|
|
|
|
|
// even with responseType: 'json'.
|
|
|
|
|
// See https://github.com/Raynos/xhr/issues/123
|
|
|
|
|
try {
|
|
|
|
|
body = JSON.parse(body);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// Not parseable anyway, don't worry about it
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-06-06 10:09:01 -04:00
|
|
|
|
// Legacy API responses come as lists, and indicate to redirect the client like
|
|
|
|
|
// [{success: true, redirect: "/location/to/redirect"}]
|
|
|
|
|
try {
|
|
|
|
|
if ('redirect' in body[0]) window.location = body[0].redirect;
|
2018-01-30 10:33:15 -05:00
|
|
|
|
} catch (e) {
|
2016-06-06 10:09:01 -04:00
|
|
|
|
// do nothing
|
|
|
|
|
}
|
|
|
|
|
callback(err, body, res);
|
|
|
|
|
});
|
2018-01-30 10:33:15 -05:00
|
|
|
|
};
|
2016-06-06 10:09:01 -04:00
|
|
|
|
|
|
|
|
|
if (typeof jar.get('scratchlanguage') !== 'undefined') {
|
2018-01-30 10:33:15 -05:00
|
|
|
|
opts.headers['Accept-Language'] = `${jar.get('scratchlanguage')}, en;q=0.8`;
|
2016-06-06 10:09:01 -04:00
|
|
|
|
}
|
|
|
|
|
if (opts.authentication) {
|
|
|
|
|
opts.headers['X-Token'] = opts.authentication;
|
|
|
|
|
}
|
|
|
|
|
if (opts.useCsrf) {
|
2018-01-30 10:33:15 -05:00
|
|
|
|
jar.use('scratchcsrftoken', '/csrf_token/', (err, csrftoken) => {
|
2016-06-06 10:09:01 -04:00
|
|
|
|
if (err) return log.error('Error while retrieving CSRF token', err);
|
|
|
|
|
opts.headers['X-CSRFToken'] = csrftoken;
|
|
|
|
|
apiRequest(opts);
|
2018-01-30 10:33:15 -05:00
|
|
|
|
});
|
2016-06-06 10:09:01 -04:00
|
|
|
|
} else {
|
|
|
|
|
apiRequest(opts);
|
|
|
|
|
}
|
|
|
|
|
};
|