diff --git a/core/base-service/base-non-memory-caching.js b/core/base-service/base-non-memory-caching.js index a988e27ee4fe3..db457ae58aad7 100644 --- a/core/base-service/base-non-memory-caching.js +++ b/core/base-service/base-non-memory-caching.js @@ -5,6 +5,7 @@ const BaseService = require('./base') const { setCacheHeaders } = require('./cache-headers') const { makeSend } = require('./legacy-result-sender') const coalesceBadge = require('./coalesce-badge') +const { prepareRoute, namedParamsForMatch } = require('./route') // Badges are subject to two independent types of caching: in-memory and // downstream. @@ -26,9 +27,10 @@ module.exports = class NonMemoryCachingBaseService extends BaseService { static register({ camp }, serviceConfig) { const { cacheHeaders: cacheHeaderConfig } = serviceConfig const { _cacheLength: serviceDefaultCacheLengthSeconds } = this + const { regex, captureNames } = prepareRoute(this.route) - camp.route(this._regex, async (queryParams, match, end, ask) => { - const namedParams = this._namedParamsForMatch(match) + camp.route(regex, async (queryParams, match, end, ask) => { + const namedParams = namedParamsForMatch(captureNames, match, this) const serviceData = await this.invoke( {}, serviceConfig, diff --git a/core/base-service/base-static.js b/core/base-service/base-static.js index 8f27cde417a20..094040a935ed8 100644 --- a/core/base-service/base-static.js +++ b/core/base-service/base-static.js @@ -9,14 +9,16 @@ const { } = require('./cache-headers') const { makeSend } = require('./legacy-result-sender') const coalesceBadge = require('./coalesce-badge') +const { prepareRoute, namedParamsForMatch } = require('./route') module.exports = class BaseStaticService extends BaseService { static register({ camp }, serviceConfig) { const { profiling: { makeBadge: shouldProfileMakeBadge }, } = serviceConfig + const { regex, captureNames } = prepareRoute(this.route) - camp.route(this._regex, async (queryParams, match, end, ask) => { + camp.route(regex, async (queryParams, match, end, ask) => { analytics.noteRequest(queryParams, match) if (serverHasBeenUpSinceResourceCached(ask.req)) { @@ -26,7 +28,7 @@ module.exports = class BaseStaticService extends BaseService { return } - const namedParams = this._namedParamsForMatch(match) + const namedParams = namedParamsForMatch(captureNames, match, this) const serviceData = await this.invoke( {}, serviceConfig, diff --git a/core/base-service/base.js b/core/base-service/base.js index 92b1efb7952e2..547ea2d31b2b3 100644 --- a/core/base-service/base.js +++ b/core/base-service/base.js @@ -2,7 +2,6 @@ // See available emoji at http://emoji.muan.co/ const emojic = require('emojic') -const pathToRegexp = require('path-to-regexp') const Joi = require('joi') const { checkErrorResponse } = require('../../lib/error-helper') const { assertValidCategory } = require('../../services/categories') @@ -14,6 +13,12 @@ const { InvalidParameter, Deprecated, } = require('./errors') +const { + makeFullUrl, + assertValidRoute, + prepareRoute, + namedParamsForMatch, +} = require('./route') const { assertValidServiceDefinition } = require('./service-definitions') const trace = require('./trace') const { validateExample, transformExample } = require('./transform-example') @@ -150,13 +155,11 @@ class BaseService { return [] } - static _makeFullUrl(partialUrl) { - return `/${[this.route.base, partialUrl].filter(Boolean).join('/')}` - } - static validateDefinition() { assertValidCategory(this.category, `Category for ${this.name}`) + assertValidRoute(this.route, `Route for ${this.name}`) + Joi.assert( this.defaultBadgeData, defaultBadgeDataSchema, @@ -171,9 +174,9 @@ class BaseService { static getDefinition() { const { category, name, isDeprecated } = this - let format, pattern, queryParams + let base, format, pattern, queryParams try { - ;({ format, pattern, query: queryParams = [] } = this.route) + ;({ base, format, pattern, query: queryParams = [] } = this.route) } catch (e) { // Legacy services do not have a route. } @@ -184,7 +187,7 @@ class BaseService { let route if (pattern) { - route = { pattern: this._makeFullUrl(pattern), queryParams } + route = { pattern: makeFullUrl(base, pattern), queryParams } } else if (format) { route = { format, queryParams } } else { @@ -198,44 +201,6 @@ class BaseService { return result } - static get _regexFromPath() { - const { pattern } = this.route - const fullPattern = `${this._makeFullUrl( - pattern - )}.:ext(svg|png|gif|jpg|json)` - - const keys = [] - const regex = pathToRegexp(fullPattern, keys, { - strict: true, - sensitive: true, - }) - const capture = keys.map(item => item.name).slice(0, -1) - - return { regex, capture } - } - - static get _regex() { - const { pattern, format, capture } = this.route - if ( - pattern !== undefined && - (format !== undefined || capture !== undefined) - ) { - throw Error( - `Since the route for ${ - this.name - } includes a pattern, it should not include a format or capture` - ) - } else if (pattern !== undefined) { - return this._regexFromPath.regex - } else if (format !== undefined) { - return new RegExp( - `^${this._makeFullUrl(this.route.format)}\\.(svg|png|gif|jpg|json)$` - ) - } else { - throw Error(`The route for ${this.name} has neither pattern nor format`) - } - } - static get _cacheLength() { const cacheLengths = { build: 30, @@ -246,28 +211,6 @@ class BaseService { return cacheLengths[this.category] } - static _namedParamsForMatch(match) { - const { pattern, capture } = this.route - const names = pattern ? this._regexFromPath.capture : capture || [] - - // Assume the last match is the format, and drop match[0], which is the - // entire match. - const captures = match.slice(1, -1) - - if (names.length !== captures.length) { - throw new Error( - `Service ${this.name} declares incorrect number of capture groups ` + - `(expected ${names.length}, got ${captures.length})` - ) - } - - const result = {} - names.forEach((name, index) => { - result[name] = captures[index] - }) - return result - } - _handleError(error) { if (error instanceof NotFound || error instanceof InvalidParameter) { trace.logTrace('outbound', emojic.noGoodWoman, 'Handled error', error) @@ -344,12 +287,14 @@ class BaseService { static register({ camp, handleRequest, githubApiProvider }, serviceConfig) { const { cacheHeaders: cacheHeaderConfig, fetchLimitBytes } = serviceConfig + const { regex, captureNames } = prepareRoute(this.route) + camp.route( - this._regex, + regex, handleRequest(cacheHeaderConfig, { queryParams: this.route.queryParams, handler: async (queryParams, match, sendBadge, request) => { - const namedParams = this._namedParamsForMatch(match) + const namedParams = namedParamsForMatch(captureNames, match, this) const serviceData = await this.invoke( { sendAndCacheRequest: request.asPromise, diff --git a/core/base-service/base.spec.js b/core/base-service/base.spec.js index d6062b153a241..e82658746b5d0 100644 --- a/core/base-service/base.spec.js +++ b/core/base-service/base.spec.js @@ -2,7 +2,6 @@ const Joi = require('joi') const { expect } = require('chai') -const { test, given, forCases } = require('sazerac') const sinon = require('sinon') const trace = require('./trace') @@ -60,6 +59,7 @@ class DummyService extends BaseService { }, ] } + static get route() { return { base: 'foo', @@ -72,128 +72,6 @@ class DummyService extends BaseService { describe('BaseService', function() { const defaultConfig = { handleInternalErrors: false } - describe('URL pattern matching', function() { - context('A `pattern` with a named param is declared', function() { - const regexExec = str => DummyService._regex.exec(str) - const getNamedParamA = str => { - const [, namedParamA] = regexExec(str) - return namedParamA - } - const namedParams = str => { - const match = regexExec(str) - return DummyService._namedParamsForMatch(match) - } - - test(regexExec, () => { - forCases([ - given('/foo/bar.bar.bar.zip'), - given('/foo/bar/bar.svg'), - // This is a valid example with the wrong extension separator, to - // test that we only accept a `.`. - given('/foo/bar.bar.bar_svg'), - ]).expect(null) - }) - - test(getNamedParamA, () => { - forCases([ - given('/foo/bar.bar.bar.svg'), - given('/foo/bar.bar.bar.png'), - given('/foo/bar.bar.bar.gif'), - given('/foo/bar.bar.bar.jpg'), - given('/foo/bar.bar.bar.json'), - ]).expect('bar.bar.bar') - }) - - test(namedParams, () => { - forCases([ - given('/foo/bar.bar.bar.svg'), - given('/foo/bar.bar.bar.png'), - given('/foo/bar.bar.bar.gif'), - given('/foo/bar.bar.bar.jpg'), - given('/foo/bar.bar.bar.json'), - ]).expect({ namedParamA: 'bar.bar.bar' }) - }) - }) - - context('A `format` with a named param is declared', function() { - class ServiceWithFormat extends BaseService { - static get route() { - return { - base: 'foo', - format: '([^/]+)', - capture: ['namedParamA'], - } - } - } - - const regexExec = str => ServiceWithFormat._regex.exec(str) - const getNamedParamA = str => { - const [, namedParamA] = regexExec(str) - return namedParamA - } - const namedParams = str => { - const match = regexExec(str) - return ServiceWithFormat._namedParamsForMatch(match) - } - - test(regexExec, () => { - forCases([ - given('/foo/bar.bar.bar.zip'), - given('/foo/bar/bar.svg'), - // This is a valid example with the wrong extension separator, to - // test that we only accept a `.`. - given('/foo/bar.bar.bar_svg'), - ]).expect(null) - }) - - test(getNamedParamA, () => { - forCases([ - given('/foo/bar.bar.bar.svg'), - given('/foo/bar.bar.bar.png'), - given('/foo/bar.bar.bar.gif'), - given('/foo/bar.bar.bar.jpg'), - given('/foo/bar.bar.bar.json'), - ]).expect('bar.bar.bar') - }) - - test(namedParams, () => { - forCases([ - given('/foo/bar.bar.bar.svg'), - given('/foo/bar.bar.bar.png'), - given('/foo/bar.bar.bar.gif'), - given('/foo/bar.bar.bar.jpg'), - given('/foo/bar.bar.bar.json'), - ]).expect({ namedParamA: 'bar.bar.bar' }) - }) - }) - - context('No named params are declared', function() { - class ServiceWithZeroNamedParams extends BaseService { - static get route() { - return { - base: 'foo', - format: '(?:[^/]+)', - } - } - } - - const namedParams = str => { - const match = ServiceWithZeroNamedParams._regex.exec(str) - return ServiceWithZeroNamedParams._namedParamsForMatch(match) - } - - test(namedParams, () => { - forCases([ - given('/foo/bar.bar.bar.svg'), - given('/foo/bar.bar.bar.png'), - given('/foo/bar.bar.bar.gif'), - given('/foo/bar.bar.bar.jpg'), - given('/foo/bar.bar.bar.json'), - ]).expect({}) - }) - }) - }) - it('Invokes the handler as expected', async function() { expect( await DummyService.invoke( diff --git a/core/base-service/route.js b/core/base-service/route.js new file mode 100644 index 0000000000000..dc61c4010eeb3 --- /dev/null +++ b/core/base-service/route.js @@ -0,0 +1,75 @@ +'use strict' + +const Joi = require('joi') +const pathToRegexp = require('path-to-regexp') + +function makeFullUrl(base, partialUrl) { + return `/${[base, partialUrl].filter(Boolean).join('/')}` +} + +const routeSchema = Joi.object({ + base: Joi.string().allow(''), + pattern: Joi.string().allow(''), + format: Joi.string(), + capture: Joi.alternatives().when('format', { + is: Joi.string().required(), + then: Joi.array().items(Joi.string().required()), + }), + queryParams: Joi.array().items(Joi.string().required()), +}) + .xor('pattern', 'format') + .required() + +function assertValidRoute(route, message = undefined) { + Joi.assert(route, routeSchema, message) +} + +function prepareRoute({ base, pattern, format, capture }) { + let regex, captureNames + if (pattern === undefined) { + regex = new RegExp( + `^${makeFullUrl(base, format)}\\.(svg|png|gif|jpg|json)$` + ) + captureNames = capture || [] + } else { + const fullPattern = `${makeFullUrl( + base, + pattern + )}.:ext(svg|png|gif|jpg|json)` + const keys = [] + regex = pathToRegexp(fullPattern, keys, { + strict: true, + sensitive: true, + }) + captureNames = keys.map(item => item.name).slice(0, -1) + } + return { regex, captureNames } +} + +function namedParamsForMatch(captureNames = [], match, ServiceClass) { + // Assume the last match is the format, and drop match[0], which is the + // entire match. + const captures = match.slice(1, -1) + + if (captureNames.length !== captures.length) { + throw new Error( + `Service ${ + ServiceClass.name + } declares incorrect number of capture groups ` + + `(expected ${captureNames.length}, got ${captures.length})` + ) + } + + const result = {} + captureNames.forEach((name, index) => { + result[name] = captures[index] + }) + return result +} + +module.exports = { + makeFullUrl, + assertValidRoute, + prepareRoute, + namedParamsForMatch, +} diff --git a/core/base-service/route.spec.js b/core/base-service/route.spec.js new file mode 100644 index 0000000000000..50f19b4480e59 --- /dev/null +++ b/core/base-service/route.spec.js @@ -0,0 +1,87 @@ +'use strict' + +const { test, given, forCases } = require('sazerac') +const { prepareRoute, namedParamsForMatch } = require('./route') + +describe('Route helpers', function() { + context('A `pattern` with a named param is declared', function() { + const { regex, captureNames } = prepareRoute({ + base: 'foo', + pattern: ':namedParamA', + queryParams: ['queryParamA'], + }) + + const regexExec = str => regex.exec(str) + test(regexExec, () => { + forCases([ + given('/foo/bar.bar.bar.zip'), + given('/foo/bar/bar.svg'), + // This is a valid example with the wrong extension separator, to + // test that we only accept a `.`. + given('/foo/bar.bar.bar_svg'), + ]).expect(null) + }) + + const namedParams = str => + namedParamsForMatch(captureNames, regex.exec(str)) + test(namedParams, () => { + forCases([ + given('/foo/bar.bar.bar.svg'), + given('/foo/bar.bar.bar.png'), + given('/foo/bar.bar.bar.gif'), + given('/foo/bar.bar.bar.jpg'), + given('/foo/bar.bar.bar.json'), + ]).expect({ namedParamA: 'bar.bar.bar' }) + }) + }) + + context('A `format` with a named param is declared', function() { + const { regex, captureNames } = prepareRoute({ + base: 'foo', + format: '([^/]+)', + capture: ['namedParamA'], + }) + + const regexExec = str => regex.exec(str) + test(regexExec, () => { + forCases([ + given('/foo/bar.bar.bar.zip'), + given('/foo/bar/bar.svg'), + // This is a valid example with the wrong extension separator, to + // test that we only accept a `.`. + given('/foo/bar.bar.bar_svg'), + ]).expect(null) + }) + + const namedParams = str => + namedParamsForMatch(captureNames, regex.exec(str)) + test(namedParams, () => { + forCases([ + given('/foo/bar.bar.bar.svg'), + given('/foo/bar.bar.bar.png'), + given('/foo/bar.bar.bar.gif'), + given('/foo/bar.bar.bar.jpg'), + given('/foo/bar.bar.bar.json'), + ]).expect({ namedParamA: 'bar.bar.bar' }) + }) + }) + + context('No named params are declared', function() { + const { regex, captureNames } = prepareRoute({ + base: 'foo', + format: '(?:[^/]+)', + }) + + const namedParams = str => + namedParamsForMatch(captureNames, regex.exec(str)) + test(namedParams, () => { + forCases([ + given('/foo/bar.bar.bar.svg'), + given('/foo/bar.bar.bar.png'), + given('/foo/bar.bar.bar.gif'), + given('/foo/bar.bar.bar.jpg'), + given('/foo/bar.bar.bar.json'), + ]).expect({}) + }) + }) +}) diff --git a/core/base-service/transform-example.js b/core/base-service/transform-example.js index 1f803eb19d097..bc07357fcfa5a 100644 --- a/core/base-service/transform-example.js +++ b/core/base-service/transform-example.js @@ -3,6 +3,7 @@ const Joi = require('joi') const pathToRegexp = require('path-to-regexp') const coalesceBadge = require('./coalesce-badge') +const { makeFullUrl } = require('./route') const optionalObjectOfKeyValues = Joi.object().pattern( /./, @@ -127,13 +128,16 @@ function transformExample(inExample, index, ServiceClass) { let example if (namedParams) { example = { - pattern: ServiceClass._makeFullUrl(pattern || ServiceClass.route.pattern), + pattern: makeFullUrl( + ServiceClass.route.base, + pattern || ServiceClass.route.pattern + ), namedParams, queryParams, } } else { example = { - path: ServiceClass._makeFullUrl(previewUrl), + path: makeFullUrl(ServiceClass.route.base, previewUrl), queryParams, } } @@ -152,7 +156,7 @@ function transformExample(inExample, index, ServiceClass) { preview = { label, message: `${message}`, color } } else { preview = { - path: ServiceClass._makeFullUrl(previewUrl), + path: makeFullUrl(ServiceClass.route.base, previewUrl), queryParams, } } diff --git a/services/amo/amo.service.js b/services/amo/amo.service.js index ef15bd8f4ca05..44fe85a51adc4 100644 --- a/services/amo/amo.service.js +++ b/services/amo/amo.service.js @@ -85,6 +85,7 @@ class AmoRating extends LegacyService { static get route() { return { base: 'amo', + pattern: '', } } diff --git a/services/bitrise/bitrise.service.js b/services/bitrise/bitrise.service.js index 36ee69350c811..abe3c64c9396b 100644 --- a/services/bitrise/bitrise.service.js +++ b/services/bitrise/bitrise.service.js @@ -17,6 +17,7 @@ module.exports = class Bitrise extends LegacyService { static get route() { return { base: 'bitrise', + pattern: ':appId/:branch', } } @@ -24,7 +25,6 @@ module.exports = class Bitrise extends LegacyService { return [ { title: 'Bitrise', - pattern: ':appId/:branch', namedParams: { appId: 'cde737473028420d', branch: 'master' }, queryParams: { token: 'GCIdEzacE4GW32jLVrZb7A' }, staticPreview: { diff --git a/services/bugzilla/bugzilla.service.js b/services/bugzilla/bugzilla.service.js index 10c7b1a09731b..06d602ba541a7 100644 --- a/services/bugzilla/bugzilla.service.js +++ b/services/bugzilla/bugzilla.service.js @@ -24,6 +24,7 @@ module.exports = class Bugzilla extends LegacyService { static get route() { return { base: 'bugzilla', + pattern: ':bugNumber', } } @@ -31,7 +32,6 @@ module.exports = class Bugzilla extends LegacyService { return [ { title: 'Bugzilla bug status', - pattern: ':bugNumber', namedParams: { bugNumber: '996038' }, staticPreview: { label: 'bug 996038', diff --git a/services/buildkite/buildkite.service.js b/services/buildkite/buildkite.service.js index 4e30a44e64cf2..e2243c09a3835 100644 --- a/services/buildkite/buildkite.service.js +++ b/services/buildkite/buildkite.service.js @@ -18,6 +18,7 @@ module.exports = class Buildkite extends LegacyService { static get route() { return { base: 'buildkite', + pattern: '', } } diff --git a/services/bundlephobia/bundlephobia.service.js b/services/bundlephobia/bundlephobia.service.js index f27be57b54dc4..1bc78d49971fe 100644 --- a/services/bundlephobia/bundlephobia.service.js +++ b/services/bundlephobia/bundlephobia.service.js @@ -20,6 +20,7 @@ module.exports = class Bundlephobia extends LegacyService { static get route() { return { base: 'bundlephobia', + pattern: '', } } diff --git a/services/chrome-web-store/chrome-web-store.service.js b/services/chrome-web-store/chrome-web-store.service.js index 7f8e18c300252..f7ec547c989c9 100644 --- a/services/chrome-web-store/chrome-web-store.service.js +++ b/services/chrome-web-store/chrome-web-store.service.js @@ -18,7 +18,6 @@ const { const commonExample = { title: 'Chrome Web Store', - pattern: ':storeId', namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' }, } @@ -30,6 +29,7 @@ class ChromeWebStoreDownloads extends LegacyService { static get route() { return { base: 'chrome-web-store/users', + pattern: ':storeId', } } @@ -53,6 +53,7 @@ class ChromeWebStoreVersion extends LegacyService { static get route() { return { base: 'chrome-web-store/v', + pattern: ':storeId', } } @@ -80,6 +81,7 @@ class ChromeWebStorePrice extends LegacyService { static get route() { return { base: 'chrome-web-store/price', + pattern: ':storeId', } } @@ -103,6 +105,7 @@ class ChromeWebStoreRating extends LegacyService { static get route() { return { base: 'chrome-web-store', + pattern: ':storeId', } } diff --git a/services/cocoapods/cocoapods-metrics.service.js b/services/cocoapods/cocoapods-metrics.service.js index 28fc5b7d92c42..9fb9506aa979f 100644 --- a/services/cocoapods/cocoapods-metrics.service.js +++ b/services/cocoapods/cocoapods-metrics.service.js @@ -21,6 +21,7 @@ module.exports = class CocoapodsMetrics extends LegacyService { static get route() { return { base: 'cocoapods/metrics/doc-percent', + pattern: ':spec', } } @@ -28,7 +29,6 @@ module.exports = class CocoapodsMetrics extends LegacyService { return [ { title: 'Cocoapods doc percentage', - pattern: ':spec', namedParams: { spec: 'AFNetworking' }, staticPreview: { label: 'docs', message: '94%', color: 'green' }, }, diff --git a/services/cocoapods/cocoapods-version.service.js b/services/cocoapods/cocoapods-version.service.js index cf4928d58ad57..3e0a27a4c22ed 100644 --- a/services/cocoapods/cocoapods-version.service.js +++ b/services/cocoapods/cocoapods-version.service.js @@ -10,6 +10,7 @@ module.exports = class CocoapodsVersion extends LegacyService { static get route() { return { base: 'cocoapods/v', + pattern: ':spec', } } diff --git a/services/codeclimate/codeclimate.service.js b/services/codeclimate/codeclimate.service.js index 304acfbba73ed..d1f0c0e7b18af 100644 --- a/services/codeclimate/codeclimate.service.js +++ b/services/codeclimate/codeclimate.service.js @@ -12,6 +12,7 @@ class CodeclimateCoverage extends LegacyService { static get route() { return { base: 'codeclimate', + pattern: ':which(coverage|coverage-letter)/:userRepo*', } } @@ -57,6 +58,8 @@ class Codeclimate extends LegacyService { static get route() { return { base: 'codeclimate', + pattern: + ':which(issues|maintainability|maintainability-percentage|tech-debt)/:userRepo*', } } diff --git a/services/codecov/codecov.service.js b/services/codecov/codecov.service.js index 8068ecf20b188..6c5e19ffb87fd 100644 --- a/services/codecov/codecov.service.js +++ b/services/codecov/codecov.service.js @@ -21,6 +21,7 @@ module.exports = class Codecov extends LegacyService { static get route() { return { base: 'codecov/c', + pattern: '', } } diff --git a/services/codeship/codeship.service.js b/services/codeship/codeship.service.js index e1e884f923b16..dd31d1e5ac436 100644 --- a/services/codeship/codeship.service.js +++ b/services/codeship/codeship.service.js @@ -17,6 +17,7 @@ module.exports = class Codeship extends LegacyService { static get route() { return { base: 'codeship', + pattern: '', } } diff --git a/services/continuousphp/continuousphp.service.js b/services/continuousphp/continuousphp.service.js index 5ab44ad6db767..149017823cd30 100644 --- a/services/continuousphp/continuousphp.service.js +++ b/services/continuousphp/continuousphp.service.js @@ -15,7 +15,10 @@ module.exports = class ContinuousPhp extends LegacyService { } static get route() { - return { base: 'continuousphp' } + return { + base: 'continuousphp', + pattern: '', + } } static get examples() { diff --git a/services/cookbook/cookbook.service.js b/services/cookbook/cookbook.service.js index 6d1bd699ae229..b3d195fdbb903 100644 --- a/services/cookbook/cookbook.service.js +++ b/services/cookbook/cookbook.service.js @@ -19,14 +19,16 @@ module.exports = class Cookbook extends LegacyService { } static get route() { - return { base: 'cookbook/v' } + return { + base: 'cookbook/v', + pattern: ':cookbook', + } } static get examples() { return [ { title: 'Chef cookbook', - pattern: ':cookbook', namedParams: { cookbook: 'chef-sugar' }, staticPreview: { label: 'cookbook', message: 'v5.0.0', color: 'blue' }, }, diff --git a/services/coveralls/coveralls.service.js b/services/coveralls/coveralls.service.js index e861bef126313..8f21f44f525d3 100644 --- a/services/coveralls/coveralls.service.js +++ b/services/coveralls/coveralls.service.js @@ -20,6 +20,7 @@ module.exports = class Coveralls extends LegacyService { static get route() { return { base: 'coveralls', + pattern: '', } } diff --git a/services/david/david.service.js b/services/david/david.service.js index 3b92c1bd6ab55..08819106fd87b 100644 --- a/services/david/david.service.js +++ b/services/david/david.service.js @@ -17,6 +17,7 @@ module.exports = class David extends LegacyService { static get route() { return { base: 'david', + pattern: '', } } diff --git a/services/github/github-commit-activity.service.js b/services/github/github-commit-activity.service.js index 9574cde1d579f..6eab06e51a0a5 100644 --- a/services/github/github-commit-activity.service.js +++ b/services/github/github-commit-activity.service.js @@ -23,6 +23,7 @@ module.exports = class GithubCommitActivity extends LegacyService { static get route() { return { base: 'github/commit-activity', + pattern: ':interval(y|4w|w)/:user/:repo', } } diff --git a/services/github/github-commit-status.service.js b/services/github/github-commit-status.service.js index e02be790bb738..7a5cbdabf5a38 100644 --- a/services/github/github-commit-status.service.js +++ b/services/github/github-commit-status.service.js @@ -21,6 +21,7 @@ module.exports = class GithubCommitStatus extends LegacyService { static get route() { return { base: 'github/commit-status', + pattern: ':user/:repo/:branch/:commit', } } @@ -28,7 +29,6 @@ module.exports = class GithubCommitStatus extends LegacyService { return [ { title: 'GitHub commit merge status', - pattern: ':user/:repo/:branch/:commit', namedParams: { user: 'badges', repo: 'shields', diff --git a/services/github/github-downloads.service.js b/services/github/github-downloads.service.js index 6eea815f874b9..2709d927bb836 100644 --- a/services/github/github-downloads.service.js +++ b/services/github/github-downloads.service.js @@ -23,6 +23,7 @@ module.exports = class GithubDownloads extends LegacyService { static get route() { return { base: 'github', + pattern: '', } } diff --git a/services/github/github-followers.service.js b/services/github/github-followers.service.js index 306d022656fa1..327244941126a 100644 --- a/services/github/github-followers.service.js +++ b/services/github/github-followers.service.js @@ -22,6 +22,7 @@ module.exports = class GithubFollowers extends LegacyService { static get route() { return { base: 'github/followers', + pattern: ':user', } } @@ -29,7 +30,6 @@ module.exports = class GithubFollowers extends LegacyService { return [ { title: 'GitHub followers', - pattern: ':user', previewUrl: 'espadrine', // https://github.com/badges/shields/issues/2479 // namedParams: { diff --git a/services/github/github-forks.service.js b/services/github/github-forks.service.js index 2993b8d3fc65b..32995b543784f 100644 --- a/services/github/github-forks.service.js +++ b/services/github/github-forks.service.js @@ -22,6 +22,7 @@ module.exports = class GithubForks extends LegacyService { static get route() { return { base: 'github/forks', + pattern: ':user/:repo', } } diff --git a/services/github/github-stars.service.js b/services/github/github-stars.service.js index 8601538cb83a1..944a21a4cd84e 100644 --- a/services/github/github-stars.service.js +++ b/services/github/github-stars.service.js @@ -23,6 +23,7 @@ module.exports = class GithubStars extends LegacyService { static get route() { return { base: 'github/stars', + pattern: ':user/:repo', } } diff --git a/services/github/github-watchers.service.js b/services/github/github-watchers.service.js index ddfa51e180602..680045f6bd361 100644 --- a/services/github/github-watchers.service.js +++ b/services/github/github-watchers.service.js @@ -22,6 +22,7 @@ module.exports = class GithubWatchers extends LegacyService { static get route() { return { base: 'github/watchers', + pattern: ':user/:repo', } } diff --git a/services/jenkins/jenkins-plugin-version.service.js b/services/jenkins/jenkins-plugin-version.service.js index 5bfabb8c073ea..faf0bf7c3c13b 100644 --- a/services/jenkins/jenkins-plugin-version.service.js +++ b/services/jenkins/jenkins-plugin-version.service.js @@ -12,7 +12,7 @@ const { version: versionColor } = require('../../lib/color-formatters') // https://github.com/badges/shields/blob/master/doc/rewriting-services.md // // Do not base new services on this code. -module.exports = class JenkinsPlugin extends LegacyService { +module.exports = class JenkinsPluginVersion extends LegacyService { static get category() { return 'version' } @@ -20,6 +20,7 @@ module.exports = class JenkinsPlugin extends LegacyService { static get route() { return { base: 'jenkins/plugin/v', + pattern: ':plugin', } } @@ -27,7 +28,6 @@ module.exports = class JenkinsPlugin extends LegacyService { return [ { title: 'Jenkins Plugins', - pattern: ':plugin', namedParams: { plugin: 'blueocean', }, diff --git a/services/legacy-service.js b/services/legacy-service.js index b57969722a2c7..97b269270821d 100644 --- a/services/legacy-service.js +++ b/services/legacy-service.js @@ -12,6 +12,11 @@ const { BaseService } = require('.') // BaseJsonService. Refer to the tutorial: // https://github.com/badges/shields/blob/master/doc/TUTORIAL.md class LegacyService extends BaseService { + // Provide a placeholder for services which do not define a route. + static get route() { + return { pattern: '' } + } + static registerLegacyRouteHandler({ camp, cache, githubApiProvider }) { throw Error(`registerLegacyRouteHandler() not implemented for ${this.name}`) } diff --git a/services/lgtm/lgtm-alerts.service.js b/services/lgtm/lgtm-alerts.service.js index 4ee05f8e195bf..2d6aa44472ba4 100644 --- a/services/lgtm/lgtm-alerts.service.js +++ b/services/lgtm/lgtm-alerts.service.js @@ -19,6 +19,7 @@ module.exports = class LgtmAlerts extends LegacyService { static get route() { return { base: 'lgtm/alerts', + pattern: '', } } diff --git a/services/lgtm/lgtm-grade.service.js b/services/lgtm/lgtm-grade.service.js index 15bd756a10020..6827311ec9664 100644 --- a/services/lgtm/lgtm-grade.service.js +++ b/services/lgtm/lgtm-grade.service.js @@ -18,6 +18,7 @@ module.exports = class LgtmGrade extends LegacyService { static get route() { return { base: 'lgtm/grade', + pattern: '', } } diff --git a/services/liberapay/liberapay.service.js b/services/liberapay/liberapay.service.js index 96c19e43f8d32..e3246e3ac8e6d 100644 --- a/services/liberapay/liberapay.service.js +++ b/services/liberapay/liberapay.service.js @@ -20,6 +20,7 @@ module.exports = class Liberapay extends LegacyService { static get route() { return { base: 'liberapay', + pattern: '', } } diff --git a/services/librariesio/librariesio-dependencies.service.js b/services/librariesio/librariesio-dependencies.service.js index def778e5c338f..5b5e259ec13e7 100644 --- a/services/librariesio/librariesio-dependencies.service.js +++ b/services/librariesio/librariesio-dependencies.service.js @@ -18,6 +18,7 @@ module.exports = class LibrariesioDependencies extends LegacyService { static get route() { return { base: 'librariesio', + pattern: '', } } diff --git a/services/luarocks/luarocks.service.js b/services/luarocks/luarocks.service.js index b8867acd784e1..c5f4a414d02ba 100644 --- a/services/luarocks/luarocks.service.js +++ b/services/luarocks/luarocks.service.js @@ -22,6 +22,7 @@ module.exports = class Luarocks extends LegacyService { static get route() { return { base: 'luarocks/v', + pattern: ':user/:moduleName', } } @@ -29,7 +30,6 @@ module.exports = class Luarocks extends LegacyService { return [ { title: 'LuaRocks', - pattern: ':user/:moduleName', namedParams: { user: 'mpeterv', moduleName: 'luacheck', diff --git a/services/maven-central/maven-central.service.js b/services/maven-central/maven-central.service.js index 2539a334075f5..cdf406e9a3b9e 100644 --- a/services/maven-central/maven-central.service.js +++ b/services/maven-central/maven-central.service.js @@ -20,6 +20,7 @@ module.exports = class MavenCentral extends LegacyService { static get route() { return { base: 'maven-central/v', + pattern: '', } } diff --git a/services/maven-metadata/maven-metadata.service.js b/services/maven-metadata/maven-metadata.service.js index eb22f2acf7f61..419b136494b05 100644 --- a/services/maven-metadata/maven-metadata.service.js +++ b/services/maven-metadata/maven-metadata.service.js @@ -20,6 +20,7 @@ module.exports = class MavenMetadata extends LegacyService { static get route() { return { base: 'maven-metadata/v', + pattern: '', } } diff --git a/services/microbadger/microbadger.service.js b/services/microbadger/microbadger.service.js index c3cea3e1f8a07..17c9033a352ee 100644 --- a/services/microbadger/microbadger.service.js +++ b/services/microbadger/microbadger.service.js @@ -21,6 +21,7 @@ module.exports = class MicroBadger extends LegacyService { static get route() { return { base: 'microbadger', + pattern: '', } } diff --git a/services/packagist/packagist-downloads.service.js b/services/packagist/packagist-downloads.service.js index 0bb7a62dce8e1..7ecb15116da15 100644 --- a/services/packagist/packagist-downloads.service.js +++ b/services/packagist/packagist-downloads.service.js @@ -21,6 +21,7 @@ module.exports = class PackagistDownloads extends LegacyService { static get route() { return { base: 'packagist', + pattern: ':interval(dm|dd|dt)/:user/:repo', } } diff --git a/services/packagist/packagist-php-version.service.js b/services/packagist/packagist-php-version.service.js index 5206187e2dec1..4cd89f3cad3eb 100644 --- a/services/packagist/packagist-php-version.service.js +++ b/services/packagist/packagist-php-version.service.js @@ -18,6 +18,7 @@ module.exports = class PackagistPhpVersion extends LegacyService { static get route() { return { base: 'packagist/php-v', + pattern: '', } } diff --git a/services/packagist/packagist-version.service.js b/services/packagist/packagist-version.service.js index 1ce130a0c9481..30182737da797 100644 --- a/services/packagist/packagist-version.service.js +++ b/services/packagist/packagist-version.service.js @@ -24,6 +24,7 @@ module.exports = class PackagistVersion extends LegacyService { static get route() { return { base: 'packagist', + pattern: ':which(v|vpre)/:user/:repo', } } diff --git a/services/php-eye/php-eye-hhvm.service.js b/services/php-eye/php-eye-hhvm.service.js index 042028083d1a1..7e3ae2f384f32 100644 --- a/services/php-eye/php-eye-hhvm.service.js +++ b/services/php-eye/php-eye-hhvm.service.js @@ -21,6 +21,7 @@ module.exports = class PhpeyeHhvm extends LegacyService { static get route() { return { base: 'hhvm', + pattern: '', } } diff --git a/services/php-eye/php-eye-php-version.service.js b/services/php-eye/php-eye-php-version.service.js index 089d2418943fe..33740da0c558c 100644 --- a/services/php-eye/php-eye-php-version.service.js +++ b/services/php-eye/php-eye-php-version.service.js @@ -22,6 +22,7 @@ module.exports = class PhpEyePhpVersion extends LegacyService { static get route() { return { base: 'php-eye', + pattern: ':user/:repo', } } diff --git a/services/puppetforge/puppetforge-modules.service.js b/services/puppetforge/puppetforge-modules.service.js index 54d8bc5d3319e..99fae097fbb9a 100644 --- a/services/puppetforge/puppetforge-modules.service.js +++ b/services/puppetforge/puppetforge-modules.service.js @@ -20,6 +20,7 @@ class PuppetforgeModuleVersion extends LegacyService { static get route() { return { base: 'puppetforge/v', + pattern: ':user/:moduleName', } } @@ -43,6 +44,7 @@ class PuppetforgeModulePdkVersion extends LegacyService { static get route() { return { base: 'puppetforge/pdk-version', + pattern: ':user/:moduleName', } } @@ -50,7 +52,6 @@ class PuppetforgeModulePdkVersion extends LegacyService { return [ { title: 'Puppet Forge – PDK version', - pattern: ':user/:moduleName', namedParams: { user: 'tragiccode', moduleName: 'azure_key_vault', @@ -75,6 +76,7 @@ class PuppetforgeModuleDownloads extends LegacyService { static get route() { return { base: 'puppetforge/dt', + pattern: ':user/:moduleName', } } @@ -98,6 +100,7 @@ class PuppetforgeModuleEndorsement extends LegacyService { static get route() { return { base: 'puppetforge/e', + pattern: ':user/:moduleName', } } @@ -121,6 +124,7 @@ class PuppetforgeModuleFeedback extends LegacyService { static get route() { return { base: 'puppetforge/f', + pattern: ':user/:moduleName', } } diff --git a/services/puppetforge/puppetforge-users.service.js b/services/puppetforge/puppetforge-users.service.js index 1c2df1fda74b5..61aa550ae81f0 100644 --- a/services/puppetforge/puppetforge-users.service.js +++ b/services/puppetforge/puppetforge-users.service.js @@ -16,6 +16,7 @@ class PuppetforgeUserReleases extends LegacyService { static get route() { return { base: 'puppetforge/rc', + pattern: ':user', } } @@ -39,6 +40,7 @@ class PuppetforgeUserModules extends LegacyService { static get route() { return { base: 'puppetforge/mc', + pattern: ':user', } } diff --git a/services/scrutinizer/scrutinizer.service.js b/services/scrutinizer/scrutinizer.service.js index f9c7d48d44c28..0e79a210d6b26 100644 --- a/services/scrutinizer/scrutinizer.service.js +++ b/services/scrutinizer/scrutinizer.service.js @@ -15,6 +15,7 @@ class ScrutinizerBuild extends LegacyService { static get route() { return { base: 'scrutinizer', + pattern: '', } } @@ -38,6 +39,7 @@ class ScrutinizerCoverage extends LegacyService { static get route() { return { base: 'scrutinizer', + pattern: '', } } @@ -71,6 +73,7 @@ class Scrutinizer extends LegacyService { static get route() { return { base: 'scrutinizer', + pattern: '', } } diff --git a/services/sonarqube/sonarqube.service.js b/services/sonarqube/sonarqube.service.js index 1eadf1a4248dc..c9bee47990250 100644 --- a/services/sonarqube/sonarqube.service.js +++ b/services/sonarqube/sonarqube.service.js @@ -16,6 +16,7 @@ class SonarqubeCoverage extends LegacyService { static get route() { return { base: 'sonar', + pattern: '', } } @@ -51,6 +52,7 @@ class Sonarqube extends LegacyService { static get route() { return { base: 'sonar', + pattern: '', } } diff --git a/services/twitter/twitter.service.js b/services/twitter/twitter.service.js index 67f23793fffd0..71ed50e966f11 100644 --- a/services/twitter/twitter.service.js +++ b/services/twitter/twitter.service.js @@ -19,6 +19,7 @@ class TwitterUrl extends LegacyService { static get route() { return { base: 'twitter/url', + pattern: '', } } @@ -73,6 +74,7 @@ class TwitterFollow extends LegacyService { static get route() { return { base: 'twitter/follow', + pattern: ':user', } } diff --git a/services/vaadin-directory/vaadin-directory.service.js b/services/vaadin-directory/vaadin-directory.service.js index 12831328d7adf..01303c6f42a7b 100644 --- a/services/vaadin-directory/vaadin-directory.service.js +++ b/services/vaadin-directory/vaadin-directory.service.js @@ -20,6 +20,7 @@ class VaadinDirectoryRating extends LegacyService { static get route() { return { base: 'vaadin-directory', + pattern: ':which(rating|stars|rating-count)/:packageName', } } diff --git a/services/waffle/waffle.service.js b/services/waffle/waffle.service.js index 24de8e0018317..1e7dc409e066e 100644 --- a/services/waffle/waffle.service.js +++ b/services/waffle/waffle.service.js @@ -21,6 +21,7 @@ module.exports = class Waffle extends LegacyService { static get route() { return { base: 'waffle/label', + pattern: ':user/:repo/:query', } } @@ -28,7 +29,6 @@ module.exports = class Waffle extends LegacyService { return [ { title: 'Waffle.io', - pattern: ':user/:repo/:query', namedParams: { user: 'evancohen', repo: 'smart-mirror', diff --git a/services/website/website.service.js b/services/website/website.service.js index d3f592e74c95b..81167d3c8e115 100644 --- a/services/website/website.service.js +++ b/services/website/website.service.js @@ -88,6 +88,7 @@ module.exports = class Website extends LegacyService { static get route() { return { base: '', + pattern: '', } } diff --git a/test-fixtures/valid-array.fixture.js b/test-fixtures/valid-array.fixture.js index 42bc59e011de8..0d96853969df0 100644 --- a/test-fixtures/valid-array.fixture.js +++ b/test-fixtures/valid-array.fixture.js @@ -7,11 +7,19 @@ class GoodServiceOne extends BaseJsonService { static get category() { return 'build' } + + static get route() { + return { pattern: 'good/one' } + } } class GoodServiceTwo extends LegacyService { static get category() { return 'build' } + + static get route() { + return { pattern: 'good/two' } + } } module.exports = [GoodServiceOne, GoodServiceTwo] diff --git a/test-fixtures/valid-class.fixture.js b/test-fixtures/valid-class.fixture.js index 9f4bf0fd46e1d..248234ee8599b 100644 --- a/test-fixtures/valid-class.fixture.js +++ b/test-fixtures/valid-class.fixture.js @@ -6,6 +6,10 @@ class GoodService extends BaseJsonService { static get category() { return 'build' } + + static get route() { + return { pattern: 'good' } + } } module.exports = GoodService diff --git a/test-fixtures/valid-object.fixture.js b/test-fixtures/valid-object.fixture.js index 7c5353d3c45ef..b022fa1d22ea6 100644 --- a/test-fixtures/valid-object.fixture.js +++ b/test-fixtures/valid-object.fixture.js @@ -7,11 +7,19 @@ class GoodServiceOne extends BaseJsonService { static get category() { return 'build' } + + static get route() { + return { pattern: 'good/one' } + } } class GoodServiceTwo extends LegacyService { static get category() { return 'build' } + + static get route() { + return { pattern: 'good/two' } + } } module.exports = { GoodServiceOne, GoodServiceTwo }