From 70a6ca838914d55d2a3fd27e6cc72c0941673861 Mon Sep 17 00:00:00 2001 From: Shane Holloway Date: Fri, 12 May 2017 10:41:04 -0600 Subject: [PATCH 1/5] Added responseHandlers function registry and corresponding tests --- index.js | 17 +++++++++++---- test/tests.js | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 0776514..93e0618 100644 --- a/index.js +++ b/index.js @@ -11,10 +11,18 @@ return '?' + arr.join('&') } + var responseHandlers = { + 'json': function(response) { return response.json() }, + 'text': function(response) { return response.text() }, + 'response': true + } function _fetch (method, url, opts, data, queryParams) { opts.method = method opts.headers = opts.headers || {} - opts.responseAs = (opts.responseAs && ['json', 'text', 'response'].indexOf(opts.responseAs) >= 0) ? opts.responseAs : 'json' + + if (opts.responseAs !== true && typeof opts.responseAs !== 'function') { + opts.responseAs = responseHandlers[opts.responseAs] || responseHandlers.json + } defaults(opts.headers, { 'Accept': 'application/json', @@ -25,7 +33,7 @@ url += getQuery(queryParams) } - if (data) { + if (data && 'string' !== typeof data) { opts.body = JSON.stringify(data); } else { delete opts.body; @@ -34,11 +42,11 @@ return fetchival.fetch(url, opts) .then(function (response) { if (response.status >= 200 && response.status < 300) { - if(opts.responseAs=="response") + if(opts.responseAs===true) return response if (response.status == 204) return null; - return response[opts.responseAs](); + return opts.responseAs(response); } var err = new Error(response.statusText) err.response = response @@ -83,6 +91,7 @@ // Expose fetch so that other polyfills can be used // Bind fetch to window to avoid TypeError: Illegal invocation fetchival.fetch = typeof fetch !== 'undefined' ? fetch.bind(window) : null + fetchival.responseHandlers = responseHandlers // Support CommonJS, AMD & browser if (typeof exports === 'object') diff --git a/test/tests.js b/test/tests.js index dbbb6e7..482a8ac 100644 --- a/test/tests.js +++ b/test/tests.js @@ -17,6 +17,30 @@ var requestText = fetchival('http://jsonplaceholder.typicode.com', { responseAs: 'text' }) +fetchival.responseHandlers['registered-example'] = function(response) { + return response.json() + .then(function (json) { + return {kind: 'registered-example', json: json, response: response} + }) +} + +var requestRegisteredExample = fetchival('http://jsonplaceholder.typicode.com', { + mode: 'cors', + headers: { 'X-TEST': 'test' }, + responseAs: 'registered-example' +}) + +var requestCustomExample = fetchival('http://jsonplaceholder.typicode.com', { + mode: 'cors', + headers: { 'X-TEST': 'test' }, + responseAs: function(response) { + return response.json() + .then(function (json) { + return {kind: 'custom', json: json, response: response} + }) + } +}) + describe('fetchival', function () { this.timeout(5000) this.slow(5000) @@ -135,4 +159,37 @@ describe('fetchival', function () { }) }) }) + + describe('requestCustomExample(posts/1/comments)', function () { + var posts = requestCustomExample('posts') + var comments = posts(1 + '/comments') + + it('should #get()', function (done) { + comments + .get() + .then(function (extendedReponseObj) { + assert.equal('custom', extendedReponseObj.kind) + assert(extendedReponseObj.response.ok) + assert(extendedReponseObj.json.length) + done() + }) + }) + }) + + describe('requestRegisteredExample(posts/1/comments)', function () { + var posts = requestRegisteredExample('posts') + var comments = posts(1 + '/comments') + + it('should #get()', function (done) { + comments + .get() + .then(function (extendedReponseObj) { + assert.equal('registered-example', extendedReponseObj.kind) + assert(extendedReponseObj.response.ok) + assert(extendedReponseObj.json.length) + done() + }) + }) + }) + }) From 5f6e0fe0a63ddeb07daa305252b7c455e9b65e48 Mon Sep 17 00:00:00 2001 From: Shane Holloway Date: Fri, 12 May 2017 13:16:00 -0600 Subject: [PATCH 2/5] Fixed bug modifying opts and opts.headers across calls --- index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 93e0618..08ea1e1 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ function defaults (target, obj) { for (var prop in obj) target[prop] = target[prop] || obj[prop] + return target } function getQuery (queryParams) { @@ -17,8 +18,10 @@ 'response': true } function _fetch (method, url, opts, data, queryParams) { + // Use a shallow copy of opts parameter and opts.header subfield + opts = defaults({}, opts) opts.method = method - opts.headers = opts.headers || {} + opts.headers = defaults({}, opts.headers || {}) if (opts.responseAs !== true && typeof opts.responseAs !== 'function') { opts.responseAs = responseHandlers[opts.responseAs] || responseHandlers.json From f958d9fb7b8e88eb4ff8c0bc07f5fb030310a658 Mon Sep 17 00:00:00 2001 From: Shane Holloway Date: Fri, 12 May 2017 13:18:16 -0600 Subject: [PATCH 3/5] Added optional queryParams argument to post, put, patch, and delete methods --- index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 08ea1e1..b15e73e 100644 --- a/index.js +++ b/index.js @@ -72,20 +72,20 @@ return _fetch('GET', url, opts, null, queryParams) } - _.post = function (data) { - return _fetch('POST', url, opts, data) + _.post = function (data, queryParams) { + return _fetch('POST', url, opts, data, queryParams) } - _.put = function (data) { - return _fetch('PUT', url, opts, data) + _.put = function (data, queryParams) { + return _fetch('PUT', url, opts, data, queryParams) } - _.patch = function (data) { - return _fetch('PATCH', url, opts, data) + _.patch = function (data, queryParams) { + return _fetch('PATCH', url, opts, data, queryParams) } - _.delete = function () { - return _fetch('DELETE', url, opts) + _.delete = function (queryParams) { + return _fetch('DELETE', url, opts, null, queryParams) } return _ From 3dd0a16dd8477ffdc4b607001795c78032110fa7 Mon Sep 17 00:00:00 2001 From: Shane Holloway Date: Fri, 12 May 2017 13:22:27 -0600 Subject: [PATCH 4/5] Added encodeHandler function registry to allow for passing data as raw string, FormData, ArrayBuffer, ArrayBufferView, Blob, URLSearchParams --- index.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index b15e73e..0fa5cc5 100644 --- a/index.js +++ b/index.js @@ -12,11 +12,21 @@ return '?' + arr.join('&') } + function identity(arg) { return arg } + var responseHandlers = { 'json': function(response) { return response.json() }, 'text': function(response) { return response.text() }, 'response': true } + + var encodeHandlers = { + 'json': JSON.stringify, + 'noop': identity, + true: identity, + false: identity, + } + function _fetch (method, url, opts, data, queryParams) { // Use a shallow copy of opts parameter and opts.header subfield opts = defaults({}, opts) @@ -36,10 +46,11 @@ url += getQuery(queryParams) } - if (data && 'string' !== typeof data) { - opts.body = JSON.stringify(data); - } else { - delete opts.body; + if (data) { + if (typeof opts.encodeAs !== 'function') { + opts.encodeAs = encodeHandlers[opts.encodeAs] || encodeHandlers.json + } + opts.body = opts.encodeAs(data) } return fetchival.fetch(url, opts) @@ -95,6 +106,7 @@ // Bind fetch to window to avoid TypeError: Illegal invocation fetchival.fetch = typeof fetch !== 'undefined' ? fetch.bind(window) : null fetchival.responseHandlers = responseHandlers + fetchival.encodeHandlers = encodeHandlers // Support CommonJS, AMD & browser if (typeof exports === 'object') From 4e588e5c5a847b42ed6d2dc01ff4c0fd7965f8df Mon Sep 17 00:00:00 2001 From: Shane Holloway Date: Fri, 12 May 2017 13:35:09 -0600 Subject: [PATCH 5/5] Added errorAs function option to customize error handling --- index.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 0fa5cc5..966a5e5 100644 --- a/index.js +++ b/index.js @@ -62,9 +62,13 @@ return null; return opts.responseAs(response); } - var err = new Error(response.statusText) - err.response = response - throw err + if(typeof opts.errorAs !== 'function') { + var err = new Error(response.statusText) + err.response = response + throw err + } else { + throw opts.errorAs(response) + } }) }