diff --git a/services/redmine/redmine.service.js b/services/redmine/redmine.service.js index ff42d98695..1c23d8fd55 100644 --- a/services/redmine/redmine.service.js +++ b/services/redmine/redmine.service.js @@ -1,19 +1,77 @@ 'use strict' -const xml2js = require('xml2js') -const LegacyService = require('../legacy-service') -const { makeBadgeData: getBadgeData } = require('../../lib/badge-data') const { starRating } = require('../../lib/text-formatters') const { floorCount: floorCountColor } = require('../../lib/color-formatters') -module.exports = class Redmine extends LegacyService { +const Joi = require('joi') +const BaseXmlService = require('../base-xml') + +const schema = Joi.object({ + 'redmine-plugin': Joi.object({ + 'ratings-average': Joi.number() + .min(0) + .required(), + }).required(), +}) + +class BaseRedminePluginRating extends BaseXmlService { + async fetch({ plugin }) { + const url = `https://www.redmine.org/plugins/${plugin}.xml` + return this._requestXml({ schema, url }) + } + static get category() { return 'rating' } + static render({ rating }) { + throw new Error(`render() function not implemented for ${this.name}`) + } + + async handle({ plugin }) { + const data = await this.fetch({ plugin }) + const rating = data['redmine-plugin']['ratings-average'] + return this.constructor.render({ rating }) + } +} + +class RedminePluginRating extends BaseRedminePluginRating { static get route() { return { - base: 'redmine/plugin', + base: 'redmine/plugin/rating', + pattern: ':plugin', + } + } + + static get defaultBadgeData() { + return { label: 'redmine' } + } + + static get examples() { + return [ + { + title: 'Plugin on redmine.org', + namedParams: { plugin: 'redmine_xlsx_format_issue_exporter' }, + staticExample: this.render({ rating: 5 }), + keywords: ['redmine', 'plugin'], + }, + ] + } + + static render({ rating }) { + return { + label: 'rating', + message: `${rating.toFixed(1)}/5.0`, + color: floorCountColor(rating, 2, 3, 4), + } + } +} + +class RedminePluginStars extends BaseRedminePluginRating { + static get route() { + return { + base: 'redmine/plugin/stars', + pattern: ':plugin', } } @@ -21,60 +79,20 @@ module.exports = class Redmine extends LegacyService { return [ { title: 'Plugin on redmine.org', - previewUrl: 'rating/redmine_xlsx_format_issue_exporter', - keywords: ['redmine', 'plugin'], - }, - { - title: 'Plugin on redmine.org', - previewUrl: 'stars/redmine_xlsx_format_issue_exporter', + namedParams: { plugin: 'redmine_xlsx_format_issue_exporter' }, + staticExample: this.render({ rating: 5 }), keywords: ['redmine', 'plugin'], }, ] } - static registerLegacyRouteHandler({ camp, cache }) { - camp.route( - /^\/redmine\/plugin\/(rating|stars)\/(.*)\.(svg|png|gif|jpg|json)$/, - cache((data, match, sendBadge, request) => { - const type = match[1] - const plugin = match[2] - const format = match[3] - const options = { - method: 'GET', - uri: `https://www.redmine.org/plugins/${plugin}.xml`, - } - - const badgeData = getBadgeData(type, data) - request(options, (err, res, buffer) => { - if (err != null) { - badgeData.text[1] = 'inaccessible' - sendBadge(format, badgeData) - return - } - - // eslint-disable-next-line handle-callback-err - xml2js.parseString(buffer.toString(), (err, data) => { - try { - const rating = data['redmine-plugin']['ratings-average'][0]._ - badgeData.colorscheme = floorCountColor(rating, 2, 3, 4) - - switch (type) { - case 'rating': - badgeData.text[1] = `${rating}/5.0` - break - case 'stars': - badgeData.text[1] = starRating(Math.round(rating)) - break - } - - sendBadge(format, badgeData) - } catch (e) { - badgeData.text[1] = 'invalid' - sendBadge(format, badgeData) - } - }) - }) - }) - ) + static render({ rating }) { + return { + label: 'stars', + message: starRating(Math.round(rating)), + color: floorCountColor(rating, 2, 3, 4), + } } } + +module.exports = { RedminePluginRating, RedminePluginStars } diff --git a/services/redmine/redmine.tester.js b/services/redmine/redmine.tester.js index d6a1ac95c7..b06cd1460d 100644 --- a/services/redmine/redmine.tester.js +++ b/services/redmine/redmine.tester.js @@ -9,16 +9,6 @@ module.exports = t t.create('plugin rating') .get('/plugin/rating/redmine_xlsx_format_issue_exporter.json') - .intercept(nock => - nock('https://www.redmine.org') - .get('/plugins/redmine_xlsx_format_issue_exporter.xml') - .reply( - 200, - '' + - '1.23456' + - '' - ) - ) .expectJSONTypes( Joi.object().keys({ name: 'rating', @@ -28,16 +18,6 @@ t.create('plugin rating') t.create('plugin stars') .get('/plugin/stars/redmine_xlsx_format_issue_exporter.json') - .intercept(nock => - nock('https://www.redmine.org') - .get('/plugins/redmine_xlsx_format_issue_exporter.xml') - .reply( - 200, - '' + - '1.23456' + - '' - ) - ) .expectJSONTypes( Joi.object().keys({ name: 'stars', @@ -47,22 +27,9 @@ t.create('plugin stars') t.create('plugin not found') .get('/plugin/rating/plugin_not_found.json') - .intercept(nock => - nock('https://www.redmine.org') - .get('/plugins/plugin_not_found.xml') - .reply(404, '') - ) .expectJSONTypes( Joi.object().keys({ - name: 'rating', - value: 'invalid', + name: 'redmine', + value: 'not found', }) ) - -t.create('connection error') - .get('/plugin/rating/redmine_xlsx_format_issue_exporter.json') - .networkOff() - .expectJSON({ - name: 'rating', - value: 'inaccessible', - })