From 3251051f92654a5c489774606c88fa81a1e8136c Mon Sep 17 00:00:00 2001 From: Jae Bradley Date: Thu, 10 Sep 2020 21:41:52 -0400 Subject: [PATCH] Add more context to 403 response errors --- src/__specs__/responseStatusMessage.spec.js | 56 +++++++++++++++++++++ src/formatError.js | 9 +++- src/responseStatusMessage.js | 28 +++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/__specs__/responseStatusMessage.spec.js create mode 100644 src/responseStatusMessage.js diff --git a/src/__specs__/responseStatusMessage.spec.js b/src/__specs__/responseStatusMessage.spec.js new file mode 100644 index 0000000..2cb4070 --- /dev/null +++ b/src/__specs__/responseStatusMessage.spec.js @@ -0,0 +1,56 @@ +import ResponseStatusMessage from '../responseStatusMessage'; + +describe('ResponseStatusMessage', () => { + describe('#hasAdvice', () => { + describe('when status code is 403', () => { + it('is true', () => { + // eslint-disable-next-line no-unused-expressions + expect(new ResponseStatusMessage({ code: 403, text: 'Forbidden' }).hasAdvice).to.be.true; + }); + }); + + describe('when status code is not 403', () => { + describe('when status code is 200', () => { + it('is false', () => { + // eslint-disable-next-line no-unused-expressions + expect(new ResponseStatusMessage({ code: 200, text: 'Success' }).hasAdvice).to.be.false; + }); + }); + }); + }); + + describe('#advice', () => { + describe('when status code is 403', () => { + it('returns advice for recovering from a Forbidden error', () => { + expect(new ResponseStatusMessage({ code: 403, text: 'Forbidden' }).advice). + to.equal('Was a valid API key provided for an organization & application you have access to?'); + }); + }); + + describe('when status code is not 403', () => { + describe('when status code is 200', () => { + it('returns undefined', () => { + // eslint-disable-next-line no-unused-expressions + expect(new ResponseStatusMessage({ code: 200, text: 'Success' }).advice).to.be.undefined; + }); + }); + }); + }); + + describe('#toString', () => { + describe('when status code is 403', () => { + it('returns advice for recovering from a Forbidden error', () => { + expect(new ResponseStatusMessage({ code: 403, text: 'Forbidden' }).toString()). + to.equal('403 Forbidden - Was a valid API key provided for an organization & application you have access to?'); + }); + }); + + describe('when status code is not 403', () => { + describe('when status code is 200', () => { + it('returns status code and status text', () => { + expect(new ResponseStatusMessage({ code: 200, text: 'Success' }).toString()).to.equal('200 Success'); + }); + }); + }); + }); +}); diff --git a/src/formatError.js b/src/formatError.js index 445acac..15e42ce 100644 --- a/src/formatError.js +++ b/src/formatError.js @@ -1,9 +1,16 @@ +import ResponseStatusMessage from './responseStatusMessage'; + export default async function formatError(res, { verbose = false } = {}) { if (res.status < 300) { return; } - console.error(`${res.status} ${res.statusText}`); + console.error( + new ResponseStatusMessage({ + code: res.status, + text: res.statusText, + }).toString() + ); const body = await res.text(); diff --git a/src/responseStatusMessage.js b/src/responseStatusMessage.js new file mode 100644 index 0000000..839f6ee --- /dev/null +++ b/src/responseStatusMessage.js @@ -0,0 +1,28 @@ +const RESPONSE_STATUS_CODES_TO_ADVICE = Object.freeze({ + 403: 'Was a valid API key provided for an organization & application you have access to?', +}); + +class ResponseStatusMessage { + constructor({ code, text }) { + this.code = code; + this.text = text; + } + + get advice() { + return RESPONSE_STATUS_CODES_TO_ADVICE[this.code]; + } + + get hasAdvice() { + return this.code in RESPONSE_STATUS_CODES_TO_ADVICE; + } + + toString() { + const formattedMessage = `${this.code} ${this.text}`; + if (this.hasAdvice) { + return `${formattedMessage} - ${this.advice}`; + } + return formattedMessage; + } +} + +export default ResponseStatusMessage;