From f78e6f1f8a24132bccb148f9f5fc2975426642c7 Mon Sep 17 00:00:00 2001 From: chris48s Date: Sat, 16 Jun 2018 20:50:14 +0100 Subject: [PATCH] [gem cdnjs appveyor clojars] refactor clojars, establish BaseJsonService (#1702) * refactor clojars integration * DRY up services that request data from JSON endpoints --- lib/all-badge-examples.js | 4 --- server.js | 32 ------------------------ services/appveyor/appveyor.js | 13 +++------- services/base.js | 24 +++++++++++++++++- services/base.spec.js | 2 +- services/cdnjs/cdnjs.js | 15 +++-------- services/clojars/clojars.js | 47 +++++++++++++++++++++++++++++++++++ services/gem/gem.js | 42 ++++++++++--------------------- 8 files changed, 91 insertions(+), 88 deletions(-) create mode 100644 services/clojars/clojars.js diff --git a/lib/all-badge-examples.js b/lib/all-badge-examples.js index 0db8e83b21d1b..e0bc3f077608e 100644 --- a/lib/all-badge-examples.js +++ b/lib/all-badge-examples.js @@ -893,10 +893,6 @@ const allBadgeExamples = [ title: 'Maven metadata URI', previewUri: '/maven-metadata/v/http/central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml.svg' }, - { - title: 'Clojars', - previewUri: '/clojars/v/prismic.svg' - }, { title: 'CocoaPods', previewUri: '/cocoapods/v/AFNetworking.svg' diff --git a/server.js b/server.js index c9637f5171d2f..5a96333fdd923 100644 --- a/server.js +++ b/server.js @@ -2067,38 +2067,6 @@ cache(function(data, match, sendBadge, request) { }); })); -// Clojars version integration -camp.route(/^\/clojars\/v\/(.+)\.(svg|png|gif|jpg|json)$/, -cache(function(data, match, sendBadge, request) { - const clojar = match[1]; // eg, `prismic` or `foo/bar`. - const format = match[2]; - const apiUrl = 'https://clojars.org/' + clojar + '/latest-version.json'; - const badgeData = getBadgeData('clojars', data); - request(apiUrl, function(err, res, buffer) { - if (err !== null) { - badgeData.text[1] = 'inaccessible'; - sendBadge(format, badgeData); - return; - } - try { - const json = JSON.parse(buffer); - if (Object.keys(json).length === 0) { - /* Note the 'not found' response from clojars is: - status code = 200, body = {} */ - badgeData.text[1] = 'not found'; - sendBadge(format, badgeData); - return; - } - badgeData.text[1] = "[" + clojar + " \"" + json.version + "\"]"; - badgeData.colorscheme = versionColor(json.version); - sendBadge(format, badgeData); - } catch(e) { - badgeData.text[1] = 'invalid'; - sendBadge(format, badgeData); - } - }); -})); - // iTunes App Store version camp.route(/^\/itunes\/v\/(.+)\.(svg|png|gif|jpg|json)$/, cache(function(data, match, sendBadge, request) { diff --git a/services/appveyor/appveyor.js b/services/appveyor/appveyor.js index 6e620a9ca483f..e5ffe1e8556c6 100644 --- a/services/appveyor/appveyor.js +++ b/services/appveyor/appveyor.js @@ -1,21 +1,14 @@ 'use strict'; -const BaseService = require('../base'); -const { - checkErrorResponse, - asJson, -} = require('../../lib/error-helper'); +const { BaseJsonService } = require('../base'); -module.exports = class AppVeyor extends BaseService { +module.exports = class AppVeyor extends BaseJsonService { async handle({repo, branch}) { let apiUrl = 'https://ci.appveyor.com/api/projects/' + repo; if (branch != null) { apiUrl += '/branch/' + branch; } - const json = await this._sendAndCacheRequest(apiUrl, { - headers: { 'Accept': 'application/json' } - }).then(checkErrorResponse.asPromise({ notFoundMessage: 'project not found or access denied' })) - .then(asJson); + const json = await this._requestJson(apiUrl, {}, 'project not found or access denied'); const { build: { status } } = json; if (status === 'success') { diff --git a/services/base.js b/services/base.js index cbc4c93839338..e328dc3fbe7d9 100644 --- a/services/base.js +++ b/services/base.js @@ -11,8 +11,13 @@ const { makeColor, setBadgeColor, } = require('../lib/badge-data'); +const { + checkErrorResponse, + asJson, +} = require('../lib/error-helper'); + -module.exports = class BaseService { +class BaseService { constructor({ sendAndCacheRequest }, { handleInternalErrors }) { this._sendAndCacheRequest = sendAndCacheRequest; this._handleInternalErrors = handleInternalErrors; @@ -215,3 +220,20 @@ module.exports = class BaseService { })); } }; + +class BaseJsonService extends BaseService { + async _requestJson(url, options = {}, notFoundMessage) { + return this._sendAndCacheRequest(url, + {...{ 'headers': { 'Accept': 'application/json' } }, ...options} + ).then( + checkErrorResponse.asPromise( + notFoundMessage ? { notFoundMessage: notFoundMessage } : undefined + ) + ).then(asJson); + } +}; + +module.exports = { + BaseService, + BaseJsonService, +}; diff --git a/services/base.spec.js b/services/base.spec.js index cd5b579db276f..ae7ce6ac8f5eb 100644 --- a/services/base.spec.js +++ b/services/base.spec.js @@ -4,7 +4,7 @@ const { expect } = require('chai'); const { test, given, forCases } = require('sazerac'); const sinon = require('sinon'); -const BaseService = require('./base'); +const { BaseService } = require('./base'); require('../lib/register-chai-plugins.spec'); diff --git a/services/cdnjs/cdnjs.js b/services/cdnjs/cdnjs.js index e49afc4cc6ba9..226127eb7a30f 100644 --- a/services/cdnjs/cdnjs.js +++ b/services/cdnjs/cdnjs.js @@ -1,21 +1,14 @@ 'use strict'; -const BaseService = require('../base'); -const { - checkErrorResponse, - asJson, -} = require('../../lib/error-helper'); +const { BaseJsonService } = require('../base'); const { NotFound } = require('../errors'); const { addv: versionText } = require('../../lib/text-formatters'); const { version: versionColor} = require('../../lib/color-formatters'); -module.exports = class Cdnjs extends BaseService { +module.exports = class Cdnjs extends BaseJsonService { async handle({library}) { const apiUrl = 'https://api.cdnjs.com/libraries/' + library + '?fields=version'; - const json = await this._sendAndCacheRequest(apiUrl, { - headers: { 'Accept': 'application/json' } - }).then(checkErrorResponse.asPromise()) - .then(asJson); + const json = await this._requestJson(apiUrl); if (Object.keys(json).length === 0) { /* Note the 'not found' response from cdnjs is: @@ -42,7 +35,7 @@ module.exports = class Cdnjs extends BaseService { static get url() { return { base: 'cdnjs/v', - format: '(.*)', + format: '(.+)', capture: ['library'] }; } diff --git a/services/clojars/clojars.js b/services/clojars/clojars.js new file mode 100644 index 0000000000000..a596cffaac997 --- /dev/null +++ b/services/clojars/clojars.js @@ -0,0 +1,47 @@ +'use strict'; + +const { BaseJsonService } = require('../base'); +const { NotFound } = require('../errors'); +const { version: versionColor } = require('../../lib/color-formatters'); + +module.exports = class Clojars extends BaseJsonService { + async handle({clojar}) { + const apiUrl = 'https://clojars.org/' + clojar + '/latest-version.json'; + const json = await this._requestJson(apiUrl); + + if (Object.keys(json).length === 0) { + /* Note the 'not found' response from clojars is: + status code = 200, body = {} */ + throw new NotFound(); + } + + return { + message: "[" + clojar + " \"" + json.version + "\"]", + color: versionColor(json.version) + }; + } + + // Metadata + static get defaultBadgeData() { + return { label: 'clojars' }; + } + + static get category() { + return 'version'; + } + + static get url() { + return { + base: 'clojars/v', + format: '(.+)', + capture: ['clojar'] + }; + } + + static get examples() { + return [ + { previewUrl: 'prismic' } + ]; + } + +}; diff --git a/services/gem/gem.js b/services/gem/gem.js index 434239e9b1cda..6f315fa4b4377 100644 --- a/services/gem/gem.js +++ b/services/gem/gem.js @@ -2,11 +2,7 @@ const semver = require('semver'); -const BaseService = require('../base'); -const { - checkErrorResponse, - asJson, -} = require('../../lib/error-helper'); +const { BaseJsonService } = require('../base'); const { InvalidResponse } = require('../errors'); const { addv: versionText } = require('../../lib/text-formatters'); const { version: versionColor} = require('../../lib/color-formatters'); @@ -21,13 +17,10 @@ const { const { latest: latestVersion } = require('../../lib/version'); -class GemVersion extends BaseService { +class GemVersion extends BaseJsonService { async handle({repo}) { const apiUrl = 'https://rubygems.org/api/v1/gems/' + repo + '.json'; - const json = await this._sendAndCacheRequest(apiUrl, { - headers: { 'Accept': 'application/json' } - }).then(checkErrorResponse.asPromise()) - .then(asJson); + const json = await this._requestJson(apiUrl); const version = json.version; return { @@ -48,7 +41,7 @@ class GemVersion extends BaseService { static get url() { return { base: 'gem/v', - format: '(.*)', + format: '(.+)', capture: ['repo'] }; } @@ -66,7 +59,7 @@ class GemVersion extends BaseService { } }; -class GemDownloads extends BaseService { +class GemDownloads extends BaseJsonService { _getApiUrl(repo, info) { const endpoint = info === "dv" ? 'versions/' : 'gems/'; @@ -94,10 +87,7 @@ class GemDownloads extends BaseService { version = (version === "stable") ? version : semver.valid(version); const label = this._getLabel(version, info); const apiUrl = this._getApiUrl(repo, info); - const json = await this._sendAndCacheRequest(apiUrl, { - headers: { 'Accept': 'application/atom+json,application/json' } - }).then(checkErrorResponse.asPromise()) - .then(asJson); + const json = await this._requestJson(apiUrl); let downloads; if (info === "dt") { @@ -155,7 +145,7 @@ class GemDownloads extends BaseService { static get url() { return { base: 'gem', - format: '(dt|dtv|dv)/(.*)', + format: '(dt|dtv|dv)/(.+)', capture: ['info', 'rubygem'] }; } @@ -194,14 +184,11 @@ class GemDownloads extends BaseService { } }; -class GemOwner extends BaseService { +class GemOwner extends BaseJsonService { async handle({user}) { const apiUrl = 'https://rubygems.org/api/v1/owners/' + user + '/gems.json'; - const json = await this._sendAndCacheRequest(apiUrl, { - headers: { 'Accept': 'application/json' } - }).then(checkErrorResponse.asPromise()) - .then(asJson); + const json = await this._requestJson(apiUrl); const count = json.length; return { @@ -222,7 +209,7 @@ class GemOwner extends BaseService { static get url() { return { base: 'gem/u', - format: '(.*)', + format: '(.+)', capture: ['user'] }; } @@ -240,7 +227,7 @@ class GemOwner extends BaseService { } }; -class GemRank extends BaseService { +class GemRank extends BaseJsonService { _getApiUrl(repo, totalRank, dailyRank) { let endpoint; @@ -256,10 +243,7 @@ class GemRank extends BaseService { const totalRank = (info === 'rt'); const dailyRank = (info === 'rd'); const apiUrl = this._getApiUrl(repo, totalRank, dailyRank); - const json = await this._sendAndCacheRequest(apiUrl, { - headers: { 'Accept': 'application/json' } - }).then(checkErrorResponse.asPromise()) - .then(asJson); + const json = await this._requestJson(apiUrl); let rank; if (totalRank) { @@ -289,7 +273,7 @@ class GemRank extends BaseService { static get url() { return { base: 'gem', - format: '(rt|rd)/(.*)', + format: '(rt|rd)/(.+)', capture: ['info', 'repo'] }; }