Skip to content

Commit

Permalink
Add [NpmStatDownloads] Badge (#9783)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
dukeluo and chris48s authored Dec 12, 2023
1 parent 407fe39 commit 57ba623
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 0 deletions.
71 changes: 71 additions & 0 deletions services/npm-stat/npm-stat-downloads.service.js
Original file line number Diff line number Diff line change
@@ -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 changes: 25 additions & 0 deletions services/npm-stat/npm-stat-downloads.spec.js
Original file line number Diff line number Diff line change
@@ -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 changes: 35 additions & 0 deletions services/npm-stat/npm-stat-downloads.tester.js
Original file line number Diff line number Diff line change
@@ -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',
})

0 comments on commit 57ba623

Please sign in to comment.