mirror of
				https://github.com/elyby/accounts-frontend.git
				synced 2025-05-31 14:11:58 +05:30 
			
		
		
		
	#303: properly handling json and request errors. Added InternalServerError
This commit is contained in:
		@@ -5,6 +5,7 @@ import { updateUser, setGuest } from 'components/user/actions';
 | 
			
		||||
import { setLocale } from 'components/i18n/actions';
 | 
			
		||||
import { setAccountSwitcher } from 'components/auth/actions';
 | 
			
		||||
import logger from 'services/logger';
 | 
			
		||||
import { InternalServerError } from 'services/request';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
    add,
 | 
			
		||||
@@ -36,7 +37,7 @@ export function authenticate({token, refreshToken}) {
 | 
			
		||||
    return (dispatch, getState) =>
 | 
			
		||||
        authentication.validateToken({token, refreshToken})
 | 
			
		||||
            .catch((resp = {}) => {
 | 
			
		||||
                if (resp.originalResponse && resp.originalResponse.status >= 500) {
 | 
			
		||||
                if (resp instanceof InternalServerError) {
 | 
			
		||||
                    // delegate error recovering to the later logic
 | 
			
		||||
                    return Promise.reject(resp);
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,12 @@
 | 
			
		||||
import { InternalServerError } from 'services/request';
 | 
			
		||||
 | 
			
		||||
export default function BsodMiddleware(dispatchBsod, logger) {
 | 
			
		||||
    return {
 | 
			
		||||
        catch(resp) {
 | 
			
		||||
            if (resp
 | 
			
		||||
                && resp.originalResponse
 | 
			
		||||
                && /404|5\d\d/.test(resp.originalResponse.status)
 | 
			
		||||
        catch(resp = {}) {
 | 
			
		||||
            if (resp instanceof InternalServerError
 | 
			
		||||
                || (resp.originalResponse
 | 
			
		||||
                    && /404|5\d\d/.test(resp.originalResponse.status)
 | 
			
		||||
                )
 | 
			
		||||
            ) {
 | 
			
		||||
                dispatchBsod();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { getJwtPayload } from 'functions';
 | 
			
		||||
import authentication from 'services/api/authentication';
 | 
			
		||||
import logger from 'services/logger';
 | 
			
		||||
import { InternalServerError } from 'services/request';
 | 
			
		||||
import { updateToken, logoutAll } from 'components/accounts/actions';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -72,7 +73,7 @@ function requestAccessToken(refreshToken, dispatch) {
 | 
			
		||||
        return authentication.requestToken(refreshToken)
 | 
			
		||||
            .then(({token}) => dispatch(updateToken(token)))
 | 
			
		||||
            .catch((resp = {}) => {
 | 
			
		||||
                if (resp.originalResponse && resp.originalResponse.status >= 500) {
 | 
			
		||||
                if (resp instanceof InternalServerError) {
 | 
			
		||||
                    return Promise.reject(resp);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								src/services/request/InternalServerError.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/services/request/InternalServerError.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
function InternalServerError(error, resp) {
 | 
			
		||||
    error = error || {};
 | 
			
		||||
 | 
			
		||||
    this.name = 'InternalServerError';
 | 
			
		||||
    this.message = 'InternalServerError';
 | 
			
		||||
    this.stack = (new Error()).stack;
 | 
			
		||||
 | 
			
		||||
    if (resp) {
 | 
			
		||||
        this.originalResponse = resp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (error.message) {
 | 
			
		||||
        this.message = error.message;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (typeof error === 'string') {
 | 
			
		||||
        this.message = error;
 | 
			
		||||
    } else {
 | 
			
		||||
        this.error = error;
 | 
			
		||||
        Object.assign(this, error);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
InternalServerError.prototype = Object.create(Error.prototype);
 | 
			
		||||
InternalServerError.prototype.constructor = InternalServerError;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default InternalServerError;
 | 
			
		||||
@@ -1,3 +1,6 @@
 | 
			
		||||
import request from './request';
 | 
			
		||||
import InternalServerError from './InternalServerError';
 | 
			
		||||
 | 
			
		||||
export default request;
 | 
			
		||||
 | 
			
		||||
export { InternalServerError };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import PromiseMiddlewareLayer from './PromiseMiddlewareLayer';
 | 
			
		||||
import InternalServerError from './InternalServerError';
 | 
			
		||||
 | 
			
		||||
const middlewareLayer = new PromiseMiddlewareLayer();
 | 
			
		||||
 | 
			
		||||
@@ -65,12 +66,27 @@ export default {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const checkStatus = (resp) => Promise[resp.status >= 200 && resp.status < 300 ? 'resolve' : 'reject'](resp);
 | 
			
		||||
const toJSON = (resp) => resp.json().then((json) => {
 | 
			
		||||
    json.originalResponse = resp;
 | 
			
		||||
const toJSON = (resp = {}) => {
 | 
			
		||||
    if (!resp.json) {
 | 
			
		||||
        // e.g. 'TypeError: Failed to fetch' due to CORS
 | 
			
		||||
        throw new InternalServerError(resp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return json;
 | 
			
		||||
    return resp.json().then((json) => {
 | 
			
		||||
        json.originalResponse = resp;
 | 
			
		||||
 | 
			
		||||
        return json;
 | 
			
		||||
    }, (error) => Promise.reject(
 | 
			
		||||
        new InternalServerError(error, resp)
 | 
			
		||||
    ));
 | 
			
		||||
};
 | 
			
		||||
const rejectWithJSON = (resp) => toJSON(resp).then((resp) => {
 | 
			
		||||
    if (resp.originalResponse.status >= 500) {
 | 
			
		||||
        throw new InternalServerError(resp, resp.originalResponse);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    throw resp;
 | 
			
		||||
});
 | 
			
		||||
const rejectWithJSON = (resp) => toJSON(resp).then((resp) => {throw resp;});
 | 
			
		||||
const handleResponseSuccess = (resp) => Promise[resp.success || typeof resp.success === 'undefined' ? 'resolve' : 'reject'](resp);
 | 
			
		||||
 | 
			
		||||
function doFetch(url, options = {}) {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import sinon from 'sinon';
 | 
			
		||||
import { routeActions } from 'react-router-redux';
 | 
			
		||||
 | 
			
		||||
import logger from 'services/logger';
 | 
			
		||||
import { InternalServerError } from 'services/request';
 | 
			
		||||
import authentication from 'services/api/authentication';
 | 
			
		||||
import {
 | 
			
		||||
    authenticate,
 | 
			
		||||
@@ -133,9 +134,7 @@ describe('components/accounts/actions', () => {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('rejects when 5xx without logouting', () => {
 | 
			
		||||
            const resp = {
 | 
			
		||||
                originalResponse: {status: 500}
 | 
			
		||||
            };
 | 
			
		||||
            const resp = new InternalServerError(null, {status: 500});
 | 
			
		||||
 | 
			
		||||
            authentication.validateToken.returns(Promise.reject(resp));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import sinon from 'sinon';
 | 
			
		||||
import refreshTokenMiddleware from 'components/user/middlewares/refreshTokenMiddleware';
 | 
			
		||||
 | 
			
		||||
import authentication from 'services/api/authentication';
 | 
			
		||||
import { InternalServerError } from 'services/request';
 | 
			
		||||
import { updateToken } from 'components/accounts/actions';
 | 
			
		||||
 | 
			
		||||
const refreshToken = 'foo';
 | 
			
		||||
@@ -145,11 +146,7 @@ describe('refreshTokenMiddleware', () => {
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should not logout if request failed with 5xx', () => {
 | 
			
		||||
                const resp = {
 | 
			
		||||
                    originalResponse: {
 | 
			
		||||
                        status: 500
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                const resp = new InternalServerError(null, {status: 500});
 | 
			
		||||
 | 
			
		||||
                authentication.requestToken.returns(Promise.reject(resp));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								tests/services/request/request.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								tests/services/request/request.test.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import expect from 'unexpected';
 | 
			
		||||
import sinon from 'sinon';
 | 
			
		||||
 | 
			
		||||
import request from 'services/request';
 | 
			
		||||
import { InternalServerError } from 'services/request';
 | 
			
		||||
 | 
			
		||||
describe('services/request', () => {
 | 
			
		||||
    beforeEach(() => {
 | 
			
		||||
        sinon.stub(window, 'fetch').named('fetch');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    afterEach(() => {
 | 
			
		||||
        window.fetch.restore();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('InternalServerError', () => {
 | 
			
		||||
        it('should wrap fetch error', () => {
 | 
			
		||||
            const resp = new TypeError('Fetch error');
 | 
			
		||||
 | 
			
		||||
            fetch.returns(Promise.reject(resp));
 | 
			
		||||
 | 
			
		||||
            return expect(request.get('/foo'), 'to be rejected')
 | 
			
		||||
                .then((error) => {
 | 
			
		||||
                    expect(error, 'to be an', InternalServerError);
 | 
			
		||||
                    expect(error.originalResponse, 'to be undefined');
 | 
			
		||||
                    expect(error.message, 'to equal', resp.message);
 | 
			
		||||
                });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should wrap json errors', () => {
 | 
			
		||||
            const resp = new Response('bad resp format', {status: 200});
 | 
			
		||||
 | 
			
		||||
            fetch.returns(Promise.resolve(resp));
 | 
			
		||||
 | 
			
		||||
            return expect(request.get('/foo'), 'to be rejected')
 | 
			
		||||
                .then((error) => {
 | 
			
		||||
                    expect(error, 'to be an', InternalServerError);
 | 
			
		||||
                    expect(error.originalResponse, 'to be', resp);
 | 
			
		||||
                    expect(error.message, 'to contain', 'JSON');
 | 
			
		||||
                });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('should wrap 5xx errors', () => {
 | 
			
		||||
            const resp = new Response('{}', {status: 500});
 | 
			
		||||
 | 
			
		||||
            fetch.returns(Promise.resolve(resp));
 | 
			
		||||
 | 
			
		||||
            return expect(request.get('/foo'), 'to be rejected')
 | 
			
		||||
                .then((error) => {
 | 
			
		||||
                    expect(error, 'to be an', InternalServerError);
 | 
			
		||||
                    expect(error.originalResponse, 'to be', resp);
 | 
			
		||||
                });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
		Reference in New Issue
	
	Block a user