diff --git a/services/wikiapiary/wikiapiary-installs.service.js b/services/wikiapiary/wikiapiary-installs.service.js new file mode 100644 index 0000000000000..3aa11e110da2a --- /dev/null +++ b/services/wikiapiary/wikiapiary-installs.service.js @@ -0,0 +1,110 @@ +'use strict' + +const Joi = require('joi') +const { metric } = require('../text-formatters') +const { BaseJsonService, NotFound } = require('..') + +const documentation = ` +

+ The name of an extension is case-sensitive excluding the first character. +

+

+ For example, in the case of ParserFunctions, the following are + valid: +

+ + However, the following are invalid: + +

+` + +const schema = Joi.object({ + query: Joi.object({ + results: Joi.alternatives([ + Joi.object() + .required() + .pattern(/^\w+:.+$/, { + printouts: Joi.object({ + 'Has website count': Joi.array() + .required() + .items(Joi.number().required()), + }).required(), + }), + Joi.array().required(), + ]).required(), + }).required(), +}).required() + +/** + * This badge displays the total installations of a MediaWiki extensions, skins, + * etc via Wikiapiary. + * + * {@link https://www.mediawiki.org/wiki/Manual:Extensions MediaWiki Extensions Manual} + */ +module.exports = class WikiapiaryInstalls extends BaseJsonService { + static category = 'downloads' + static route = { + base: 'wikiapiary', + pattern: ':variant(extension|skin|farm|generator|host)/installs/:name', + } + + static examples = [ + { + title: 'Wikiapiary installs', + namedParams: { variant: 'extension', name: 'ParserFunctions' }, + staticPreview: this.render({ usage: 11170 }), + documentation, + keywords: ['mediawiki'], + }, + ] + + static defaultBadgeData = { label: 'installs', color: 'informational' } + + static render({ usage }) { + return { message: metric(usage) } + } + + static validate({ results }) { + if (Array.isArray(results)) + throw new NotFound({ prettyMessage: 'not found' }) + } + + async fetch({ variant, name }) { + return this._requestJson({ + schema, + url: `https://wikiapiary.com/w/api.php`, + options: { + qs: { + action: 'ask', + query: `[[${variant}:${name}]]|?Has_website_count`, + format: 'json', + }, + }, + }) + } + + async handle({ variant, name }) { + const response = await this.fetch({ variant, name }) + const { results } = response.query + + this.constructor.validate({ results }) + + const keyLowerCase = `${variant}:${name.toLowerCase()}` + const resultKey = Object.keys(results).find( + key => keyLowerCase === key.toLowerCase() + ) + + if (resultKey === undefined) + throw new NotFound({ prettyMessage: 'not found' }) + + const [usage] = results[resultKey].printouts['Has website count'] + return this.constructor.render({ usage }) + } +} diff --git a/services/wikiapiary/wikiapiary-installs.tester.js b/services/wikiapiary/wikiapiary-installs.tester.js new file mode 100644 index 0000000000000..fee38c87645f1 --- /dev/null +++ b/services/wikiapiary/wikiapiary-installs.tester.js @@ -0,0 +1,44 @@ +'use strict' + +const t = (module.exports = require('../tester').createServiceTester()) +const { isMetric } = require('../test-validators') + +t.create('Extension') + .get('/extension/installs/ParserFunctions.json') + .expectBadge({ label: 'installs', message: isMetric }) + +t.create('Skins') + .get('/skin/installs/Vector.json') + .expectBadge({ label: 'installs', message: isMetric }) + +t.create('Extension Not Found') + .get('/extension/installs/FakeExtensionThatDoesNotExist.json') + .expectBadge({ label: 'installs', message: 'not found' }) + +t.create('Name Lowercase') + .get('/extension/installs/parserfunctions.json') + .expectBadge({ label: 'installs', message: 'not found' }) + +t.create('Name Title Case') + .get('/extension/installs/parserFunctions.json') + .expectBadge({ label: 'installs', message: isMetric }) + +t.create('Malformed API Response') + .get('/extension/installs/ParserFunctions.json') + .intercept(nock => + nock('https://wikiapiary.com') + .get('/w/api.php') + .query({ + action: 'ask', + query: '[[extension:ParserFunctions]]|?Has_website_count', + format: 'json', + }) + .reply(200, { + query: { + results: { + 'Extension:Malformed': { printouts: { 'Has website count': [0] } }, + }, + }, + }) + ) + .expectBadge({ label: 'installs', message: 'not found' })