Add [NpmStatDownloads] Badge (#9783)
* feat: add npm downloads (by author) badge * Update services/npm-stat/npm-stat-downloads.service.js Co-authored-by: chris48s <chris48s@users.noreply.github.com> * test: add test cases for NpmStatDownloads helper getTotalDownloads * refactor: using dayjs to get from && until date string * feat: remove support of dt --------- Co-authored-by: chris48s <chris48s@users.noreply.github.com>
This commit is contained in:
71
services/npm-stat/npm-stat-downloads.service.js
Normal file
71
services/npm-stat/npm-stat-downloads.service.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import Joi from 'joi'
|
||||
import dayjs from 'dayjs'
|
||||
import { nonNegativeInteger } from '../validators.js'
|
||||
import { BaseJsonService } from '../index.js'
|
||||
import { renderDownloadsBadge } from '../downloads.js'
|
||||
|
||||
const schema = Joi.object()
|
||||
.pattern(Joi.string(), Joi.object().pattern(Joi.string(), nonNegativeInteger))
|
||||
.required()
|
||||
|
||||
const intervalMap = {
|
||||
dw: { interval: 'week' },
|
||||
dm: { interval: 'month' },
|
||||
dy: { interval: 'year' },
|
||||
}
|
||||
|
||||
export default class NpmStatDownloads extends BaseJsonService {
|
||||
static category = 'downloads'
|
||||
|
||||
static route = {
|
||||
base: 'npm-stat',
|
||||
pattern: ':interval(dw|dm|dy)/:author',
|
||||
}
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'npm (by author)',
|
||||
documentation:
|
||||
'The total number of downloads of npm packages published by the specified author from [npm-stat](https://npm-stat.com).',
|
||||
namedParams: { interval: 'dy', author: 'dukeluo' },
|
||||
staticPreview: this.render({ interval: 'dy', downloadCount: 30000 }),
|
||||
keywords: ['node'],
|
||||
},
|
||||
]
|
||||
|
||||
static _cacheLength = 21600
|
||||
|
||||
static defaultBadgeData = { label: 'downloads' }
|
||||
|
||||
static getTotalDownloads(data) {
|
||||
const add = (x, y) => x + y
|
||||
const sum = nums => nums.reduce(add, 0)
|
||||
|
||||
return Object.values(data).reduce(
|
||||
(count, packageDownloads) => count + sum(Object.values(packageDownloads)),
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
static render({ interval, downloads }) {
|
||||
return renderDownloadsBadge({
|
||||
downloads,
|
||||
interval: intervalMap[interval].interval,
|
||||
colorOverride: downloads > 0 ? 'brightgreen' : 'red',
|
||||
})
|
||||
}
|
||||
|
||||
async handle({ interval, author }) {
|
||||
const unit = intervalMap[interval].interval
|
||||
const today = dayjs()
|
||||
const until = today.format('YYYY-MM-DD')
|
||||
const from = today.subtract(1, unit).format('YYYY-MM-DD')
|
||||
const data = await this._requestJson({
|
||||
url: `https://npm-stat.com/api/download-counts?author=${author}&from=${from}&until=${until}`,
|
||||
schema,
|
||||
})
|
||||
const downloads = this.constructor.getTotalDownloads(data)
|
||||
|
||||
return this.constructor.render({ interval, downloads })
|
||||
}
|
||||
}
|
||||
25
services/npm-stat/npm-stat-downloads.spec.js
Normal file
25
services/npm-stat/npm-stat-downloads.spec.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { test, given } from 'sazerac'
|
||||
import NpmStatDownloads from './npm-stat-downloads.service.js'
|
||||
|
||||
describe('NpmStatDownloads helpers', function () {
|
||||
test(NpmStatDownloads.getTotalDownloads, () => {
|
||||
given({
|
||||
'hexo-theme-candelas': {
|
||||
'2022-12-01': 1,
|
||||
'2022-12-02': 2,
|
||||
'2022-12-03': 3,
|
||||
},
|
||||
'@dukeluo/fanjs': {
|
||||
'2022-12-01': 10,
|
||||
'2022-12-02': 20,
|
||||
'2022-12-03': 30,
|
||||
},
|
||||
'eslint-plugin-check-file': {
|
||||
'2022-12-01': 100,
|
||||
'2022-12-02': 200,
|
||||
'2022-12-03': 300,
|
||||
},
|
||||
}).expect(666)
|
||||
given({}).expect(0)
|
||||
})
|
||||
})
|
||||
35
services/npm-stat/npm-stat-downloads.tester.js
Normal file
35
services/npm-stat/npm-stat-downloads.tester.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { isMetricOverTimePeriod } from '../test-validators.js'
|
||||
import { createServiceTester } from '../tester.js'
|
||||
export const t = await createServiceTester()
|
||||
|
||||
t.create('weekly downloads of npm author dukeluo')
|
||||
.get('/dw/dukeluo.json')
|
||||
.expectBadge({
|
||||
label: 'downloads',
|
||||
message: isMetricOverTimePeriod,
|
||||
color: 'brightgreen',
|
||||
})
|
||||
|
||||
t.create('monthly downloads of npm author dukeluo')
|
||||
.get('/dm/dukeluo.json')
|
||||
.expectBadge({
|
||||
label: 'downloads',
|
||||
message: isMetricOverTimePeriod,
|
||||
color: 'brightgreen',
|
||||
})
|
||||
|
||||
t.create('yearly downloads of npm author dukeluo')
|
||||
.get('/dy/dukeluo.json')
|
||||
.expectBadge({
|
||||
label: 'downloads',
|
||||
message: isMetricOverTimePeriod,
|
||||
color: 'brightgreen',
|
||||
})
|
||||
|
||||
t.create('downloads of unknown npm package author')
|
||||
.get('/dy/npm-api-does-not-have-this-package-author.json')
|
||||
.expectBadge({
|
||||
label: 'downloads',
|
||||
message: '0/year',
|
||||
color: 'red',
|
||||
})
|
||||
Reference in New Issue
Block a user