Rewrite [GithubDownloads] (#3351)

For consistency with other download badges, I changed some formatting:

-  **downloads | 24k total** -> **downloads | 24k**
- **downloads | 3k** -> **downloads@latest | 3k**
- **downloads | 3k v0.29.0** -> **downloads@v0.29.0 | 3k**

Ref #2863
This commit is contained in:
Paul Melnikow
2019-04-22 22:55:31 -04:00
committed by GitHub
parent f13326dddf
commit ca22d01606
2 changed files with 159 additions and 171 deletions

View File

@@ -1,21 +1,30 @@
'use strict'
const LegacyService = require('../legacy-service')
const { makeBadgeData: getBadgeData } = require('../../lib/badge-data')
const { makeLogo: getLogo } = require('../../lib/logos')
const Joi = require('joi')
const { NotFound } = require('..')
const { metric } = require('../text-formatters')
const {
documentation,
checkErrorResponse: githubCheckErrorResponse,
} = require('./github-helpers')
const { nonNegativeInteger } = require('../validators')
const { downloadCount: downloadCountColor } = require('../color-formatters')
const { GithubAuthService } = require('./github-auth-service')
const { documentation, errorMessagesFor } = require('./github-helpers')
// This legacy service should be rewritten to use e.g. BaseJsonService.
//
// Tips for rewriting:
// https://github.com/badges/shields/blob/master/doc/rewriting-services.md
//
// Do not base new services on this code.
module.exports = class GithubDownloads extends LegacyService {
const releaseSchema = Joi.object({
assets: Joi.array()
.items({
name: Joi.string().required(),
download_count: nonNegativeInteger,
})
.required(),
}).required()
const releaseArraySchema = Joi.alternatives().try(
Joi.array().items(releaseSchema),
Joi.array().length(0)
)
const keywords = ['github download']
module.exports = class GithubDownloads extends GithubAuthService {
static get category() {
return 'downloads'
}
@@ -23,7 +32,7 @@ module.exports = class GithubDownloads extends LegacyService {
static get route() {
return {
base: 'github',
pattern: '',
pattern: ':kind(downloads|downloads-pre)/:user/:repo/:tag*/:assetName',
}
}
@@ -36,12 +45,12 @@ module.exports = class GithubDownloads extends LegacyService {
user: 'atom',
repo: 'atom',
},
staticPreview: {
label: 'downloads',
message: '857k total',
color: 'brightgreen',
},
staticPreview: this.render({
assetName: 'total',
downloadCount: 857000,
}),
documentation,
keywords,
},
{
title: 'GitHub Releases',
@@ -51,12 +60,13 @@ module.exports = class GithubDownloads extends LegacyService {
repo: 'atom',
tag: 'latest',
},
staticPreview: {
label: 'downloads',
message: '27k',
color: 'brightgreen',
},
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloadCount: 27000,
}),
documentation,
keywords,
},
{
title: 'GitHub Pre-Releases',
@@ -66,12 +76,13 @@ module.exports = class GithubDownloads extends LegacyService {
repo: 'atom',
tag: 'latest',
},
staticPreview: {
label: 'downloads',
message: '2k',
color: 'brightgreen',
},
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloadCount: 2000,
}),
documentation,
keywords,
},
{
title: 'GitHub Releases (by Release)',
@@ -81,12 +92,13 @@ module.exports = class GithubDownloads extends LegacyService {
repo: 'atom',
tag: 'v0.190.0',
},
staticPreview: {
label: 'downloads',
message: '490k v0.190.0',
color: 'brightgreen',
},
staticPreview: this.render({
tag: 'v0.190.0',
assetName: 'total',
downloadCount: 490000,
}),
documentation,
keywords,
},
{
title: 'GitHub Releases (by Asset)',
@@ -97,12 +109,13 @@ module.exports = class GithubDownloads extends LegacyService {
tag: 'latest',
path: 'atom-amd64.deb',
},
staticPreview: {
label: 'downloads',
message: '3k [atom-amd64.deb]',
color: 'brightgreen',
},
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloadCount: 3000,
}),
documentation,
keywords,
},
{
title: 'GitHub Pre-Releases (by Asset)',
@@ -113,119 +126,96 @@ module.exports = class GithubDownloads extends LegacyService {
tag: 'latest',
path: 'atom-amd64.deb',
},
staticPreview: {
label: 'downloads',
message: '237 [atom-amd64.deb]',
color: 'brightgreen',
},
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloadCount: 237,
}),
documentation,
keywords,
},
]
}
static registerLegacyRouteHandler({ camp, cache, githubApiProvider }) {
camp.route(
/^\/github\/(downloads|downloads-pre)\/([^/]+)\/([^/]+)(\/.+)?\/([^/]+)\.(svg|png|gif|jpg|json)$/,
cache((data, match, sendBadge, request) => {
const type = match[1] // downloads or downloads-pre
const user = match[2] // eg, qubyte/rubidium
const repo = match[3]
static get defaultBadgeData() {
return {
label: 'downloads',
namedLogo: 'github',
}
}
let tag = match[4] // eg, v0.190.0, latest, null if querying all releases
const assetName = match[5].toLowerCase() // eg. total, atom-amd64.deb, atom.x86_64.rpm
const format = match[6]
static render({ tag, assetName, downloadCount }) {
return {
label: tag ? `downloads@${tag}` : 'downloads',
message:
assetName === 'total'
? metric(downloadCount)
: `${metric(downloadCount)} [${assetName}]`,
color: downloadCountColor(downloadCount),
}
}
if (tag) {
tag = tag.slice(1)
}
static transform({ releases, assetName }) {
const downloadCount = releases.reduce((accum1, { assets }) => {
const filteredAssets =
assetName === 'total'
? assets
: assets.filter(({ name }) => name.toLowerCase() === assetName)
return (
accum1 +
filteredAssets.reduce(
(accum2, { download_count: downloadCount }) => accum2 + downloadCount,
0
)
)
}, 0)
return { downloadCount }
}
let total = true
if (tag) {
total = false
}
let apiUrl = `/repos/${user}/${repo}/releases`
if (!total) {
const releasePath =
tag === 'latest'
? type === 'downloads'
? 'latest'
: ''
: `tags/${tag}`
if (releasePath) {
apiUrl = `${apiUrl}/${releasePath}`
}
} else {
apiUrl = `${apiUrl}?per_page=500`
}
const badgeData = getBadgeData('downloads', data)
if (badgeData.template === 'social') {
badgeData.logo = getLogo('github', data)
}
githubApiProvider.request(request, apiUrl, {}, (err, res, buffer) => {
if (
githubCheckErrorResponse(
badgeData,
err,
res,
'repo or release not found'
)
) {
sendBadge(format, badgeData)
return
}
try {
let data = JSON.parse(buffer)
if (type === 'downloads-pre' && tag === 'latest') {
data = data[0]
}
let downloads = 0
const labelWords = []
if (total) {
data.forEach(tagData => {
tagData.assets.forEach(asset => {
if (
assetName === 'total' ||
assetName === asset.name.toLowerCase()
) {
downloads += asset.download_count
}
})
})
labelWords.push('total')
if (assetName !== 'total') {
labelWords.push(`[${assetName}]`)
}
} else {
data.assets.forEach(asset => {
if (
assetName === 'total' ||
assetName === asset.name.toLowerCase()
) {
downloads += asset.download_count
}
})
if (tag !== 'latest') {
labelWords.push(tag)
}
if (assetName !== 'total') {
labelWords.push(`[${assetName}]`)
}
}
labelWords.unshift(metric(downloads))
badgeData.text[1] = labelWords.join(' ')
badgeData.colorscheme = 'brightgreen'
sendBadge(format, badgeData)
} catch (e) {
badgeData.text[1] = 'none'
sendBadge(format, badgeData)
}
})
async handle({ kind, user, repo, tag, assetName }) {
let releases
if (tag === 'latest' && kind === 'downloads') {
const latestRelease = await this._requestJson({
schema: releaseSchema,
url: `/repos/${user}/${repo}/releases/latest`,
errorMessages: errorMessagesFor('repo not found'),
})
)
releases = [latestRelease]
} else if (tag === 'latest') {
// Keep only the latest release.
const [latestReleaseIncludingPrereleases] = await this._requestJson({
schema: releaseArraySchema,
url: `/repos/${user}/${repo}/releases`,
options: { qs: { per_page: 1 } },
errorMessages: errorMessagesFor('repo not found'),
})
releases = [latestReleaseIncludingPrereleases]
} else if (tag) {
const wantedRelease = await this._requestJson({
schema: releaseSchema,
url: `/repos/${user}/${repo}/releases/tags/${tag}`,
errorMessages: errorMessagesFor('repo or release not found'),
})
releases = [wantedRelease]
} else {
const allReleases = await this._requestJson({
schema: releaseArraySchema,
url: `/repos/${user}/${repo}/releases`,
options: { qs: { per_page: 500 } },
errorMessages: errorMessagesFor('repo not found'),
})
releases = allReleases
}
if (releases.length === 0) {
throw new NotFound({ prettyMessage: 'no releases' })
}
const { downloadCount } = this.constructor.transform({
releases,
assetName,
})
return this.constructor.render({ tag, assetName, downloadCount })
}
}

View File

@@ -6,69 +6,67 @@ const t = (module.exports = require('../tester').createServiceTester())
t.create('Downloads all releases')
.get('/downloads/photonstorm/phaser/total.json')
.expectBadge({
label: 'downloads',
message: Joi.string().regex(/^\w+\s+total$/),
})
.expectBadge({ label: 'downloads', message: isMetric })
t.create('Downloads all releases (no releases)')
.get('/downloads/badges/shields/total.json')
.expectBadge({ label: 'downloads', message: 'no releases' })
t.create('Downloads-pre all releases (no releases)')
.get('/downloads-pre/badges/shields/total.json')
.expectBadge({ label: 'downloads', message: 'no releases' })
t.create('Downloads all releases (repo not found)')
.get('/downloads/badges/helmets/total.json')
.expectBadge({
label: 'downloads',
message: 'repo or release not found',
})
.expectBadge({ label: 'downloads', message: 'repo not found' })
t.create('Downloads-pre all releases (repo not found)')
.get('/downloads-pre/badges/helmets/total.json')
.expectBadge({ label: 'downloads', message: 'repo not found' })
t.create('downloads for latest release')
.get('/downloads/photonstorm/phaser/latest/total.json')
.expectBadge({ label: 'downloads', message: isMetric })
.expectBadge({ label: 'downloads@latest', message: isMetric })
t.create('downloads-pre for latest release')
.get('/downloads-pre/photonstorm/phaser/latest/total.json')
.expectBadge({ label: 'downloads', message: isMetric })
.expectBadge({ label: 'downloads@latest', message: isMetric })
t.create('downloads for release without slash')
.get('/downloads/atom/atom/v0.190.0/total.json')
.expectBadge({
label: 'downloads',
message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? v0\.190\.0$/),
})
.expectBadge({ label: 'downloads@v0.190.0', message: isMetric })
t.create('downloads for specific asset without slash')
.get('/downloads/atom/atom/v0.190.0/atom-amd64.deb.json')
.expectBadge({
label: 'downloads',
message: Joi.string().regex(
/^[0-9]+[kMGTPEZY]? v0\.190\.0 \[atom-amd64\.deb\]$/
),
label: 'downloads@v0.190.0',
message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? \[atom-amd64\.deb\]$/),
})
t.create('downloads for specific asset from latest release')
.get('/downloads/atom/atom/latest/atom-amd64.deb.json')
.expectBadge({
label: 'downloads',
label: 'downloads@latest',
message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? \[atom-amd64\.deb\]$/),
})
t.create('downloads-pre for specific asset from latest release')
.get('/downloads-pre/atom/atom/latest/atom-amd64.deb.json')
.expectBadge({
label: 'downloads',
label: 'downloads@latest',
message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? \[atom-amd64\.deb\]$/),
})
t.create('downloads for release with slash')
.get('/downloads/NHellFire/dban/stable/v2.2.8/total.json')
.expectBadge({
label: 'downloads',
message: Joi.string().regex(/^[0-9]+[kMGTPEZY]? stable\/v2\.2\.8$/),
})
.expectBadge({ label: 'downloads@stable/v2.2.8', message: isMetric })
t.create('downloads for specific asset with slash')
.get('/downloads/NHellFire/dban/stable/v2.2.8/dban-2.2.8_i586.iso.json')
.expectBadge({
label: 'downloads',
label: 'downloads@stable/v2.2.8',
message: Joi.string().regex(
/^[0-9]+[kMGTPEZY]? stable\/v2\.2\.8 \[dban-2\.2\.8_i586\.iso\]$/
/^[0-9]+[kMGTPEZY]? \[dban-2\.2\.8_i586\.iso\]$/
),
})