From b77480afa79e162749c9629aad09b8d2f4d659b7 Mon Sep 17 00:00:00 2001 From: chris48s Date: Sun, 16 Sep 2018 15:35:06 +0100 Subject: [PATCH 1/3] refactor arch user repositories [aur] service code --- lib/all-badge-examples.js | 15 --- services/aur/aur.service.js | 227 ++++++++++++++++++++++++++---------- services/aur/aur.tester.js | 80 +------------ 3 files changed, 172 insertions(+), 150 deletions(-) diff --git a/lib/all-badge-examples.js b/lib/all-badge-examples.js index 6fd70421be121..83d4f972e66d3 100644 --- a/lib/all-badge-examples.js +++ b/lib/all-badge-examples.js @@ -984,11 +984,6 @@ const allBadgeExamples = [ name: 'License', }, examples: [ - { - title: 'AUR', - previewUrl: '/aur/license/yaourt.svg', - keywords: ['aur'], - }, { title: 'GitHub', previewUrl: '/github/license/mashape/apistatus.svg', @@ -1063,11 +1058,6 @@ const allBadgeExamples = [ '/chrome-web-store/rating-count/ogffaloegjglncjfehdfplabnoondfjo.svg', keywords: ['chrome'], }, - { - title: 'AUR', - previewUrl: '/aur/votes/yaourt.svg', - keywords: ['aur'], - }, { title: 'Mozilla Add-on', previewUrl: '/amo/rating/dustman.svg', @@ -1376,11 +1366,6 @@ const allBadgeExamples = [ previewUrl: '/dub/v/vibe-d.svg', keywords: ['dub'], }, - { - title: 'AUR', - previewUrl: '/aur/version/yaourt.svg', - keywords: ['aur'], - }, { title: 'Chrome Web Store', previewUrl: '/chrome-web-store/v/ogffaloegjglncjfehdfplabnoondfjo.svg', diff --git a/services/aur/aur.service.js b/services/aur/aur.service.js index 74b077940d00d..9d34d191ef3b8 100644 --- a/services/aur/aur.service.js +++ b/services/aur/aur.service.js @@ -1,67 +1,174 @@ 'use strict' -const LegacyService = require('../legacy-service') -const { - makeBadgeData: getBadgeData, - makeLabel: getLabel, -} = require('../../lib/badge-data') -const { checkErrorResponse } = require('../../lib/error-helper') +const Joi = require('joi') const { floorCount: floorCountColor } = require('../../lib/color-formatters') const { addv: versionText } = require('../../lib/text-formatters') +const BaseJsonService = require('../base-json') +const { NotFound } = require('../errors') +const { nonNegativeInteger } = require('../validators') -// For the Arch user repository (AUR). -module.exports = class Aur extends LegacyService { - static registerLegacyRouteHandler({ camp, cache }) { - camp.route( - /^\/aur\/(version|votes|license)\/(.*)\.(svg|png|gif|jpg|json)$/, - cache((data, match, sendBadge, request) => { - const info = match[1] - const pkg = match[2] - const format = match[3] - const apiUrl = 'https://aur.archlinux.org/rpc.php?type=info&arg=' + pkg - const badgeData = getBadgeData('aur', data) - request(apiUrl, (err, res, buffer) => { - if (checkErrorResponse(badgeData, err, res)) { - sendBadge(format, badgeData) - return - } - try { - const parsedBuffer = JSON.parse(buffer) - const parsedData = parsedBuffer.results - if (parsedBuffer.resultcount === 0) { - // Note the 'not found' response from Arch Linux is: - // status code = 200, - // body = {"version":1,"type":"info","resultcount":0,"results":[]} - badgeData.text[1] = 'not found' - sendBadge(format, badgeData) - return - } - - if (info === 'version') { - badgeData.text[1] = versionText(parsedData.Version) - if (parsedData.OutOfDate === null) { - badgeData.colorscheme = 'blue' - } else { - badgeData.colorscheme = 'orange' - } - } else if (info === 'votes') { - const votes = parsedData.NumVotes - badgeData.text[0] = getLabel('votes', data) - badgeData.text[1] = votes - badgeData.colorscheme = floorCountColor(votes, 2, 20, 60) - } else if (info === 'license') { - const license = parsedData.License - badgeData.text[0] = getLabel('license', data) - badgeData.text[1] = license - badgeData.colorscheme = 'blue' - } - sendBadge(format, badgeData) - } catch (e) { - badgeData.text[1] = 'invalid' - sendBadge(format, badgeData) - } - }) - }) - ) +const aurSchema = Joi.object({ + resultcount: nonNegativeInteger, + results: Joi.alternatives( + Joi.array() + .min(0) + .max(0) + .required(), + Joi.object({ + License: Joi.string().required(), + NumVotes: nonNegativeInteger, + Version: Joi.string().required(), + OutOfDate: nonNegativeInteger.allow(null), + }).required() + ), +}).required() + +class BaseAurService extends BaseJsonService { + async fetch({ pkg }) { + return this._requestJson({ + schema: aurSchema, + url: `https://aur.archlinux.org/rpc.php?type=info&arg=${pkg}`, + }) + } + + static get defaultBadgeData() { + return { label: 'aur' } + } + + static _validate(data, schema) { + if (data.resultcount === 0) { + // Note the 'not found' response from Arch Linux is: + // status code = 200, + // body = {"version":1,"type":"info","resultcount":0,"results":[]} + throw new NotFound() + } + return super._validate(data, schema) + } +} + +class AurLicense extends BaseAurService { + static render({ license }) { + return { message: license, color: 'blue' } + } + + async handle({ pkg }) { + const json = await this.fetch({ pkg }) + return this.constructor.render({ license: json.results.License }) + } + + static get defaultBadgeData() { + return { label: 'license' } + } + + static get category() { + return 'license' + } + + static get url() { + return { + base: 'aur/license', + format: '(.+)', + capture: ['pkg'], + } + } + + static get examples() { + return [ + { + title: `AUR license`, + urlPattern: ':package', + exampleUrl: 'yaourt', + staticExample: this.render({ license: 'GPL' }), + }, + ] + } +} + +class AurVotes extends BaseAurService { + static render({ votes }) { + return { + message: votes, + color: floorCountColor(votes, 2, 20, 60), + } + } + + async handle({ pkg }) { + const json = await this.fetch({ pkg }) + return this.constructor.render({ votes: json.results.NumVotes }) } + + static get defaultBadgeData() { + return { label: 'votes' } + } + + static get category() { + return 'rating' + } + + static get url() { + return { + base: 'aur/votes', + format: '(.+)', + capture: ['pkg'], + } + } + + static get examples() { + return [ + { + title: `AUR votes`, + urlPattern: ':package', + exampleUrl: 'yaourt', + staticExample: this.render({ license: '3029' }), + }, + ] + } +} + +class AurVersion extends BaseAurService { + static render({ version, outOfDate }) { + const color = outOfDate === null ? 'blue' : 'orange' + return { message: versionText(version), color: color } + } + + async handle({ pkg }) { + const json = await this.fetch({ pkg }) + return this.constructor.render({ + version: json.results.Version, + outOfDate: json.results.OutOfDate, + }) + } + + static get defaultBadgeData() { + return { label: 'version' } + } + + static get category() { + return 'version' + } + + static get url() { + return { + base: 'aur/version', + format: '(.+)', + capture: ['pkg'], + } + } + + static get examples() { + return [ + { + title: `AUR version`, + urlPattern: ':package', + exampleUrl: 'yaourt', + staticExample: this.render({ version: 'v1.9-1', outOfDate: null }), + }, + ] + } +} + +module.exports = { + AurLicense, + AurVersion, + AurVotes, } diff --git a/services/aur/aur.tester.js b/services/aur/aur.tester.js index 128747f71edcb..00e695c337e88 100644 --- a/services/aur/aur.tester.js +++ b/services/aur/aur.tester.js @@ -5,7 +5,6 @@ const ServiceTester = require('../service-tester') const { isVPlusDottedVersionNClausesWithOptionalSuffix, } = require('../test-validators') -const { invalidJSON } = require('../response-fixtures') const t = new ServiceTester({ id: 'aur', title: 'Arch Linux AUR' }) module.exports = t @@ -16,7 +15,7 @@ t.create('version (valid)') .get('/version/yaourt.json?style=_shields_test') .expectJSONTypes( Joi.object().keys({ - name: 'aur', + name: 'version', value: isVPlusDottedVersionNClausesWithOptionalSuffix, colorB: '#007ec6', }) @@ -26,7 +25,7 @@ t.create('version (valid, out of date)') .get('/version/gog-gemini-rue.json?style=_shields_test') .expectJSONTypes( Joi.object().keys({ - name: 'aur', + name: 'version', value: isVPlusDottedVersionNClausesWithOptionalSuffix, colorB: '#fe7d37', }) @@ -34,30 +33,7 @@ t.create('version (valid, out of date)') t.create('version (not found)') .get('/version/not-a-package.json') - .expectJSON({ name: 'aur', value: 'not found' }) - -t.create('version (connection error)') - .get('/version/yaourt.json') - .networkOff() - .expectJSON({ name: 'aur', value: 'inaccessible' }) - -t.create('version (unexpected response)') - .get('/version/yaourt.json') - .intercept(nock => - nock('https://aur.archlinux.org') - .get('/rpc.php?type=info&arg=yaourt') - .reply(invalidJSON) - ) - .expectJSON({ name: 'aur', value: 'invalid' }) - -t.create('version (error response)') - .get('/version/yaourt.json') - .intercept(nock => - nock('https://aur.archlinux.org') - .get('/rpc.php?type=info&arg=yaourt') - .reply(500, '{"error":"oh noes!!"}') - ) - .expectJSON({ name: 'aur', value: 'invalid' }) + .expectJSON({ name: 'version', value: 'not found' }) // votes tests @@ -72,30 +48,7 @@ t.create('votes (valid)') t.create('votes (not found)') .get('/votes/not-a-package.json') - .expectJSON({ name: 'aur', value: 'not found' }) - -t.create('votes (connection error)') - .get('/votes/yaourt.json') - .networkOff() - .expectJSON({ name: 'aur', value: 'inaccessible' }) - -t.create('votes (unexpected response)') - .get('/votes/yaourt.json') - .intercept(nock => - nock('https://aur.archlinux.org') - .get('/rpc.php?type=info&arg=yaourt') - .reply(invalidJSON) - ) - .expectJSON({ name: 'aur', value: 'invalid' }) - -t.create('votes (error response)') - .get('/votes/yaourt.json') - .intercept(nock => - nock('https://aur.archlinux.org') - .get('/rpc.php?type=info&arg=yaourt') - .reply(500, '{"error":"oh noes!!"}') - ) - .expectJSON({ name: 'aur', value: 'invalid' }) + .expectJSON({ name: 'votes', value: 'not found' }) // license tests @@ -105,27 +58,4 @@ t.create('license (valid)') t.create('license (not found)') .get('/license/not-a-package.json') - .expectJSON({ name: 'aur', value: 'not found' }) - -t.create('license (connection error)') - .get('/license/yaourt.json') - .networkOff() - .expectJSON({ name: 'aur', value: 'inaccessible' }) - -t.create('license (unexpected response)') - .get('/license/yaourt.json') - .intercept(nock => - nock('https://aur.archlinux.org') - .get('/rpc.php?type=info&arg=yaourt') - .reply(invalidJSON) - ) - .expectJSON({ name: 'aur', value: 'invalid' }) - -t.create('license (error response)') - .get('/license/yaourt.json') - .intercept(nock => - nock('https://aur.archlinux.org') - .get('/rpc.php?type=info&arg=yaourt') - .reply(500, '{"error":"oh noes!!"}') - ) - .expectJSON({ name: 'aur', value: 'invalid' }) + .expectJSON({ name: 'license', value: 'not found' }) From af73eadc4ca149d755b9892076837b3fead8762f Mon Sep 17 00:00:00 2001 From: chris48s Date: Sat, 6 Oct 2018 16:27:32 +0100 Subject: [PATCH 2/3] more succinct validator --- services/aur/aur.service.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/aur/aur.service.js b/services/aur/aur.service.js index 9d34d191ef3b8..aa0bf035f5ea8 100644 --- a/services/aur/aur.service.js +++ b/services/aur/aur.service.js @@ -11,8 +11,7 @@ const aurSchema = Joi.object({ resultcount: nonNegativeInteger, results: Joi.alternatives( Joi.array() - .min(0) - .max(0) + .length(0) .required(), Joi.object({ License: Joi.string().required(), From f29fc07faaf1b113353d4290b861c0aaec5e5069 Mon Sep 17 00:00:00 2001 From: chris48s Date: Sat, 6 Oct 2018 16:32:41 +0100 Subject: [PATCH 3/3] version --> aur --- services/aur/aur.service.js | 4 ---- services/aur/aur.tester.js | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/services/aur/aur.service.js b/services/aur/aur.service.js index aa0bf035f5ea8..f6fa4bfd18c12 100644 --- a/services/aur/aur.service.js +++ b/services/aur/aur.service.js @@ -138,10 +138,6 @@ class AurVersion extends BaseAurService { }) } - static get defaultBadgeData() { - return { label: 'version' } - } - static get category() { return 'version' } diff --git a/services/aur/aur.tester.js b/services/aur/aur.tester.js index 00e695c337e88..37bdf2e658207 100644 --- a/services/aur/aur.tester.js +++ b/services/aur/aur.tester.js @@ -15,7 +15,7 @@ t.create('version (valid)') .get('/version/yaourt.json?style=_shields_test') .expectJSONTypes( Joi.object().keys({ - name: 'version', + name: 'aur', value: isVPlusDottedVersionNClausesWithOptionalSuffix, colorB: '#007ec6', }) @@ -25,7 +25,7 @@ t.create('version (valid, out of date)') .get('/version/gog-gemini-rue.json?style=_shields_test') .expectJSONTypes( Joi.object().keys({ - name: 'version', + name: 'aur', value: isVPlusDottedVersionNClausesWithOptionalSuffix, colorB: '#fe7d37', }) @@ -33,7 +33,7 @@ t.create('version (valid, out of date)') t.create('version (not found)') .get('/version/not-a-package.json') - .expectJSON({ name: 'version', value: 'not found' }) + .expectJSON({ name: 'aur', value: 'not found' }) // votes tests