Skip to content

Commit

Permalink
BaseService terminology: Rename url to route (badges#2278)
Browse files Browse the repository at this point in the history
The term “url” is overloaded in services, to refer to the Shields route and also the API URL. Calling the Shields URL a “route” is on the whole more descriptive, and makes it clearer and more obvious which one of these we’re talking about. It’s a small thing, though seems like an improvement.

We have a few functions called `buildUrl`. I’ve renamed them to `buildRoute` when they refer to routes, and left them as `buildUrl` when they refer to API URLs.

I included a minor style tweak and some formatting cleanup in `TUTORIAL.md`.
  • Loading branch information
paulmelnikow authored Nov 9, 2018
1 parent c0f9a88 commit 02ec19f
Show file tree
Hide file tree
Showing 147 changed files with 225 additions and 224 deletions.
23 changes: 11 additions & 12 deletions doc/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const BaseService = require('../base') // (2)

module.exports = class Example extends BaseService { // (3)

static get url() { // (4)
static get route() { // (4)
return {
base: 'example',
pattern: ':text',
Expand All @@ -121,14 +121,14 @@ Description of the code:
1. We declare strict mode at the start of each file. This prevents certain classes of error such as undeclared variables.
2. Our service badge class will extend `BaseService` so we need to require it. We declare variables with `const` and `let` in preference to `var`.
3. Our module must export a class which extends `BaseService`
4. `url()` declares a route. We declare getters as `static`.
4. `route()` declares a route. We declare getters as `static`.
* `base` defines the static part of the route.
* `pattern` defines the variable part of the route. It can include any
number of named parameters. These are converted into
regular expressions by [`path-to-regexp`][path-to-regexp].
5. All badges must implement the `async handle()` function. This is called to invoke our code. Note that the signature of `handle()` will match the capturing group defined in `url()` Because we're capturing a single variable called `text` our function signature is `async handle({ text })`. Although in this simple case, we aren't performing any asynchronous calls, `handle()` would usually spend some time blocked on I/O. We use the `async`/`await` pattern for asynchronous code. Our `handle()` function returns an object with 3 properties:
5. All badges must implement the `async handle()` function. This is called to invoke our code. Note that the signature of `handle()` will match the capturing group defined in `route()` Because we're capturing a single variable called `text` our function signature is `async handle({ text })`. Although in this simple case, we aren't performing any asynchronous calls, `handle()` would usually spend some time blocked on I/O. We use the `async`/`await` pattern for asynchronous code. Our `handle()` function returns an object with 3 properties:
* `label`: the text on the left side of the badge
* `message`: the text on the right side of the badge - here we are passing through the parameter we captured in the URL regex
* `message`: the text on the right side of the badge - here we are passing through the parameter we captured in the route regex
* `color`: the background color of the right side of the badge

The process of turning this object into an image is handled automatically by the `BaseService` class.
Expand Down Expand Up @@ -157,13 +157,13 @@ const BaseJsonService = require('../base-json') // (2)
const { renderVersionBadge } = require('../../lib/version') // (3)

const Joi = require('joi') // (4)
const versionSchema = Joi.object({ // (4)
const schema = Joi.object({ // (4)
version: Joi.string().required(), // (4)
}).required() // (4)

module.exports = class GemVersion extends BaseJsonService { // (5)

static get url() { // (6)
static get route() { // (6)
return {
base: 'gem/v',
pattern: ':gem',
Expand All @@ -174,16 +174,15 @@ module.exports = class GemVersion extends BaseJsonService { // (5)
return { label: 'gem' }
}

async handle({ gem }) { // (8)
async handle({ gem }) { // (8)
const { version } = await this.fetch({ gem })
return this.constructor.render({ version })
}

async fetch({ gem }) { // (9)
const url = `https://rubygems.org/api/v1/gems/${gem}.json`
async fetch({ gem }) { // (9)
return this._requestJson({
url,
schema: versionSchema,
schema,
url: `https://rubygems.org/api/v1/gems/${gem}.json`,
})
}

Expand Down Expand Up @@ -271,7 +270,7 @@ module.exports = class GemVersion extends BaseJsonService {
2. The examples property defines an array of examples. In this case the array will contain a single object, but in some cases it is helpful to provide multiple usage examples.
3. Our example object should contain the following properties:
* `title`: Descriptive text that will be shown next to the badge
* `urlPattern`: Describe the variable part of the URL using `:param` syntax.
* `urlPattern`: Describe the variable part of the route using `:param` syntax.
* `staticExample`: On the index page we want to show an example badge, but for performance reasons we want that example to be generated without making an API call. `staticExample` should be populated by calling our `render()` method with some valid data.
* `exampleUrl`: Provide a valid example of params we can call the badge with. In this case we need a valid ruby gem, so we've picked [formatador](https://rubygems.org/gems/formatador)
* `keywords`: If we want to provide additional keywords other than the title, we can add them here. This helps users to search for relevant badges.
Expand Down
4 changes: 2 additions & 2 deletions services/ansible/ansible.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class AnsibleGalaxyRoleDownloads extends AnsibleGalaxyRole {
return 'downloads'
}

static get url() {
static get route() {
return {
base: 'ansible/role/d',
pattern: ':roleId',
Expand Down Expand Up @@ -85,7 +85,7 @@ class AnsibleGalaxyRoleName extends AnsibleGalaxyRole {
return 'other'
}

static get url() {
static get route() {
return {
base: 'ansible/role',
format: '(.+)',
Expand Down
6 changes: 3 additions & 3 deletions services/apm/apm.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class APMDownloads extends BaseAPMService {
return { label: 'downloads' }
}

static get url() {
static get route() {
return {
base: 'apm/dm',
pattern: ':repo',
Expand Down Expand Up @@ -89,7 +89,7 @@ class APMVersion extends BaseAPMService {
return 'version'
}

static get url() {
static get route() {
return {
base: 'apm/v',
format: '(.+)',
Expand Down Expand Up @@ -133,7 +133,7 @@ class APMLicense extends BaseAPMService {
return 'license'
}

static get url() {
static get route() {
return {
base: 'apm/l',
format: '(.+)',
Expand Down
2 changes: 1 addition & 1 deletion services/appveyor/appveyor-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module.exports = class AppVeyorBase extends BaseJsonService {
})
}

static buildUrl(base) {
static buildRoute(base) {
return {
base,
format: '([^/]+/[^/]+)(?:/(.+))?',
Expand Down
4 changes: 2 additions & 2 deletions services/appveyor/appveyor-ci.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
const AppVeyorBase = require('./appveyor-base')

module.exports = class AppVeyorCi extends AppVeyorBase {
static get url() {
return this.buildUrl('appveyor/ci')
static get route() {
return this.buildRoute('appveyor/ci')
}

static get examples() {
Expand Down
4 changes: 2 additions & 2 deletions services/appveyor/appveyor-tests.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ const documentation = `
`

module.exports = class AppVeyorTests extends AppVeyorBase {
static get url() {
static get route() {
return {
...this.buildUrl('appveyor/tests'),
...this.buildRoute('appveyor/tests'),
queryParams: [
'compact_message',
'passed_label',
Expand Down
6 changes: 3 additions & 3 deletions services/aur/aur.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class AurLicense extends BaseAurService {
return 'license'
}

static get url() {
static get route() {
return {
base: 'aur/license',
format: '(.+)',
Expand Down Expand Up @@ -104,7 +104,7 @@ class AurVotes extends BaseAurService {
return 'rating'
}

static get url() {
static get route() {
return {
base: 'aur/votes',
format: '(.+)',
Expand Down Expand Up @@ -142,7 +142,7 @@ class AurVersion extends BaseAurService {
return 'version'
}

static get url() {
static get route() {
return {
base: 'aur/version',
format: '(.+)',
Expand Down
2 changes: 1 addition & 1 deletion services/azure-devops/azure-devops-build.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module.exports = class AzureDevOpsBuild extends BaseSvgService {
return 'build'
}

static get url() {
static get route() {
return {
base: '',
format: '(?:azure-devops|vso)/build/([^/]+)/([^/]+)/([^/]+)(?:/(.+))?',
Expand Down
2 changes: 1 addition & 1 deletion services/azure-devops/azure-devops-release.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ module.exports = class AzureDevOpsRelease extends BaseSvgService {
return 'build'
}

static get url() {
static get route() {
return {
base: '',
format: '(?:azure-devops|vso)/release/([^/]+)/([^/]+)/([^/]+)/([^/]+)',
Expand Down
2 changes: 1 addition & 1 deletion services/base-json.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class DummyJsonService extends BaseJsonService {
return 'cat'
}

static get url() {
static get route() {
return {
base: 'foo',
}
Expand Down
2 changes: 1 addition & 1 deletion services/base-svg-scraping.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class DummySvgScrapingService extends BaseSvgScrapingService {
return 'cat'
}

static get url() {
static get route() {
return {
base: 'foo',
}
Expand Down
2 changes: 1 addition & 1 deletion services/base-xml.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class DummyXmlService extends BaseXmlService {
return 'cat'
}

static get url() {
static get route() {
return {
base: 'foo',
}
Expand Down
32 changes: 16 additions & 16 deletions services/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ class BaseService {
}

/**
* Asynchronous function to handle requests for this service. Takes the URL
* parameters (as defined in the `url` property), performs a request using
* `this._sendAndCacheRequest`, and returns the badge data.
* Asynchronous function to handle requests for this service. Take the route
* parameters (as defined in the `route` property), perform a request using
* `this._sendAndCacheRequest`, and return the badge data.
*/
async handle(namedParams, queryParams) {
throw new Error(`Handler not implemented for ${this.constructor.name}`)
Expand All @@ -53,40 +53,40 @@ class BaseService {

/**
* Returns an object:
* - base: (Optional) The base path of the URLs for this service. This is
* - base: (Optional) The base path of the routes for this service. This is
* used as a prefix.
* - format: Regular expression to use for URLs for this service's badges
* - format: Regular expression to use for routes for this service's badges
* - capture: Array of names for the capture groups in the regular
* expression. The handler will be passed an object containing
* the matches.
* - queryParams: Array of names for query parameters which will the service
* uses. For cache safety, only the whitelisted query
* parameters will be passed to the handler.
*/
static get url() {
throw new Error(`URL not defined for ${this.name}`)
static get route() {
throw new Error(`Route not defined for ${this.name}`)
}

/**
* Default data for the badge. Can include things such as default logo, color,
* etc. These defaults will be used if the value is not explicitly overridden
* by either the handler or by the user via URL parameters.
* by either the handler or by the user via query parameters.
*/
static get defaultBadgeData() {
return {}
}

/**
* Example URLs for this service. These should use the format
* specified in `url`, and can be used to demonstrate how to use badges for
* specified in `route`, and can be used to demonstrate how to use badges for
* this service.
*/
static get examples() {
return []
}

static _makeFullUrl(partialUrl) {
return `/${[this.url.base, partialUrl].filter(Boolean).join('/')}`
return `/${[this.route.base, partialUrl].filter(Boolean).join('/')}`
}

static _makeStaticExampleUrl(serviceData) {
Expand Down Expand Up @@ -177,7 +177,7 @@ class BaseService {
}

static get _regexFromPath() {
const { pattern } = this.url
const { pattern } = this.route
const fullPattern = `${this._makeFullUrl(
pattern
)}.:ext(svg|png|gif|jpg|json)`
Expand All @@ -193,7 +193,7 @@ class BaseService {
}

static get _regex() {
const { pattern, format, capture } = this.url
const { pattern, format, capture } = this.route
if (
pattern !== undefined &&
(format !== undefined || capture !== undefined)
Expand All @@ -207,7 +207,7 @@ class BaseService {
return this._regexFromPath.regex
} else if (format !== undefined) {
// Regular expressions treat "/" specially, so we need to escape them
const escapedPath = this.url.format.replace(/\//g, '\\/')
const escapedPath = this.route.format.replace(/\//g, '\\/')
const fullRegex = `^${this._makeFullUrl(
escapedPath
)}.(svg|png|gif|jpg|json)$`
Expand All @@ -227,8 +227,8 @@ class BaseService {
}

static _namedParamsForMatch(match) {
const { url } = this
const names = url.pattern ? this._regexFromPath.capture : url.capture || []
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.
Expand Down Expand Up @@ -357,7 +357,7 @@ class BaseService {
camp.route(
this._regex,
handleRequest({
queryParams: this.url.queryParams,
queryParams: this.route.queryParams,
handler: async (queryParams, match, sendBadge, request) => {
const namedParams = this._namedParamsForMatch(match)
const serviceInstance = new ServiceClass(
Expand Down
6 changes: 3 additions & 3 deletions services/base.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class DummyService extends BaseService {
},
]
}
static get url() {
static get route() {
return {
base: 'foo',
pattern: ':namedParamA',
Expand Down Expand Up @@ -98,7 +98,7 @@ describe('BaseService', function() {

context('A `format` with a named param is declared', function() {
class ServiceWithFormat extends BaseService {
static get url() {
static get route() {
return {
base: 'foo',
format: '([^/]+)',
Expand Down Expand Up @@ -147,7 +147,7 @@ describe('BaseService', function() {

context('No named params are declared', function() {
class ServiceWithZeroNamedParams extends BaseService {
static get url() {
static get route() {
return {
base: 'foo',
format: '(?:[^/]+)',
Expand Down
2 changes: 1 addition & 1 deletion services/beerpay/beerpay.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = class Beerpay extends LegacyService {
return 'funding'
}

static get url() {
static get route() {
return { base: 'beerpay' }
}

Expand Down
2 changes: 1 addition & 1 deletion services/bintray/bintray.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = class Bintray extends LegacyService {
return 'version'
}

static get url() {
static get route() {
return { base: 'bintray/v' }
}

Expand Down
6 changes: 3 additions & 3 deletions services/bitbucket/bitbucket-issues.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const bitbucketIssuesSchema = Joi.object({
}).required()

function issueClassGenerator(raw) {
const urlPrefix = raw ? 'issues-raw' : 'issues'
const routePrefix = raw ? 'issues-raw' : 'issues'
const badgeSuffix = raw ? '' : ' open'

return class BitbucketIssues extends BaseJsonService {
Expand Down Expand Up @@ -47,9 +47,9 @@ function issueClassGenerator(raw) {
return { label: 'issues' }
}

static get url() {
static get route() {
return {
base: `bitbucket/${urlPrefix}`,
base: `bitbucket/${routePrefix}`,
format: '([^/]+)/([^/]+)',
capture: ['user', 'repo'],
}
Expand Down
Loading

0 comments on commit 02ec19f

Please sign in to comment.