From 2bfd0976fb4aac49f388a97edaa8297254072728 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Fri, 30 Aug 2019 19:22:54 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + README.md | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ example-1.js | 13 +++++++ example-2.js | 16 ++++++++ example-3.js | 16 ++++++++ index.js | 35 ++++++++++++++++++ package.json | 17 +++++++++ yarn.lock | 57 +++++++++++++++++++++++++++++ 8 files changed, 256 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 example-1.js create mode 100644 example-2.js create mode 100644 example-3.js create mode 100644 index.js create mode 100644 package.json create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..93b6011 --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# axios-get-json-response + +A simple utility for checking status codes and parsing JSON data from Axios responses. While Axios *can* do this by itself, it doesn't document any distinguishable error types, and so when an error occurs you can never know *what* went wrong. + +This library fixes that, by giving you two distinct error types. Hopefully this functionality eventually makes it into Axios itself. + +Both error types correctly inherit from Error, and therefore a) have stacktraces, and b) can be used with `instanceof` as well as things based on it, like Bluebird's [filtered `.catch`](http://bluebirdjs.com/docs/api/catch.html#filtered-catch). + +## Examples + +An existent and valid URL: + +```js +"use strict"; + +const axios = require("axios"); +const Promise = require("bluebird"); +const getJson = require("axios-get-json-response"); + +Promise.try(() => { + return axios.get("http://cryto.net/test.json", getJson.axiosConfiguration); // {"hello": "world"} +}).then((response) => { + let parsedJson = getJson.parse(response); + + console.log(parsedJson); // { hello: 'world' } +}); +``` + +Using `axios.create` to set the custom configuration as a default, and showing the result for an existent URL that is *not* valid JSON: + +```js +"use strict"; + +const axios = require("axios"); +const Promise = require("bluebird"); +const getJson = require("axios-get-json-response"); + +let manualAxios = axios.create(getJson.axiosConfiguration); + +Promise.try(() => { + return manualAxios.get("http://cryto.net/invalid.json"); // {"hello": "world" +}).then((response) => { + let parsedJson = getJson.parse(response); + /* throws: ParsingFailed: Could not parse response body as JSON */ + + console.log(parsedJson); +}); +``` + +The result for a non-existent URL: + +```js +"use strict"; + +const axios = require("axios"); +const Promise = require("bluebird"); +const getJson = require("axios-get-json-response"); + +let manualAxios = axios.create(getJson.axiosConfiguration); + +Promise.try(() => { + return manualAxios.get("http://cryto.net/non-existent.json"); // URL does not exist +}).then((response) => { + let parsedJson = getJson.parse(response); + /* throws: BadResponseCode: Got an unexpected HTTP status code (404) */ + + console.log(parsedJson); +}); +``` + +## API + +### getJson.axiosConfiguration + +Some preset configuration options, that disable response code checking and body parsing in Axios. Needed for this library to work. These options are fed directly into Axios; see the example. + +### getJson.parse(response, [options]) + +Validates the status code and parses the response body. Returns the parsed JSON if successful, or throws either a `BadStatusCode` or `ParsingFailed` error depending on what went wrong (see below). + +- __response:__ The Axios response object to handle. +- __options:__ *Optional.* + - __validateStatus:__ *Optional.* A custom status code validation function, like in Axios. Defaults to the same as in Axios (all 2XX are considered valid). Expected to return `true` for a valid/expected status code, and `false` for an unexpected one. + +### getJson.BadStatusCode + +An error type that signifies that an unexpected status code was received. + +Extra properties on this error object: + +- __statusCode:__ The received status code. + +### getJson.ParsingFailed + +An error type that signifies that the response body could not be parsed as JSON. + +## Changelog + +### 1.0.0 (August 30, 2019) + +Initial release. diff --git a/example-1.js b/example-1.js new file mode 100644 index 0000000..f2371e1 --- /dev/null +++ b/example-1.js @@ -0,0 +1,13 @@ +"use strict"; + +const axios = require("axios"); +const Promise = require("bluebird"); +const getJson = require("."); + +Promise.try(() => { + return axios.get("http://cryto.net/test.json", getJson.axiosConfiguration); // {"hello": "world"} +}).then((response) => { + let parsedJson = getJson.parse(response); + + console.log(parsedJson); // { hello: 'world' } +}); diff --git a/example-2.js b/example-2.js new file mode 100644 index 0000000..e3c3c0d --- /dev/null +++ b/example-2.js @@ -0,0 +1,16 @@ +"use strict"; + +const axios = require("axios"); +const Promise = require("bluebird"); +const getJson = require("."); + +let manualAxios = axios.create(getJson.axiosConfiguration); + +Promise.try(() => { + return manualAxios.get("http://cryto.net/invalid.json"); // {"hello": "world" +}).then((response) => { + let parsedJson = getJson.parse(response); + /* throws: ParsingFailed: Could not parse response body as JSON */ + + console.log(parsedJson); +}); diff --git a/example-3.js b/example-3.js new file mode 100644 index 0000000..052b0e0 --- /dev/null +++ b/example-3.js @@ -0,0 +1,16 @@ +"use strict"; + +const axios = require("axios"); +const Promise = require("bluebird"); +const getJson = require("."); + +let manualAxios = axios.create(getJson.axiosConfiguration); + +Promise.try(() => { + return manualAxios.get("http://cryto.net/non-existent.json"); // URL does not exist +}).then((response) => { + let parsedJson = getJson.parse(response); + /* throws: BadResponseCode: Got an unexpected HTTP status code (404) */ + + console.log(parsedJson); +}); diff --git a/index.js b/index.js new file mode 100644 index 0000000..9a61f5f --- /dev/null +++ b/index.js @@ -0,0 +1,35 @@ +"use strict"; + +const defaultValue = require("default-value"); +const createError = require("create-error"); + +let BadStatusCode = createError("BadResponseCode"); +let ParsingFailed = createError("ParsingFailed"); + +module.exports = { + BadStatusCode, + ParsingFailed, + axiosConfiguration: { + /* We do the status check manually in the response-handling code, so that we can identify it as a specific type of error. */ + validateStatus: () => true, + /* Likewise, we manually attempt to parse the response body. */ + responseType: "text", + /* The `responseType` setting above is currently (erroneously?) being ignored by Axios, and it tries to parse JSON anyway, which gives us an inconsistent result. This happens through the default `transformResponse` handler, so we remove that behaviour here by overriding it. */ + transformResponse: (data) => data + }, + parse: function parseJsonResponse(response, options = {}) { + let validateStatus = defaultValue(options.validateStatus, (status) => status >= 200 && status < 300); + + if (validateStatus(response.status)) { + console.log(response.data); + + try { + return JSON.parse(response.data); + } catch (error) { + throw new ParsingFailed("Could not parse response body as JSON"); + } + } else { + throw new BadStatusCode(`Got an unexpected HTTP status code (${response.status})`, { statusCode: response.status }); + } + } +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..fe4962c --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "axios-get-json-response", + "description": "A small utility for validating an axios response and parsing it as JSON, *with* distinguishable error types", + "version": "1.0.0", + "main": "index.js", + "repository": "http://git.cryto.net/joepie91/axios-get-json-response.git", + "author": "Sven Slootweg ", + "license": "WTFPL OR CC0-1.0", + "dependencies": { + "create-error": "^0.3.1", + "default-value": "^1.0.0" + }, + "devDependencies": { + "axios": "^0.19.0", + "bluebird": "^3.5.5" + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..0d169c0 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,57 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +axios@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" + integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + +bluebird@^3.5.5: + version "3.5.5" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" + integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== + +create-error@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/create-error/-/create-error-0.3.1.tgz#69810245a629e654432bf04377360003a5351a23" + integrity sha1-aYECRaYp5lRDK/BDdzYAA6U1GiM= + +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +default-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-value/-/default-value-1.0.0.tgz#8c6f52a5a1193fe78fdc9f86eb71d16c9757c83a" + integrity sha1-jG9SpaEZP+eP3J+G63HRbJdXyDo= + dependencies: + es6-promise-try "0.0.1" + +es6-promise-try@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/es6-promise-try/-/es6-promise-try-0.0.1.tgz#10f140dad27459cef949973e5d21a087f7274b20" + integrity sha1-EPFA2tJ0Wc75SZc+XSGgh/cnSyA= + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +is-buffer@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=