Skip to content
6 changes: 3 additions & 3 deletions bootstrap-theme.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions default-theme.js

Large diffs are not rendered by default.

28 changes: 27 additions & 1 deletion dummy/src/actions/request-test-buttons.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {fetch} from "../../../src/index";
import {fetch, xhr} from "../../../src/index";

export const REQUEST_TEST_START = "REQUEST_TEST_START";
export const REQUEST_TEST_COMPLETE = "REQUEST_TEST_COMPLETE";
Expand Down Expand Up @@ -47,3 +47,29 @@ export function requestTest(url, key) {
});
};
}
export function requestTestXhr(url, key) {
return dispatch => {
dispatch(requestTestStart(key));

return xhr(url, {
credentials: "include"
})
.then(resp => {
if (resp && resp.statusText === "OK") {
dispatch(requestTestComplete(key))
} else {
dispatch(requestTestError(key));
}

return resp.json();
})
.then(json => {
console.log("@-->resp json", json);
return json;
})
.catch(resp => {
console.log("fail", resp);
dispatch(requestTestError(key))
});
};
}
21 changes: 21 additions & 0 deletions dummy/src/views/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,27 @@ class Main extends React.Component {
path="/demo/members_only_group"
endpointKey="any"/></td>
</tr>
<tr>
<td>Default user:</td>
<td><RequestTestButton
xhr
path="/demo/members_only"
endpointKey="default"/></td>
</tr>
<tr>
<td>Alternate user class:</td>
<td><RequestTestButton
xhr
path="/demo/members_only_mang"
endpointKey="evilUser"/></td>
</tr>
<tr>
<td>Group that includes both user classes:</td>
<td><RequestTestButton
xhr
path="/demo/members_only_group"
endpointKey="any"/></td>
</tr>
</tbody>
</Table>
</IndexPanel>
Expand Down
8 changes: 6 additions & 2 deletions dummy/src/views/partials/RequestTestButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { PropTypes } from "react";
import {Glyphicon} from "react-bootstrap";
import {ButtonLoader} from "../../../../src/views/bootstrap";
import {connect} from "react-redux";
import {requestTest} from "../../actions/request-test-buttons";
import {requestTest, requestTestXhr} from "../../actions/request-test-buttons";
import {getApiUrl} from "../../../../src/utils/session-storage";

class RequestTestButton extends React.Component {
Expand All @@ -17,7 +17,11 @@ class RequestTestButton extends React.Component {

handleClick () {
let url = getApiUrl() + this.props.path;
this.props.dispatch(requestTest(url, this.props.path));
if (!this.props.xhr) {
this.props.dispatch(requestTest(url, this.props.path));
} else {
this.props.dispatch(requestTestXhr(url, this.props.path));
}
}

render () {
Expand Down
4 changes: 2 additions & 2 deletions index.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions material-ui-theme.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,4 @@ export {

/* utils */
export {default as fetch} from "./utils/fetch";
export {xhr} from "./utils/fetch";
84 changes: 84 additions & 0 deletions src/utils/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,34 @@ function updateAuthCredentials(resp) {
return resp;
}

function updateAuthCredentialsXhr(xhrReq, url) {
// check config apiUrl matches the current response url
if (isApiRequest(url)) {
// set header for each key in `tokenFormat` config
var newHeaders = {};

// set flag to ensure that we don't accidentally nuke the headers
// if the response tokens aren't sent back from the API
var blankHeaders = true;

// set header key + val for each key in `tokenFormat` config
for (var key in getTokenFormat()) {
newHeaders[key] = xhrReq.getResponseHeader(key);

if (newHeaders[key]) {
blankHeaders = false;
}
}

// persist headers for next request
if (!blankHeaders) {
persistData(C.SAVED_CREDS_KEY, newHeaders);
}
}

return xhrReq.response;
}

export default function (url, options={}) {
if (!options.headers) {
options.headers = {}
Expand All @@ -82,3 +110,59 @@ export default function (url, options={}) {
return originalFetch(url, options)
.then(resp => updateAuthCredentials(resp));
}

export function xhr(url, options) {
return extendRequesterXhr(url, xhrRequest, options);
}

function extendRequesterXhr(url, requester, options={}) {
if (!options.headers) {
options.headers = {}
}
extend(options.headers, getAuthHeaders(url));
return requester(url, options)
.then(xhrReq => updateAuthCredentialsXhr(xhrReq, url));
}

function xhrRequest(url, options) {
return new Promise((resolve, reject) => {
const xhrReq = new XMLHttpRequest();
xhrReq.open(options.method, url);
xhrReq.onload = () => {
if (xhrReq.status >= 200 && xhrReq.status < 300) {
resolve(xhrReq);
} else {
reject({
status: xhrReq.status,
statusText: xhrReq.statusText
});
}
};
xhrReq.onerror = () => {
reject({
status: xhrReq.status,
statusText: xhrReq.statusText
});
};
if (options.headers) {
Object.keys(options.headers).forEach((key) => {
xhrReq.setRequestHeader(key, options.headers[key]);
});
}
let params = options.params;
if (params && typeof params === 'object' && !params.formData) {
params = objectToQueryString(params);
}
if (options.progress) {
xhrReq.upload.addEventListener('progress', options.progress, false);
}
if (params.formData) {
params = params.formData;
}
xhrReq.send(params);
});
}

function objectToQueryString(obj) {
return Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&');
}
9 changes: 8 additions & 1 deletion test/actions/client-config-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ReactDOM from "react-dom";
import {retrieveData, getCurrentEndpointKey} from "../../src/utils/session-storage";
import {syncHistoryWithStore, push} from "react-router-redux";
import {expect} from "chai";
import {fetch} from "../../src";
import {fetch, xhr} from "../../src";
import nock from "nock";

var testUid = "test@test.com",
Expand Down Expand Up @@ -164,6 +164,13 @@ export default function() {
expect(retrieveData("authHeaders")["access-token"]).to.equal(nextToken);
done();
});

// next request should include auth headers
xhr(`${altApiUrl}/api/hello`).then(() => {
// cookie should have been updated to latest
expect(retrieveData("authHeaders")["access-token"]).to.equal(nextToken);
done();
});
})
.catch(err => console.log("@-->error", err.stack));

Expand Down