diff --git a/package-lock.json b/package-lock.json index 87061a3..021b97f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@luckbox/http-adapter-factory", - "version": "1.0.2", + "version": "1.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 729fc6c..a09369f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@luckbox/http-adapter-factory", - "version": "1.0.2", + "version": "1.1.0", "description": "Easy to use http adapter factory with support for GET/POST.", "author": "Luckbox", "main": "dist/index.js", diff --git a/src/GotHttpAdapter.ts b/src/GotHttpAdapter.ts index 449c100..d81c49b 100644 --- a/src/GotHttpAdapter.ts +++ b/src/GotHttpAdapter.ts @@ -3,6 +3,7 @@ import got, { RequestError, OptionsOfUnknownResponseBody, Response as GotResponse, + ParseError as GotParseError, } from 'got'; import * as qs from 'qs'; @@ -23,6 +24,7 @@ import HttpAdapter, { DEFAULTS, } from './HttpAdapter'; import { IncomingHttpHeaders } from 'http'; +import ParseError from './errors/ParseError'; class GotHttpAdapter implements HttpAdapter { private readonly timeout: number; @@ -126,6 +128,13 @@ class GotHttpAdapter implements HttpAdapter { }); } + if (err instanceof GotParseError) { + throw new ParseError({ + message: err.message.split(/\sin\s+"?https?:\/{2}/i, 1)[0], + responseBody: err.response.rawBody.toString(), + }); + } + if (err instanceof RequestError && err.name === 'RequestError') { throw new HttpRequestError({ message: err.message, diff --git a/src/errors/ParseError.ts b/src/errors/ParseError.ts new file mode 100644 index 0000000..bcee5d7 --- /dev/null +++ b/src/errors/ParseError.ts @@ -0,0 +1,17 @@ +type ConstructorParams = { + message: string; + responseBody: string; +} + +class ParseError extends Error { + public readonly responseBody: string; + + constructor(params: ConstructorParams) { + super(params.message); + + this.name = 'ParseError'; + this.responseBody = params.responseBody; + } +} + +export default ParseError; diff --git a/src/tests/GotHttpAdapter.test.ts b/src/tests/GotHttpAdapter.test.ts index 9c44a11..7b2847c 100644 --- a/src/tests/GotHttpAdapter.test.ts +++ b/src/tests/GotHttpAdapter.test.ts @@ -1,4 +1,5 @@ -import got, { HTTPError, ParseError, RequestError } from 'got'; +import got, { HTTPError, ParseError as GotParseError, RequestError } from 'got'; +import ParseError from '../errors/ParseError'; import HttpGenericError from '../errors/HttpGenericError'; import HttpRequestError from '../errors/HttpRequestError'; import HttpStatusCodeError from '../errors/HttpStatusCodeError'; @@ -176,10 +177,10 @@ describe('GotHttpAdapter', () => { }); it('should throw HttpGenericError when got throws subclass of RequestError', async () => { - const parseError = produceFoolInstance(ParseError); - parseError.message = 'Unexpected token < at position...'; - parseError.name = 'ParseError'; - mockGot.get.mockRejectedValueOnce(parseError); + const timeoutError = produceFoolInstance(RequestError); + timeoutError.message = 'Connection has timed out'; + timeoutError.name = 'TimeoutError'; + mockGot.get.mockRejectedValueOnce(timeoutError); let caughtErr; const url = 'http://example.com'; @@ -190,7 +191,30 @@ describe('GotHttpAdapter', () => { } expect(caughtErr).toBeInstanceOf(HttpGenericError); - expect(caughtErr.originalError).toEqual(parseError); + expect(caughtErr.originalError).toEqual(timeoutError); + }); + + it('should throw ParseError when got throws ParseError', async () => { + const parseError = produceFoolInstance(GotParseError, { + message: 'Unexpected token < at position 10 in "http://example.com"', + name: 'ParseError', + response: { + rawBody: Buffer.from('Invalid JSON'), + }, + }); + + mockGot.post.mockRejectedValueOnce(parseError); + + let caughtErr; + + try { + await httpAdapter.post('http://example.com'); + } catch (err) { + caughtErr = err; + } + + expect(caughtErr).toBeInstanceOf(ParseError); + expect(caughtErr.message).toEqual('Unexpected token < at position 10'); }); }); @@ -336,11 +360,11 @@ describe('GotHttpAdapter', () => { }); it('should throw HttpGenericError when got throws subclass of RequestError', async () => { - const parseError = produceFoolInstance(ParseError, { - message: 'Unexpected token < at position...', - name: 'ParseError', + const unexpectedCloseError = produceFoolInstance(RequestError, { + message: 'Connection has been closed unexpectedly', + name: 'UnexpectedCloseError', }); - mockGot.post.mockRejectedValueOnce(parseError); + mockGot.post.mockRejectedValueOnce(unexpectedCloseError); let caughtErr; const url = 'http://example.com'; @@ -351,7 +375,30 @@ describe('GotHttpAdapter', () => { } expect(caughtErr).toBeInstanceOf(HttpGenericError); - expect(caughtErr.originalError).toEqual(parseError); + expect(caughtErr.originalError).toEqual(unexpectedCloseError); + }); + + it('should throw ParseError when got throws ParseError', async () => { + const parseError = produceFoolInstance(GotParseError, { + message: 'Unexpected token < at position 10 in "http://example.com"', + name: 'ParseError', + response: { + rawBody: Buffer.from('Invalid JSON'), + }, + }); + + mockGot.post.mockRejectedValueOnce(parseError); + + let caughtErr; + + try { + await httpAdapter.post('http://example.com'); + } catch (err) { + caughtErr = err; + } + + expect(caughtErr).toBeInstanceOf(ParseError); + expect(caughtErr.message).toEqual('Unexpected token < at position 10'); }); }); });