diff --git a/lib/error-helper.js b/lib/error-helper.js index 71aee42843607a351abb22e49cb97dc801084952..9e7fee8a3a76374eb769341d99b67d3343e704ab 100644 --- a/lib/error-helper.js +++ b/lib/error-helper.js @@ -5,19 +5,18 @@ const { InvalidResponse, Inaccessible, } = require('../services/errors') +const defaultErrorMessages = { + 404: 'not found', +} -const checkErrorResponse = function( - badgeData, - err, - res, - notFoundMessage = 'not found' -) { +const checkErrorResponse = function(badgeData, err, res, errorMessages = {}) { + errorMessages = { ...defaultErrorMessages, ...errorMessages } if (err != null) { badgeData.text[1] = 'inaccessible' badgeData.colorscheme = 'red' return true - } else if (res.statusCode === 404) { - badgeData.text[1] = notFoundMessage + } else if (errorMessages[res.statusCode] !== undefined) { + badgeData.text[1] = errorMessages[res.statusCode] badgeData.colorscheme = 'lightgrey' return true } else if (res.statusCode !== 200) { @@ -29,17 +28,23 @@ const checkErrorResponse = function( } } -checkErrorResponse.asPromise = function({ notFoundMessage } = {}) { +checkErrorResponse.asPromise = function(errorMessages = {}) { return async function({ buffer, res }) { - const underlyingError = Error( - `Got status code ${res.statusCode} (expected 200)` - ) + errorMessages = { ...defaultErrorMessages, ...errorMessages } if (res.statusCode === 404) { - throw new NotFound({ prettyMessage: notFoundMessage }) - } else if (res.statusCode >= 500) { - throw new Inaccessible({ underlyingError }) + throw new NotFound({ prettyMessage: errorMessages[404] }) } else if (res.statusCode !== 200) { - throw new InvalidResponse({ underlyingError }) + const underlying = Error( + `Got status code ${res.statusCode} (expected 200)` + ) + const props = { underlyingError: underlying } + if (errorMessages[res.statusCode] !== undefined) { + props.prettyMessage = errorMessages[res.statusCode] + } + if (res.statusCode >= 500) { + throw new Inaccessible(props) + } + throw new InvalidResponse(props) } return { buffer, res } } diff --git a/lib/error-helper.spec.js b/lib/error-helper.spec.js index 92f33cef8306202fe6cc2ff81e76206d74cb2d08..d9b91c4d8c0b6819e38e190b856d0b4984e8aa26 100644 --- a/lib/error-helper.spec.js +++ b/lib/error-helper.spec.js @@ -1,7 +1,6 @@ 'use strict' -const chai = require('chai') -const { assert, expect } = chai +const { expect } = require('chai') const { checkErrorResponse } = require('./error-helper') const { NotFound, @@ -9,48 +8,46 @@ const { Inaccessible, } = require('../services/errors') -chai.use(require('chai-as-promised')) - describe('Standard Error Handler', function() { it('makes inaccessible badge', function() { const badgeData = { text: [] } - assert.equal( - true, - checkErrorResponse(badgeData, 'something other than null', {}) - ) - assert.equal('inaccessible', badgeData.text[1]) - assert.equal('red', badgeData.colorscheme) + expect(checkErrorResponse(badgeData, 'something other than null', {})).to.be + .true + expect(badgeData.text[1]).to.equal('inaccessible') + expect(badgeData.colorscheme).to.equal('red') }) it('makes not found badge', function() { const badgeData = { text: [] } - assert.equal(true, checkErrorResponse(badgeData, null, { statusCode: 404 })) - assert.equal('not found', badgeData.text[1]) - assert.equal('lightgrey', badgeData.colorscheme) + expect(checkErrorResponse(badgeData, null, { statusCode: 404 })).to.be.true + expect(badgeData.text[1]).to.equal('not found') + expect(badgeData.colorscheme).to.equal('lightgrey') }) it('makes not found badge with custom error', function() { const badgeData = { text: [] } - assert.equal( - true, - checkErrorResponse(badgeData, null, { statusCode: 404 }, 'custom message') - ) - assert.equal('custom message', badgeData.text[1]) - assert.equal('lightgrey', badgeData.colorscheme) + expect( + checkErrorResponse( + badgeData, + null, + { statusCode: 404 }, + { 404: 'custom message' } + ) + ).to.be.true + expect(badgeData.text[1]).to.equal('custom message') + expect(badgeData.colorscheme).to.equal('lightgrey') }) it('makes invalid badge', function() { const badgeData = { text: [] } - assert.equal(true, checkErrorResponse(badgeData, null, { statusCode: 500 })) - assert.equal('invalid', badgeData.text[1]) - assert.equal('lightgrey', badgeData.colorscheme) + expect(checkErrorResponse(badgeData, null, { statusCode: 500 })).to.be.true + expect(badgeData.text[1]).to.equal('invalid') + expect(badgeData.colorscheme).to.equal('lightgrey') }) it('return false on 200 status', function() { - assert.equal( - false, - checkErrorResponse({ text: [] }, null, { statusCode: 200 }) - ) + expect(checkErrorResponse({ text: [] }, null, { statusCode: 200 })).to.be + .false }) }) @@ -78,9 +75,8 @@ describe('async error handler', function() { it('displays the custom not found message', async function() { const notFoundMessage = 'no goblins found' - const res = { statusCode: 404 } try { - await checkErrorResponse.asPromise({ notFoundMessage })({ res }) + await checkErrorResponse.asPromise({ 404: notFoundMessage })({ res }) expect.fail('Expected to throw') } catch (e) { expect(e).to.be.an.instanceof(NotFound) @@ -91,9 +87,8 @@ describe('async error handler', function() { }) context('when status is 4xx', function() { - const res = { statusCode: 499 } - it('throws InvalidResponse', async function() { + const res = { statusCode: 499 } try { await checkErrorResponse.asPromise()({ res }) expect.fail('Expected to throw') @@ -105,12 +100,27 @@ describe('async error handler', function() { expect(e.prettyMessage).to.equal('invalid') } }) + + it('displays the custom error message', async function() { + const res = { statusCode: 403 } + try { + await checkErrorResponse.asPromise({ + 403: 'access denied', + })({ res }) + expect.fail('Expected to throw') + } catch (e) { + expect(e).to.be.an.instanceof(InvalidResponse) + expect(e.message).to.equal( + 'Invalid Response: Got status code 403 (expected 200)' + ) + expect(e.prettyMessage).to.equal('access denied') + } + }) }) context('when status is 5xx', function() { - const res = { statusCode: 503 } - it('throws Inaccessible', async function() { + const res = { statusCode: 503 } try { await checkErrorResponse.asPromise()({ res }) expect.fail('Expected to throw') @@ -122,5 +132,21 @@ describe('async error handler', function() { expect(e.prettyMessage).to.equal('inaccessible') } }) + + it('displays the custom error message', async function() { + const res = { statusCode: 500 } + try { + await checkErrorResponse.asPromise({ + 500: 'server overloaded', + })({ res }) + expect.fail('Expected to throw') + } catch (e) { + expect(e).to.be.an.instanceof(Inaccessible) + expect(e.message).to.equal( + 'Inaccessible: Got status code 500 (expected 200)' + ) + expect(e.prettyMessage).to.equal('server overloaded') + } + }) }) }) diff --git a/lib/github-helpers.js b/lib/github-helpers.js index aef5a6cb3a5e8e991d09a630c39f8258f470df59..a698a2d68c2e517e57302d6f0a82a78267451233 100644 --- a/lib/github-helpers.js +++ b/lib/github-helpers.js @@ -22,14 +22,14 @@ function checkErrorResponse( badgeData, err, res, - notFoundMessage = 'repo not found' + notFoundMessage = 'repo not found', + errorMessages = {} ) { - if (res && res.statusCode === 422) { - badgeData.text[1] = notFoundMessage - badgeData.colorscheme = 'lightgrey' - return true - } - return standardCheckErrorResponse(badgeData, err, res, notFoundMessage) + return standardCheckErrorResponse(badgeData, err, res, { + ...errorMessages, + 404: notFoundMessage, + 422: notFoundMessage, + }) } const commentsColor = colorScale([1, 3, 10, 25], undefined, true) diff --git a/lib/github-helpers.spec.js b/lib/github-helpers.spec.js index 079410ba41d098eb75a95d7816590d295fe4e39d..7e97504461cc0ea29dd54fb459f57111915a845e 100644 --- a/lib/github-helpers.spec.js +++ b/lib/github-helpers.spec.js @@ -4,12 +4,34 @@ const { expect } = require('chai') const { checkErrorResponse } = require('./github-helpers') describe('GitHub Error Handler', function() { - it('makes not found badge when 422 is returned', function() { + it('makes repo not found badge when 422 is returned', function() { + const badgeData = { text: [] } + expect(checkErrorResponse(badgeData, null, { statusCode: 422 })).to.be.true + expect(badgeData.text[1]).to.equal('repo not found') + expect(badgeData.colorscheme).to.equal('lightgrey') + }) + + it('makes user not found badge when 404 is returned', function() { const badgeData = { text: [] } expect( - checkErrorResponse(badgeData, null, { statusCode: 422 }, 'repo not found') + checkErrorResponse(badgeData, null, { statusCode: 404 }, 'user not found') ).to.be.true - expect(badgeData.text[1]).to.equal('repo not found') + expect(badgeData.text[1]).to.equal('user not found') + expect(badgeData.colorscheme).to.equal('lightgrey') + }) + + it('makes badge with custom message when specified', function() { + const badgeData = { text: [] } + expect( + checkErrorResponse( + badgeData, + null, + { statusCode: 418 }, + 'repo not found', + { 418: "I'm a teapot" } + ) + ).to.be.true + expect(badgeData.text[1]).to.equal("I'm a teapot") expect(badgeData.colorscheme).to.equal('lightgrey') }) }) diff --git a/server.js b/server.js index c398385fb60c8c81e37c2bd71ca60d6bdb2e38b3..97bd2ae30e45af23389a2366b5c4664095fa69df 100644 --- a/server.js +++ b/server.js @@ -1061,7 +1061,7 @@ cache(function(data, match, sendBadge, request) { const url = 'https://lgtm.com/api/v0.1/project/' + projectId + '/details'; const badgeData = getBadgeData('lgtm', data); request(url, function(err, res, buffer) { - if (checkErrorResponse(badgeData, err, res, 'project not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'project not found' })) { sendBadge(format, badgeData); return; } @@ -1107,7 +1107,7 @@ cache(function(data, match, sendBadge, request) { })(); const badgeData = getBadgeData('code quality: ' + languageLabel, data); request(url, function(err, res, buffer) { - if (checkErrorResponse(badgeData, err, res, 'project not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'project not found' })) { sendBadge(format, badgeData); return; } @@ -1337,7 +1337,7 @@ cache(function(data, match, sendBadge, request) { branch = 'dev-master'; } request(apiUrl, function dealWithData(err, res, buffer) { - if (checkErrorResponse(badgeData, err, res, 'repo not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) { sendBadge(format, badgeData); return; } @@ -2360,7 +2360,7 @@ cache(function(data, match, sendBadge, request) { const apiUrl = `https://scrutinizer-ci.com/api/repositories/${repo}`; const badgeData = getBadgeData(type, data); request(apiUrl, {}, function(err, res, buffer) { - if (checkErrorResponse(badgeData, err, res, 'project or branch not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'project or branch not found' })) { sendBadge(format, badgeData); return; } @@ -3596,7 +3596,7 @@ cache(function(data, match, sendBadge, request) { badgeData.logo = getLogo('github', data); } githubApiProvider.request(request, apiUrl, {}, (err, res, buffer) => { - if (githubCheckErrorResponse(badgeData, err, res)) { + if (githubCheckErrorResponse(badgeData, err, res, 'repo not found', { 403: 'access denied' })) { sendBadge(format, badgeData); return; } @@ -5309,7 +5309,7 @@ cache(function(data, match, sendBadge, request) { const badgeData = getBadgeData('build', data); request(apiUrl, { json:true }, function(err, res, data) { - if (checkErrorResponse(badgeData, err, res, 'project not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'project not found' })) { sendBadge(format, badgeData); return; } @@ -5594,7 +5594,7 @@ cache(function(data, match, sendBadge, request) { var url = 'https://hub.docker.com/v2/repositories/' + path + '/stars/count/'; var badgeData = getBadgeData('docker stars', data); request(url, function(err, res, buffer) { - if (checkErrorResponse(badgeData, err, res, 'repo not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) { sendBadge(format, badgeData); return; } @@ -5626,7 +5626,7 @@ cache(function(data, match, sendBadge, request) { var url = 'https://hub.docker.com/v2/repositories/' + path; var badgeData = getBadgeData('docker pulls', data); request(url, function(err, res, buffer) { - if (checkErrorResponse(badgeData, err, res, 'repo not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) { sendBadge(format, badgeData); return; } @@ -5695,7 +5695,7 @@ cache(function(data, match, sendBadge, request) { var url = 'https://registry.hub.docker.com/v2/repositories/' + path; var badgeData = getBadgeData('docker build', data); request(url, function(err, res, buffer) { - if (checkErrorResponse(badgeData, err, res, 'repo not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) { sendBadge(format, badgeData); return; } @@ -5730,7 +5730,7 @@ cache(function(data, match, sendBadge, request) { var url = 'https://registry.hub.docker.com/v2/repositories/' + path + '/buildhistory'; var badgeData = getBadgeData('docker build', data); request(url, function(err, res, buffer) { - if (checkErrorResponse(badgeData, err, res, 'repo not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'repo not found' })) { sendBadge(format, badgeData); return; } @@ -6540,7 +6540,7 @@ cache(function(data, match, sendBadge, request) { const badgeData = getBadgeData('dependencies', data); request(options, function(err, res, json) { - if (checkErrorResponse(badgeData, err, res, 'not available')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'not available' })) { sendBadge(format, badgeData); return; } @@ -6814,7 +6814,7 @@ cache({ request(url, requestOptions, (err, res, data) => { try { - if (checkErrorResponse(badgeData, err, res, 'resource not found')) { + if (checkErrorResponse(badgeData, err, res, { 404: 'resource not found' })) { return; } diff --git a/services/apm/apm.service.js b/services/apm/apm.service.js index 277478ac5a448b46853caf6c12e72a53c73c3523..db8afe7b8e1c77eba9d55c399f55d0b739747bc6 100644 --- a/services/apm/apm.service.js +++ b/services/apm/apm.service.js @@ -22,7 +22,7 @@ class BaseAPMService extends BaseJsonService { return this._requestJson({ schema: apmSchema, url: `https://atom.io/api/packages/${repo}`, - notFoundMessage: 'package not found', + errorMessages: { 404: 'package not found' }, }) } diff --git a/services/appveyor/appveyor.service.js b/services/appveyor/appveyor.service.js index 26d029ce23994f9019b5cc40a6b20289b0cd2b20..6490d53e9262772c91c81cb694ca115562fb63fd 100644 --- a/services/appveyor/appveyor.service.js +++ b/services/appveyor/appveyor.service.js @@ -18,7 +18,7 @@ module.exports = class AppVeyor extends BaseJsonService { return this._requestJson({ schema: appVeyorSchema, url, - notFoundMessage: 'project not found or access denied', + errorMessages: { 404: 'project not found or access denied' }, }) } diff --git a/services/base-json.js b/services/base-json.js index 5584531b50e05463a5db2df445ac6493f6b4d0a6..842b1f2c864a3b326c08f2ecdee21515df400208 100644 --- a/services/base-json.js +++ b/services/base-json.js @@ -30,7 +30,7 @@ class BaseJsonService extends BaseService { } } - async _requestJson({ schema, url, options = {}, notFoundMessage }) { + async _requestJson({ schema, url, options = {}, errorMessages = {} }) { const logTrace = (...args) => this.constructor.logTrace('fetch', ...args) if (!schema || !schema.isJoi) { throw Error('A Joi schema is required') @@ -45,11 +45,7 @@ class BaseJsonService extends BaseService { logTrace(emojic.dart, 'Response status code', res.statusCode) return { res, buffer } }) - .then( - checkErrorResponse.asPromise( - notFoundMessage ? { notFoundMessage: notFoundMessage } : undefined - ) - ) + .then(checkErrorResponse.asPromise(errorMessages)) .then(asJson) .then(json => { logTrace(emojic.dart, 'Response JSON (before validation)', json) diff --git a/services/github/github.tester.js b/services/github/github.tester.js index 24db265b50292782b070642542b0182985004465..7a8fb8485d02959f580e3cb0094bd0587bfec870 100644 --- a/services/github/github.tester.js +++ b/services/github/github.tester.js @@ -99,7 +99,11 @@ t.create('License - API rate limit exceeded') documentation_url: 'https://developer.github.com/v3/#rate-limiting', }) ) - .expectJSON({ name: 'license', value: 'invalid', colorB: colorsB.lightgrey }) + .expectJSON({ + name: 'license', + value: 'access denied', + colorB: colorsB.lightgrey, + }) t.create('Contributors') .get('/contributors/cdnjs/cdnjs.json') diff --git a/services/npm/npm-base.js b/services/npm/npm-base.js index 5e27fcb84e8f70053636ee59c110ab80dd5907ad..ac0de7d73de71c5b7e6c3306139a1876622e11a4 100644 --- a/services/npm/npm-base.js +++ b/services/npm/npm-base.js @@ -82,7 +82,7 @@ module.exports = class NpmBase extends BaseJsonService { // Use a custom Accept header because of this bug: // <https://github.com/npm/npmjs.org/issues/163> options: { Accept: '*/*' }, - notFoundMessage: 'package not found', + errorMessages: { 404: 'package not found' }, }) let packageData diff --git a/services/npm/npm-downloads.service.js b/services/npm/npm-downloads.service.js index 401a693db5dd97406c8764a7b03af6e21681ba92..2855781ea3642bb3af5cd6d57e6ce13f58d74d89 100644 --- a/services/npm/npm-downloads.service.js +++ b/services/npm/npm-downloads.service.js @@ -79,7 +79,7 @@ function DownloadsForInterval(interval) { let { downloads } = await this._requestJson({ schema, url: `https://api.npmjs.org/downloads/${query}/${packageName}`, - notFoundMessage: 'package not found or too new', + errorMessages: { 404: 'package not found or too new' }, }) if (isRange) { downloads = downloads diff --git a/services/npm/npm-version.service.js b/services/npm/npm-version.service.js index 97634705b6e9ddd17747a42237e6467e7069c67a..43d3267851fd3392457aead3e502657aa27043d4 100644 --- a/services/npm/npm-version.service.js +++ b/services/npm/npm-version.service.js @@ -78,7 +78,7 @@ module.exports = class NpmVersion extends NpmBase { const packageData = await this._requestJson({ schema, url: `${registryUrl}/-/package/${slug}/dist-tags`, - notFoundMessage: 'package not found', + errorMessages: { 404: 'package not found' }, }) if (tag && !(tag in packageData)) {