Compare commits

...

2 Commits

Author SHA1 Message Date
Caleb Cartwright
5496dd057a Merge branch 'master' into gh-package-registry-downloads 2019-11-14 12:08:31 -06:00
Caleb Cartwright
3bd1328f19 feat: add Downloads badge for GH Package Registry 2019-09-28 18:17:46 -05:00
2 changed files with 443 additions and 0 deletions

View File

@@ -0,0 +1,295 @@
'use strict'
const gql = require('graphql-tag')
const Joi = require('@hapi/joi')
const { downloadCount } = require('../color-formatters')
const { metric } = require('../text-formatters')
const { nonNegativeInteger } = require('../validators')
const { GithubAuthV4Service } = require('./github-auth-service')
const { documentation, transformErrors } = require('./github-helpers')
const { NotFound } = require('..')
// https://developer.github.com/v4/object/registrypackagestatistics/
// https://developer.github.com/v4/object/registrypackageversionstatistics/
const packageStatistics = Joi.object({
statistics: Joi.object({
downloadsToday: nonNegativeInteger,
downloadsThisWeek: nonNegativeInteger,
downloadsThisMonth: nonNegativeInteger,
downloadsThisYear: nonNegativeInteger,
downloadsTotalCount: nonNegativeInteger,
}).required(),
})
const totalDownloadsSchema = Joi.object({
data: Joi.object({
repository: Joi.object({
registryPackages: Joi.object({
nodes: Joi.array()
.items(packageStatistics)
.min(0)
.required(),
}).required(),
}).required(),
}).required(),
}).required()
const versionDownloadsSchema = Joi.object({
data: Joi.object({
repository: Joi.object({
registryPackages: Joi.object({
nodes: Joi.array()
.items(
Joi.object({
version: packageStatistics.allow(null),
})
)
.min(0)
.required(),
}).required(),
}).required(),
}).required(),
}).required()
const latestVersionDownloadsSchema = Joi.object({
data: Joi.object({
repository: Joi.object({
registryPackages: Joi.object({
nodes: Joi.array()
.items(
Joi.object({
latestVersion: packageStatistics,
})
)
.min(0)
.required(),
}).required(),
}).required(),
}).required(),
}).required()
const intervalMap = {
today: {
transform: statistics => statistics.downloadsToday,
messageSuffix: '/today',
},
week: {
transform: statistics => statistics.downloadsThisWeek,
messageSuffix: '/week',
},
month: {
transform: statistics => statistics.downloadsThisMonth,
messageSuffix: '/month',
},
year: {
transform: statistics => statistics.downloadsThisYear,
messageSuffix: '/year',
},
total: {
transform: statistics => statistics.downloadsTotalCount,
messageSuffix: '',
},
}
module.exports = class GithubPackageRegistryDownloads extends GithubAuthV4Service {
static get category() {
return 'downloads'
}
static get route() {
return {
base: 'github/packages/downloads',
pattern:
':user/:repo/:interval(today|week|month|year|total)/:packageName/:version?',
}
}
static get examples() {
return [
{
title: 'GitHub Package Registry Downloads',
pattern:
':user/:repo/:interval(today|week|month|year|total)/:packageName',
namedParams: {
user: 'github',
repo: 'auto-complete-element',
interval: 'total',
packageName: 'auto-complete-element',
},
staticPreview: this.render({
interval: 'total',
count: 50,
}),
documentation,
},
{
title: 'GitHub Package Registry Downloads (Version)',
pattern:
':user/:repo/:interval(today|week|month|year|total)/:packageName/:version',
namedParams: {
user: 'github',
repo: 'auto-complete-element',
interval: 'total',
packageName: 'auto-complete-element',
version: '1.0.6',
},
staticPreview: this.render({
interval: 'total',
count: 5,
version: '1.0.6',
}),
documentation,
},
{
title: 'GitHub Package Registry Downloads (Latest Version)',
pattern:
':user/:repo/:interval(today|week|month|year|total)/:packageName/:version',
namedParams: {
user: 'github',
repo: 'auto-complete-element',
interval: 'total',
packageName: 'auto-complete-element',
version: 'latest',
},
staticPreview: this.render({
interval: 'total',
count: 9,
version: 'latest',
}),
documentation,
},
]
}
static _getLabel(version) {
if (version) {
return `downloads@${version}`
} else {
return 'downloads'
}
}
static get defaultBadgeData() {
return { label: 'downloads' }
}
static render({ interval, count, version }) {
const { messageSuffix } = intervalMap[interval]
return {
label: this._getLabel(version),
message: `${metric(count)}${messageSuffix}`,
color: downloadCount(count),
}
}
async fetch({ user, repo, packageName, version }) {
if (version) {
if (version === 'latest') {
return await this._requestGraphql({
query: gql`
query($user: String!, $repo: String!, $packageName: String!) {
repository(owner: $user, name: $repo) {
registryPackages(name: $packageName, first: 1) {
nodes {
latestVersion {
statistics {
downloadsThisMonth
downloadsThisWeek
downloadsThisYear
downloadsToday
downloadsTotalCount
}
}
}
}
}
}
`,
variables: { user, repo, packageName },
schema: latestVersionDownloadsSchema,
transformErrors,
})
} else {
return await this._requestGraphql({
query: gql`
query(
$user: String!
$repo: String!
$packageName: String!
$version: String!
) {
repository(owner: $user, name: $repo) {
registryPackages(name: $packageName, first: 1) {
nodes {
version(version: $version) {
statistics {
downloadsThisMonth
downloadsThisWeek
downloadsThisYear
downloadsToday
downloadsTotalCount
}
}
}
}
}
}
`,
variables: { user, repo, packageName, version },
schema: versionDownloadsSchema,
transformErrors,
})
}
} else {
return await this._requestGraphql({
query: gql`
query($user: String!, $repo: String!, $packageName: String!) {
repository(owner: $user, name: $repo) {
registryPackages(name: $packageName, first: 1) {
nodes {
statistics {
downloadsThisMonth
downloadsThisWeek
downloadsThisYear
downloadsToday
downloadsTotalCount
}
}
}
}
}
`,
variables: { user, repo, packageName },
schema: totalDownloadsSchema,
transformErrors,
})
}
}
transform({ json, interval, version }) {
const ghPackage = json.data.repository.registryPackages.nodes[0]
if (!ghPackage) {
throw new NotFound({ prettyMessage: 'package not found' })
}
let statistics
if (version === 'latest') {
statistics = ghPackage.latestVersion.statistics
} else if (version) {
if (!ghPackage.version) {
throw new NotFound({ prettyMessage: 'version not found' })
}
statistics = ghPackage.version.statistics
} else {
statistics = ghPackage.statistics
}
const { transform } = intervalMap[interval]
return { count: transform(statistics) }
}
async handle({ user, repo, interval, packageName, version }) {
const json = await this.fetch({ user, repo, packageName, version })
const { count } = this.transform({ json, interval, version })
return this.constructor.render({ count, interval, version })
}
}

View File

@@ -0,0 +1,148 @@
'use strict'
const Joi = require('@hapi/joi')
const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
const t = (module.exports = require('../tester').createServiceTester())
const downloadsToday = Joi.alternatives(
isMetricOverTimePeriod,
Joi.allow('0/today')
)
const downloadsThisWeek = Joi.alternatives(
isMetricOverTimePeriod,
Joi.allow('0/today')
)
const downloadsThisMonth = Joi.alternatives(
isMetricOverTimePeriod,
Joi.allow('0/today')
)
const downloadsThisYear = Joi.alternatives(
isMetricOverTimePeriod,
Joi.allow('0/today')
)
t.create('Package Registry Downloads (non-existent repository')
.get('/badges/not-a-real-repo/total/super-fake-package.json')
.expectBadge({
label: 'downloads',
message: 'repo not found',
})
t.create('Package Registry Downloads (non-existent package')
.get('/badges/shields/total/super-fake-package.json')
.expectBadge({
label: 'downloads',
message: 'package not found',
})
t.create('Package Registry Downloads (non-existent version')
.get('/github/semantic/total/semantic/9.9.9.json')
.expectBadge({
label: 'downloads',
message: 'version not found',
})
t.create('Package Registry Downloads (total)')
.get('/github/semantic/total/semantic.json')
.expectBadge({
label: 'downloads',
message: isMetric,
})
t.create('Package Registry Downloads (today)')
.get('/github/semantic/today/semantic.json')
.expectBadge({
label: 'downloads',
message: downloadsToday,
})
t.create('Package Registry Downloads (this week)')
.get('/github/semantic/week/semantic.json')
.expectBadge({
label: 'downloads',
message: downloadsThisWeek,
})
t.create('Package Registry Downloads (this month)')
.get('/github/semantic/month/semantic.json')
.expectBadge({
label: 'downloads',
message: downloadsThisMonth,
})
t.create('Package Registry Downloads (this year)')
.get('/github/semantic/year/semantic.json')
.expectBadge({
label: 'downloads',
message: downloadsThisYear,
})
t.create('Package Registry Downloads (specific version total)')
.get('/github/auto-complete-element/total/auto-complete-element/1.0.6.json')
.expectBadge({
label: 'downloads@1.0.6',
message: isMetric,
})
t.create('Package Registry Downloads (specific version today)')
.get('/github/auto-complete-element/today/auto-complete-element/1.0.6.json')
.expectBadge({
label: 'downloads@1.0.6',
message: downloadsToday,
})
t.create('Package Registry Downloads (specific version this week)')
.get('/github/auto-complete-element/week/auto-complete-element/1.0.6.json')
.expectBadge({
label: 'downloads@1.0.6',
message: downloadsThisWeek,
})
t.create('Package Registry Downloads (specific version this month)')
.get('/github/auto-complete-element/month/auto-complete-element/1.0.6.json')
.expectBadge({
label: 'downloads@1.0.6',
message: downloadsThisMonth,
})
t.create('Package Registry Downloads (specific version this year)')
.get('/github/auto-complete-element/year/auto-complete-element/1.0.6.json')
.expectBadge({
label: 'downloads@1.0.6',
message: downloadsThisYear,
})
t.create('Package Registry Downloads (latest version total)')
.get('/github/auto-complete-element/total/auto-complete-element/latest.json')
.expectBadge({
label: 'downloads@latest',
message: isMetric,
})
t.create('Package Registry Downloads (latest version today)')
.get('/github/auto-complete-element/today/auto-complete-element/latest.json')
.expectBadge({
label: 'downloads@latest',
message: downloadsToday,
})
t.create('Package Registry Downloads (latest version this week)')
.get('/github/auto-complete-element/week/auto-complete-element/latest.json')
.expectBadge({
label: 'downloads@latest',
message: downloadsThisWeek,
})
t.create('Package Registry Downloads (latest version this month)')
.get('/github/auto-complete-element/month/auto-complete-element/latest.json')
.expectBadge({
label: 'downloads@latest',
message: downloadsThisMonth,
})
t.create('Package Registry Downloads (latest version this year)')
.get('/github/auto-complete-element/year/auto-complete-element/latest.json')
.expectBadge({
label: 'downloads@latest',
message: downloadsThisYear,
})