Files
shields/services/npm/npm-downloads.service.js
chris48s df8049a765 redirect [npm] /dt to /d18m (#10033)
* redirect [npm] /dt to /d18m

* fix unit test
2024-03-23 19:38:05 +00:00

102 lines
2.8 KiB
JavaScript

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 })
}
}