Compare commits
2 Commits
requires-p
...
gh-package
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5496dd057a | ||
|
|
3bd1328f19 |
295
services/github/github-package-registry-downloads.service.js
Normal file
295
services/github/github-package-registry-downloads.service.js
Normal 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 })
|
||||
}
|
||||
}
|
||||
148
services/github/github-package-registry-downloads.tester.js
Normal file
148
services/github/github-package-registry-downloads.tester.js
Normal 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,
|
||||
})
|
||||
Reference in New Issue
Block a user