diff --git a/services/amo/amo-base.js b/services/amo/amo-base.js index 36771f8a6ea92..277c331b3992a 100644 --- a/services/amo/amo-base.js +++ b/services/amo/amo-base.js @@ -2,8 +2,19 @@ import Joi from 'joi' import { nonNegativeInteger } from '../validators.js' import { BaseJsonService } from '../index.js' +const registryMap = { + firefox: 'https://addons.mozilla.org/api/v4', + thunderbird: 'https://addons.thunderbird.net/api/v5', +} + const description = - '[addons.mozilla.org](https://addons.mozilla.org) (AMO) publishes extensions for Mozilla Firefox' + 'Supports [addons.mozilla.org](https://addons.mozilla.org) (Mozilla Firefox) and ' + + '[addons.thunderbird.net](https://addons.thunderbird.net) (Mozilla Thunderbird). ' + + 'Use `?registry=thunderbird` for Thunderbird add-ons.' + +const queryParamSchema = Joi.object({ + registry: Joi.string().valid('firefox', 'thunderbird').default('firefox'), +}).required() const schema = Joi.object({ average_daily_users: nonNegativeInteger, @@ -19,12 +30,13 @@ const schema = Joi.object({ class BaseAmoService extends BaseJsonService { static defaultBadgeData = { label: 'mozilla add-on' } - async fetch({ addonId }) { + async fetch({ addonId, registry = 'firefox' }) { + const apiUrl = registryMap[registry] return this._requestJson({ schema, - url: `https://addons.mozilla.org/api/v4/addons/addon/${addonId}/`, + url: `${apiUrl}/addons/addon/${addonId}/`, }) } } -export { BaseAmoService, description } +export { BaseAmoService, description, queryParamSchema } diff --git a/services/amo/amo-downloads.service.js b/services/amo/amo-downloads.service.js index 40e96be86721a..a4881177dec69 100644 --- a/services/amo/amo-downloads.service.js +++ b/services/amo/amo-downloads.service.js @@ -1,6 +1,10 @@ import { renderDownloadsBadge } from '../downloads.js' -import { deprecatedService, pathParams } from '../index.js' -import { BaseAmoService, description as baseDescription } from './amo-base.js' +import { deprecatedService, pathParam, queryParam } from '../index.js' +import { + BaseAmoService, + description as baseDescription, + queryParamSchema, +} from './amo-base.js' const description = `${baseDescription} @@ -11,14 +15,23 @@ only give us weekly downloads. The route \`amo/d\` redirects to \`amo/dw\`. class AmoWeeklyDownloads extends BaseAmoService { static category = 'downloads' - static route = { base: 'amo/dw', pattern: ':addonId' } + static route = { base: 'amo/dw', pattern: ':addonId', queryParamSchema } static openApi = { '/amo/dw/{addonId}': { get: { summary: 'Mozilla Add-on Downloads', description, - parameters: pathParams({ name: 'addonId', example: 'dustman' }), + parameters: [ + pathParam({ name: 'addonId', example: 'dustman' }), + queryParam({ + name: 'registry', + example: 'thunderbird', + schema: { type: 'string', enum: ['firefox', 'thunderbird'] }, + description: + 'Registry to use. Can be `firefox` (default) or `thunderbird`.', + }), + ], }, }, } @@ -31,8 +44,8 @@ class AmoWeeklyDownloads extends BaseAmoService { return renderDownloadsBadge({ downloads, interval: 'week' }) } - async handle({ addonId }) { - const data = await this.fetch({ addonId }) + async handle({ addonId }, { registry }) { + const data = await this.fetch({ addonId, registry }) return this.constructor.render({ downloads: data.weekly_downloads, }) diff --git a/services/amo/amo-downloads.tester.js b/services/amo/amo-downloads.tester.js index 4c4278b63aed2..e6e993e8b9ee5 100644 --- a/services/amo/amo-downloads.tester.js +++ b/services/amo/amo-downloads.tester.js @@ -20,3 +20,17 @@ t.create('/d URL should return deprecated badge') label: 'mozilla-add-on', message: 'https://github.com/badges/shields/pull/11583', }) + +t.create('Weekly Downloads (thunderbird)') + .get('/dw/tbkeys-lite.json?registry=thunderbird') + .intercept(nock => + nock('https://addons.thunderbird.net') + .get('/api/v5/addons/addon/tbkeys-lite/') + .reply(200, { + average_daily_users: 1000, + current_version: { version: '4.0.0' }, + ratings: { average: 4.5 }, + weekly_downloads: 200, + }), + ) + .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod }) diff --git a/services/amo/amo-rating.service.js b/services/amo/amo-rating.service.js index 3e8b2989fe52a..45755c759d7ae 100644 --- a/services/amo/amo-rating.service.js +++ b/services/amo/amo-rating.service.js @@ -1,25 +1,47 @@ import { starRating } from '../text-formatters.js' import { floorCount as floorCountColor } from '../color-formatters.js' -import { pathParams } from '../index.js' -import { BaseAmoService, description } from './amo-base.js' +import { pathParam, queryParam } from '../index.js' +import { BaseAmoService, description, queryParamSchema } from './amo-base.js' export default class AmoRating extends BaseAmoService { static category = 'rating' - static route = { base: 'amo', pattern: ':format(stars|rating)/:addonId' } + static route = { + base: 'amo', + pattern: ':format(stars|rating)/:addonId', + queryParamSchema, + } static openApi = { '/amo/rating/{addonId}': { get: { summary: 'Mozilla Add-on Rating', description, - parameters: pathParams({ name: 'addonId', example: 'dustman' }), + parameters: [ + pathParam({ name: 'addonId', example: 'dustman' }), + queryParam({ + name: 'registry', + example: 'thunderbird', + schema: { type: 'string', enum: ['firefox', 'thunderbird'] }, + description: + 'Registry to use. Can be `firefox` (default) or `thunderbird`.', + }), + ], }, }, '/amo/stars/{addonId}': { get: { summary: 'Mozilla Add-on Stars', description, - parameters: pathParams({ name: 'addonId', example: 'dustman' }), + parameters: [ + pathParam({ name: 'addonId', example: 'dustman' }), + queryParam({ + name: 'registry', + example: 'thunderbird', + schema: { type: 'string', enum: ['firefox', 'thunderbird'] }, + description: + 'Registry to use. Can be `firefox` (default) or `thunderbird`.', + }), + ], }, }, } @@ -37,8 +59,8 @@ export default class AmoRating extends BaseAmoService { } } - async handle({ format, addonId }) { - const data = await this.fetch({ addonId }) + async handle({ format, addonId }, { registry }) { + const data = await this.fetch({ addonId, registry }) return this.constructor.render({ format, rating: data.ratings.average }) } } diff --git a/services/amo/amo-rating.tester.js b/services/amo/amo-rating.tester.js index dc327d15d69f9..23fcdac8e121f 100644 --- a/services/amo/amo-rating.tester.js +++ b/services/amo/amo-rating.tester.js @@ -17,3 +17,20 @@ t.create('Stars') t.create('Rating (not found)') .get('/rating/not-a-real-plugin.json') .expectBadge({ label: 'mozilla add-on', message: 'not found' }) + +t.create('Rating (thunderbird)') + .get('/rating/tbkeys-lite.json?registry=thunderbird') + .intercept(nock => + nock('https://addons.thunderbird.net') + .get('/api/v5/addons/addon/tbkeys-lite/') + .reply(200, { + average_daily_users: 1000, + current_version: { version: '4.0.0' }, + ratings: { average: 4.5 }, + weekly_downloads: 200, + }), + ) + .expectBadge({ + label: 'rating', + message: Joi.string().regex(/^\d(\.\d)?\/\d$/), + }) diff --git a/services/amo/amo-users.service.js b/services/amo/amo-users.service.js index cd850b2b597bd..64d1bfed8cacd 100644 --- a/services/amo/amo-users.service.js +++ b/services/amo/amo-users.service.js @@ -1,17 +1,26 @@ import { renderDownloadsBadge } from '../downloads.js' -import { pathParams } from '../index.js' -import { BaseAmoService, description } from './amo-base.js' +import { pathParam, queryParam } from '../index.js' +import { BaseAmoService, description, queryParamSchema } from './amo-base.js' export default class AmoUsers extends BaseAmoService { static category = 'downloads' - static route = { base: 'amo/users', pattern: ':addonId' } + static route = { base: 'amo/users', pattern: ':addonId', queryParamSchema } static openApi = { '/amo/users/{addonId}': { get: { summary: 'Mozilla Add-on Users', description, - parameters: pathParams({ name: 'addonId', example: 'dustman' }), + parameters: [ + pathParam({ name: 'addonId', example: 'dustman' }), + queryParam({ + name: 'registry', + example: 'thunderbird', + schema: { type: 'string', enum: ['firefox', 'thunderbird'] }, + description: + 'Registry to use. Can be `firefox` (default) or `thunderbird`.', + }), + ], }, }, } @@ -24,8 +33,8 @@ export default class AmoUsers extends BaseAmoService { return renderDownloadsBadge({ downloads, colorOverride: 'blue' }) } - async handle({ addonId }) { - const data = await this.fetch({ addonId }) + async handle({ addonId }, { registry }) { + const data = await this.fetch({ addonId, registry }) return this.constructor.render({ users: data.average_daily_users }) } } diff --git a/services/amo/amo-users.tester.js b/services/amo/amo-users.tester.js index 5355be2ce1c25..8faaaec7d180e 100644 --- a/services/amo/amo-users.tester.js +++ b/services/amo/amo-users.tester.js @@ -9,3 +9,17 @@ t.create('Users') t.create('Users (not found)') .get('/not-a-real-plugin.json') .expectBadge({ label: 'users', message: 'not found' }) + +t.create('Users (thunderbird)') + .get('/tbkeys-lite.json?registry=thunderbird') + .intercept(nock => + nock('https://addons.thunderbird.net') + .get('/api/v5/addons/addon/tbkeys-lite/') + .reply(200, { + average_daily_users: 1000, + current_version: { version: '4.0.0' }, + ratings: { average: 4.5 }, + weekly_downloads: 200, + }), + ) + .expectBadge({ label: 'users', message: isMetric }) diff --git a/services/amo/amo-version.service.js b/services/amo/amo-version.service.js index 50f74f8c5b28f..5e77c4f07a9d4 100644 --- a/services/amo/amo-version.service.js +++ b/services/amo/amo-version.service.js @@ -1,23 +1,32 @@ import { renderVersionBadge } from '../version.js' -import { pathParams } from '../index.js' -import { BaseAmoService, description } from './amo-base.js' +import { pathParam, queryParam } from '../index.js' +import { BaseAmoService, description, queryParamSchema } from './amo-base.js' export default class AmoVersion extends BaseAmoService { static category = 'version' - static route = { base: 'amo/v', pattern: ':addonId' } + static route = { base: 'amo/v', pattern: ':addonId', queryParamSchema } static openApi = { '/amo/v/{addonId}': { get: { summary: 'Mozilla Add-on Version', description, - parameters: pathParams({ name: 'addonId', example: 'dustman' }), + parameters: [ + pathParam({ name: 'addonId', example: 'dustman' }), + queryParam({ + name: 'registry', + example: 'thunderbird', + schema: { type: 'string', enum: ['firefox', 'thunderbird'] }, + description: + 'Registry to use. Can be `firefox` (default) or `thunderbird`.', + }), + ], }, }, } - async handle({ addonId }) { - const data = await this.fetch({ addonId }) + async handle({ addonId }, { registry }) { + const data = await this.fetch({ addonId, registry }) return renderVersionBadge({ version: data.current_version.version }) } } diff --git a/services/amo/amo-version.tester.js b/services/amo/amo-version.tester.js index 001ae8f162082..dfd5ca62d3e1f 100644 --- a/services/amo/amo-version.tester.js +++ b/services/amo/amo-version.tester.js @@ -10,3 +10,29 @@ t.create('Version').get('/night-video-tuner.json').expectBadge({ t.create('Version (not found)') .get('/not-a-real-plugin.json') .expectBadge({ label: 'mozilla add-on', message: 'not found' }) + +t.create('Version (thunderbird)') + .get('/tbkeys-lite.json?registry=thunderbird') + .intercept(nock => + nock('https://addons.thunderbird.net') + .get('/api/v5/addons/addon/tbkeys-lite/') + .reply(200, { + average_daily_users: 1000, + current_version: { version: '4.0.0' }, + ratings: { average: 4.5 }, + weekly_downloads: 200, + }), + ) + .expectBadge({ + label: 'mozilla add-on', + message: isVPlusDottedVersionAtLeastOne, + }) + +t.create('Version (thunderbird, not found)') + .get('/not-a-real-plugin.json?registry=thunderbird') + .intercept(nock => + nock('https://addons.thunderbird.net') + .get('/api/v5/addons/addon/not-a-real-plugin/') + .reply(404), + ) + .expectBadge({ label: 'mozilla add-on', message: 'not found' })