From f199d2b40e2d6488a4c5999e0749bf1bc6cbf000 Mon Sep 17 00:00:00 2001 From: Arminkhodaei Date: Thu, 23 Apr 2020 00:44:28 +0200 Subject: [PATCH 1/2] pre-request and post-response interceptors are added --- src/index.js | 66 ++++++++++++++++++++++++++++++++++++++++++---- src/interceptor.js | 45 +++++++++++++++++++++++++++++++ test/index.test.js | 50 +++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 src/interceptor.js diff --git a/src/index.js b/src/index.js index c35b632..027ed89 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,5 @@ +import Interceptor from './interceptor'; + /** * Copyright 2018 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -91,12 +93,18 @@ export default (function create(defaults) { redaxios.all = Promise.all; /** @public */ - redaxios.spread = function(fn) { + redaxios.spread = function (fn) { return function (results) { return fn.apply(this, results); }; }; + /** @public */ + redaxios.interceptors = { + request: new Interceptor(), + response: new Interceptor() + }; + function deepMerge(opts, overrides, lowerCase) { if (Array.isArray(opts)) { return opts.concat(overrides); @@ -131,7 +139,18 @@ export default (function create(defaults) { config = url; url = config.url; } - const options = deepMerge(defaults, config || {}); + + // pre-request interception + let options = deepMerge(defaults, config || {}); + if (redaxios.interceptors.request.handlers.length > 0) { + redaxios.interceptors.request.handlers.forEach(handler => { + if (handler !== null) { + const resultConfig = handler.done(config); + options = deepMerge(options, resultConfig || {}); + } + }); + } + let data = options.data; if (options.transformRequest) { @@ -154,7 +173,7 @@ export default (function create(defaults) { let parts = document.cookie.split(/ *[;=] */); for (let i = 0; i < parts.length; i += 2) { if (parts[i] == options.xsrfCookieName) { - customHeaders[options.xsrfHeaderName] = decodeURIComponent(parts[i+1]); + customHeaders[options.xsrfHeaderName] = decodeURIComponent(parts[i + 1]); break; } } @@ -165,7 +184,7 @@ export default (function create(defaults) { } /** @type {Response} */ - const response = {}; + let response = {}; response.config = config; return fetch(url, { @@ -177,16 +196,53 @@ export default (function create(defaults) { for (i in res) { if (typeof res[i] != 'function') response[i] = res[i]; } + + // response error interception + if (!(res.status >= 200 && res.status < 300) + && redaxios.interceptors.response.handlers.length > 0) { + redaxios.interceptors.response.handlers.forEach(handler => { + if (handler && handler.error) + handler.error(res); + }); + } + if (!(options.validateStatus ? options.validateStatus(res.status) : res.ok)) { return Promise.reject(res); } const withData = options.responseType === 'stream' ? Promise.resolve(res.body) : res[options.responseType || 'text'](); + return withData.then((data) => { - response.data = data; + // post-response interception 200 status code + if (res.status >= 200 && res.status < 300 + && redaxios.interceptors.response.handlers.length > 0) { + response.data = data; + redaxios.interceptors.response.handlers.forEach(handler => { + if (handler !== null) { + const interceptedResponse = handler.done(response); + + if (interceptedResponse) + response = interceptedResponse; + } + }); + } + else { + response.data = data; + } + return response; }); + }).catch(error => { + // request error interception + if (redaxios.interceptors.request.handlers.length > 0) { + redaxios.interceptors.request.handlers.forEach(handler => { + if (handler && handler.error) + handler.error(error); + }); + } + + throw error; }); } diff --git a/src/interceptor.js b/src/interceptor.js new file mode 100644 index 0000000..85de121 --- /dev/null +++ b/src/interceptor.js @@ -0,0 +1,45 @@ +/** + * Copyright 2018 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function Interceptor() { + + /** + * @type {Array<{done: Function, error: Function }>} + */ + this.handlers = []; + + /** + * Register an interceptor + * @param {Function} done + * @param {Function} [error] + * @returns {number} The interceptor Id to be used for ejection + */ + this.use = function(done, error) { + this.handlers.push({ + done, + error: error || (() => {}) + }); + + return this.handlers.length - 1; + }; + + /** + * @param {number} id - A registered interceptor Id + */ + this.eject = function (id) { + if (this.handlers[id]) + this.handlers[id] = null; + }; +} + +export default Interceptor; \ No newline at end of file diff --git a/test/index.test.js b/test/index.test.js index 7173c93..e6aef91 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -73,4 +73,54 @@ describe('redaxios', () => { window.fetch = oldFetch; } }); + + it('pre-request interceptor', async () => { + const preRequestInterceptor = axios.interceptors.request.use((config) => { + config.test = 'testValue'; + return config; + }); + const req = axios.get(jsonExample, { + responseType: 'json' + }); + expect(req).toBeInstanceOf(Promise); + const res = await req; + expect(res).toBeInstanceOf(Object); + expect(res.config.test).toBe('testValue'); + + // eject the interceptor + axios.interceptors.request.eject(preRequestInterceptor); + + const newReq = axios.get(jsonExample, { + responseType: 'json' + }); + expect(newReq).toBeInstanceOf(Promise); + const newRes = await newReq; + expect(newRes).toBeInstanceOf(Object); + expect(newRes.config.test).toBe(undefined); + }); + + it('response interceptor', async () => { + const postResponseInterceptor = axios.interceptors.response.use((response) => { + response.data.hello = `${response.data.hello} from interceptor`; + return response; + }); + const req = axios.get(jsonExample, { + responseType: 'json' + }); + expect(req).toBeInstanceOf(Promise); + const res = await req; + expect(res).toBeInstanceOf(Object); + expect(res.data).toEqual({ hello: 'world from interceptor' }); + + // eject the interceptor + axios.interceptors.response.eject(postResponseInterceptor); + + const newReq = axios.get(jsonExample, { + responseType: 'json' + }); + expect(newReq).toBeInstanceOf(Promise); + const newRes = await newReq; + expect(newRes).toBeInstanceOf(Object); + expect(newRes.data).toEqual({ hello: 'world' }); + }); }); From d837c1ec577e6ebe5f20030f5bb6ea562ee63ac3 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Wed, 15 Jul 2020 23:17:16 -0400 Subject: [PATCH 2/2] lint fixes --- src/index.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/index.js b/src/index.js index 92cc8d6..ac95369 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,3 @@ -import Interceptor from './interceptor'; - /** * Copyright 2018 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,6 +11,8 @@ import Interceptor from './interceptor'; * limitations under the License. */ +import Interceptor from './interceptor'; + /** * @public * @typedef Options @@ -159,20 +159,22 @@ export default (function create(/** @type {Options} */ defaults) { url = config.url; } - const response = /** @type {Response} */ ({ config }); + let response = /** @type {Response} */ ({ config }); /** @type {Options} */ - const options = deepMerge(defaults, config); - + let options = deepMerge(defaults, config); + if (_data) options.data = _data; - redaxios.interceptors.request.handlers.map(handler => { + redaxios.interceptors.request.handlers.map((handler) => { if (handler) { const resultConfig = handler.done(options); options = deepMerge(options, resultConfig || {}); } }); + let data = options.data; + /** @type {Headers} */ const customHeaders = {}; @@ -218,28 +220,27 @@ export default (function create(/** @type {Options} */ defaults) { const ok = options.validateStatus ? options.validateStatus(res.status) : res.ok; if (!ok) { - redaxios.interceptors.response.handlers.map(handler => { + redaxios.interceptors.response.handlers.map((handler) => { if (handler && handler.error) { handler.error(res); } }); - const error = Promise.reject(response); - redaxios.interceptors.request.handlers.map(handler => { + const error = Promise.reject(response); + redaxios.interceptors.request.handlers.map((handler) => { if (handler && handler.error) { handler.error(error); - } + } }); return error; } - const withData = options.responseType === 'stream' - ? Promise.resolve(res.body) - : res[options.responseType || 'text'](); + const withData = + options.responseType === 'stream' ? Promise.resolve(res.body) : res[options.responseType || 'text'](); return withData.then((data) => { response.data = data; - redaxios.interceptors.response.handlers.map(handler => { - response = handler && handler.done(response) || response; - }); + redaxios.interceptors.response.handlers.map((handler) => { + response = (handler && handler.done(response)) || response; + }); return response; }); });