diff --git a/lib/wrapper.js b/lib/wrapper.js index 474982f..fc572f6 100644 --- a/lib/wrapper.js +++ b/lib/wrapper.js @@ -23,6 +23,7 @@ class RequestWrapper { const method = this.requestOptions.method.toLowerCase(); const reqOptions = this._getRequestOptions(); + const source = axios.CancelToken.source(); const axiosOptions = { method, @@ -32,14 +33,15 @@ class RequestWrapper { timeout: reqOptions.timeout, transformResponse: [body => body], maxContentLength: this.requestOptions.maxContentLength, - validateStatus: () => true + validateStatus: () => true, + cancelToken: source.token }; return axios .request(axiosOptions) .then( response => this._transformResponse(response), - error => this._handleResponseError(error) + error => this._handleResponseError(error, source) ) .then(response => { const endTime = this._getTime(); @@ -58,7 +60,11 @@ class RequestWrapper { }; } - _handleResponseError(error) { + _handleResponseError(error, source) { + if (!axios.isCancel(error)) { + source.cancel(); + logger.info('Canceled request'); + } logger.fromError('fatal_error', error, this._getLogParameters()); throw new SuiteRequestError(error.message, 500); } diff --git a/lib/wrapper.spec.js b/lib/wrapper.spec.js index afcb736..bc299a6 100644 --- a/lib/wrapper.spec.js +++ b/lib/wrapper.spec.js @@ -45,16 +45,25 @@ describe('Wrapper', function() { 'x-custom': 'alma' }, timeout: 15000, - maxContentLength: 10485760 + maxContentLength: 10485760, + cancelToken: 'source-token' }; }); describe('request handling', function() { let wrapper; let requestGetStub; + let source; beforeEach(function() { - requestGetStub = this.sandbox.stub(axios, 'request').resolves(apiResponse); + requestGetStub = this.sandbox.stub(axios, 'request'); + requestGetStub.resolves(apiResponse); + source = { + token: 'source-token', + cancel: this.sandbox.stub() + }; + this.sandbox.stub(axios.CancelToken, 'source').returns(source); + wrapper = new Wrapper(escherRequestOptions, 'http:'); }); @@ -160,6 +169,50 @@ describe('Wrapper', function() { }); }); + describe('when there was an axios error', function() { + let isCancel; + + beforeEach(function() { + const axiosError = { + message: 'axios error message', + stack: [] + }; + isCancel = this.sandbox.stub(axios, 'isCancel'); + requestGetStub.rejects(axiosError); + }); + + context('when the request has not been canceled', function() { + beforeEach(function() { + isCancel.returns(false); + }); + + it('cancels the request', function *() { + try { + yield wrapper.send(); + throw new Error('should throw SuiteRequestError'); + } catch (err) { + expect(source.cancel).to.have.been.calledWith(); + } + }); + }); + + context('when the request has already been canceled', function() { + beforeEach(function() { + isCancel.returns(true); + }); + + it('does not cancel the request', function *() { + try { + yield wrapper.send(); + throw new Error('should throw SuiteRequestError'); + } catch (err) { + expect(source.cancel).not.to.have.been.calledWith(); + } + }); + }); + + }); + it('should throw error if json is not parsable (malformed)', function *() { apiResponse.data = 'this is an invalid json';