Move api mixin to lib, remove mixin

The mixin doesn't gain us anything except complexity
This commit is contained in:
Ray Schamp 2016-06-06 10:09:01 -04:00
parent 56e16bc0dc
commit d7df1e980f
9 changed files with 109 additions and 103 deletions

View file

@ -1,7 +1,6 @@
var classNames = require('classnames');
var React = require('react');
var Api = require('../../mixins/api.jsx');
var jar = require('../../lib/jar.js');
var languages = require('../../../languages.json');
var Form = require('../forms/form.jsx');
@ -12,9 +11,6 @@ var Select = require('../forms/select.jsx');
*/
var LanguageChooser = React.createClass({
type: 'LanguageChooser',
mixins: [
Api
],
getDefaultProps: function () {
return {
languages: languages,

View file

@ -7,7 +7,7 @@ var injectIntl = ReactIntl.injectIntl;
var sessionActions = require('../../../redux/session.js');
var Api = require('../../../mixins/api.jsx');
var api = require('../../../lib/api');
var Avatar = require('../../avatar/avatar.jsx');
var Button = require('../../forms/button.jsx');
var Dropdown = require('../../dropdown/dropdown.jsx');
@ -24,9 +24,6 @@ Modal.setAppElement(document.getElementById('view'));
var Navigation = React.createClass({
type: 'Navigation',
mixins: [
Api
],
getInitialState: function () {
return {
accountNavOpen: false,
@ -85,7 +82,7 @@ var Navigation = React.createClass({
return '/users/' + this.props.session.session.user.username + '/';
},
getMessageCount: function () {
this.api({
api({
method: 'get',
uri: '/users/' + this.props.session.session.user.username + '/messages/count'
}, function (err, body) {
@ -110,7 +107,7 @@ var Navigation = React.createClass({
handleLogIn: function (formData, callback) {
this.setState({'loginError': null});
formData['useMessages'] = true;
this.api({
api({
method: 'post',
host: '',
uri: '/accounts/login/',
@ -142,7 +139,7 @@ var Navigation = React.createClass({
},
handleLogOut: function (e) {
e.preventDefault();
this.api({
api({
host: '',
method: 'post',
uri: '/accounts/logout/',

79
src/lib/api.js Normal file
View file

@ -0,0 +1,79 @@
var defaults = require('lodash.defaults');
var xhr = require('xhr');
var jar = require('./jar');
var log = require('./log');
var urlParams = require('./url-params');
/**
* 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.
*/
module.exports = function (opts, callback) {
defaults(opts, {
host: process.env.API_HOST,
headers: {},
json: {},
useCsrf: false
});
defaults(opts.headers, {
'X-Requested-With': 'XMLHttpRequest'
});
opts.uri = opts.host + opts.uri;
if (opts.params) {
opts.uri = [opts.uri, urlParams(opts.params)]
.join(opts.uri.indexOf('?') === -1 ? '?' : '&');
}
var apiRequest = function (opts) {
if (opts.host !== '') {
// For IE < 10, we must use XDR for cross-domain requests. XDR does not support
// custom headers.
defaults(opts, {useXDR: true});
delete opts.headers;
if (opts.authentication) {
var authenticationParams = ['x-token=' + opts.authentication];
var parts = opts.uri.split('?');
var qs = (parts[1] || '').split('&').concat(authenticationParams).join('&');
opts.uri = parts[0] + '?' + qs;
}
}
xhr(opts, function (err, res, body) {
if (err) log.error(err);
// 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;
} catch (err) {
// do nothing
}
callback(err, body, res);
});
}.bind(this);
if (typeof jar.get('scratchlanguage') !== 'undefined') {
opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8';
}
if (opts.authentication) {
opts.headers['X-Token'] = opts.authentication;
}
if (opts.useCsrf) {
jar.use('scratchcsrftoken', '/csrf_token/', function (err, csrftoken) {
if (err) return log.error('Error while retrieving CSRF token', err);
opts.json.csrftoken = csrftoken;
opts.headers['X-CSRFToken'] = csrftoken;
apiRequest(opts);
}.bind(this));
} else {
apiRequest(opts);
}
};

14
src/lib/url-params.js Normal file
View file

@ -0,0 +1,14 @@
/* Turn an object into an url param string
* urlParams({a: 1, b: 2, c: 3})
* // a=1&b=2&c=3
*/
module.exports = function urlParams (values) {
return Object
.keys(values)
.map(function (key) {
return [key, values[key]]
.map(encodeURIComponent)
.join('=');
})
.join('&');
};

View file

@ -1,76 +0,0 @@
var defaults = require('lodash.defaults');
var xhr = require('xhr');
var jar = require('../lib/jar.js');
var log = require('../lib/log.js');
/**
* Component mixin 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.
*/
var Api = {
api: function (opts, callback) {
defaults(opts, {
host: window.env.API_HOST,
headers: {},
json: {},
useCsrf: false
});
defaults(opts.headers, {
'X-Requested-With': 'XMLHttpRequest'
});
opts.uri = opts.host + opts.uri;
var apiRequest = function (opts) {
if (opts.host !== '') {
// For IE < 10, we must use XDR for cross-domain requests. XDR does not support
// custom headers.
defaults(opts, {useXDR: true});
delete opts.headers;
if (opts.authentication) {
var authenticationParams = ['x-token=' + opts.authentication];
var parts = opts.uri.split('?');
var qs = (parts[1] || '').split('&').concat(authenticationParams).join('&');
opts.uri = parts[0] + '?' + qs;
}
}
xhr(opts, function (err, res, body) {
if (err) log.error(err);
// 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;
} catch (err) {
// do nothing
}
callback(err, body);
});
}.bind(this);
if (typeof jar.get('scratchlanguage') !== 'undefined') {
opts.headers['Accept-Language'] = jar.get('scratchlanguage') + ', en;q=0.8';
}
if (opts.authentication) {
opts.headers['X-Token'] = opts.authentication;
}
if (opts.useCsrf) {
jar.use('scratchcsrftoken', '/csrf_token/', function (err, csrftoken) {
if (err) return log.error('Error while retrieving CSRF token', err);
opts.json.csrftoken = csrftoken;
opts.headers['X-CSRFToken'] = csrftoken;
apiRequest(opts);
}.bind(this));
} else {
apiRequest(opts);
}
}
};
module.exports = Api;

View file

@ -1,5 +1,5 @@
var keyMirror = require('keymirror');
var api = require('../mixins/api.jsx').api;
var api = require('../lib/api');
var Types = keyMirror({
SET_DETAILS: null,

View file

@ -1,5 +1,5 @@
var keyMirror = require('keymirror');
var api = require('../mixins/api.jsx').api;
var api = require('../lib/api');
var Types = keyMirror({
SET_SCHEDULE: null,

View file

@ -1,7 +1,7 @@
var keyMirror = require('keymirror');
var defaults = require('lodash.defaults');
var api = require('../mixins/api.jsx').api;
var api = require('../lib/api');
var tokenActions = require('./token.js');
var Types = keyMirror({

View file

@ -2,13 +2,12 @@ var connect = require('react-redux').connect;
var injectIntl = require('react-intl').injectIntl;
var omit = require('lodash.omit');
var React = require('react');
var render = require('../../lib/render.jsx');
var api = require('../../lib/api');
var render = require('../../lib/render.jsx');
var sessionActions = require('../../redux/session.js');
var shuffle = require('../../lib/shuffle.js').shuffle;
var Api = require('../../mixins/api.jsx');
var Activity = require('../../components/activity/activity.jsx');
var AdminPanel = require('../../components/adminpanel/adminpanel.jsx');
var DropdownBanner = require('../../components/dropdown-banner/banner.jsx');
@ -25,9 +24,6 @@ require('./splash.scss');
var Splash = injectIntl(React.createClass({
type: 'Splash',
mixins: [
Api
],
getInitialState: function () {
return {
projectCount: 14000000, // gets the shared project count
@ -90,42 +86,42 @@ var Splash = injectIntl(React.createClass({
}
},
getActivity: function () {
this.api({
api({
uri: '/proxy/users/' + this.props.session.session.user.username + '/activity?limit=5'
}, function (err, body) {
if (!err) this.setState({activity: body});
}.bind(this));
},
getFeaturedGlobal: function () {
this.api({
api({
uri: '/proxy/featured'
}, function (err, body) {
if (!err) this.setState({featuredGlobal: body});
}.bind(this));
},
getFeaturedCustom: function () {
this.api({
api({
uri: '/proxy/users/' + this.props.session.session.user.id + '/featured'
}, function (err, body) {
if (!err) this.setState({featuredCustom: body});
}.bind(this));
},
getNews: function () {
this.api({
api({
uri: '/news?limit=3'
}, function (err, body) {
if (!err) this.setState({news: body});
}.bind(this));
},
getProjectCount: function () {
this.api({
api({
uri: '/projects/count/all'
}, function (err, body) {
if (!err) this.setState({projectCount: body.count});
}.bind(this));
},
refreshHomepageCache: function () {
this.api({
api({
host: '',
uri: '/scratch_admin/homepage/clear-cache/',
method: 'post',
@ -161,7 +157,7 @@ var Splash = injectIntl(React.createClass({
this.setState({emailConfirmationModalOpen: false});
},
handleDismiss: function (cue) {
this.api({
api({
host: '',
uri: '/site-api/users/set-template-cue/',
method: 'post',