-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
/
Copy pathnpm-downloads.service.js
101 lines (90 loc) · 2.8 KB
/
npm-downloads.service.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import Joi from 'joi'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService, pathParams } from '../index.js'
import { packageNameDescription } from './npm-base.js'
// https://github.com/npm/registry/blob/master/docs/download-counts.md#output
const pointResponseSchema = Joi.object({
downloads: nonNegativeInteger,
}).required()
const intervalMap = {
dw: {
query: 'point/last-week',
schema: pointResponseSchema,
transform: json => json.downloads,
interval: 'week',
},
dm: {
query: 'point/last-month',
schema: pointResponseSchema,
transform: json => json.downloads,
interval: 'month',
},
dy: {
query: 'point/last-year',
schema: pointResponseSchema,
transform: json => json.downloads,
interval: 'year',
},
d18m: {
query: 'range/1000-01-01:3000-01-01',
// https://github.com/npm/registry/blob/master/docs/download-counts.md#output-1
schema: Joi.object({
downloads: Joi.array().items(pointResponseSchema).required(),
}).required(),
transform: json =>
json.downloads
.map(item => item.downloads)
.reduce((accum, current) => accum + current),
},
}
// This hits an entirely different API from the rest of the NPM services, so
// it does not use NpmBase.
export default class NpmDownloads extends BaseJsonService {
static category = 'downloads'
static route = {
base: 'npm',
pattern: ':interval(dw|dm|dy|d18m)/:scope(@.+)?/:packageName',
}
static openApi = {
'/npm/{interval}/{packageName}': {
get: {
summary: 'NPM Downloads',
parameters: pathParams(
{
name: 'interval',
example: 'dw',
description:
'Downloads in the last Week, Month, Year, or 18 Months',
schema: { type: 'string', enum: this.getEnum('interval') },
},
{
name: 'packageName',
example: 'localeval',
description: packageNameDescription,
},
),
},
},
}
// For testing.
static _intervalMap = intervalMap
static render({ interval, downloadCount: downloads }) {
return renderDownloadsBadge({
downloads,
interval: intervalMap[interval].interval,
colorOverride: downloads > 0 ? 'brightgreen' : 'red',
})
}
async handle({ interval, scope, packageName }) {
const { query, schema, transform } = intervalMap[interval]
const slug = scope ? `${scope}/${packageName}` : packageName
const json = await this._requestJson({
schema,
url: `https://api.npmjs.org/downloads/${query}/${slug}`,
httpErrors: { 404: 'package not found or too new' },
})
const downloadCount = transform(json)
return this.constructor.render({ interval, downloadCount })
}
}