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:
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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\]$/
|
||||
),
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user