refactor [hexpm] service (#2203)

This commit is contained in:
chris48s
2018-10-23 18:06:57 +01:00
committed by GitHub
parent aeceb283d8
commit 8ee2701836
3 changed files with 207 additions and 96 deletions

View File

@@ -632,18 +632,6 @@ const allBadgeExamples = [
previewUrl: '/packagist/dt/doctrine/orm.svg',
keywords: ['PHP'],
},
{
title: 'Hex.pm',
previewUrl: '/hexpm/dw/plug.svg',
},
{
title: 'Hex.pm',
previewUrl: '/hexpm/dd/plug.svg',
},
{
title: 'Hex.pm',
previewUrl: '/hexpm/dt/plug.svg',
},
{
title: 'WordPress plugin',
previewUrl: '/wordpress/plugin/dt/akismet.svg',
@@ -986,10 +974,6 @@ const allBadgeExamples = [
title: 'Bower',
previewUrl: '/bower/l/bootstrap.svg',
},
{
title: 'Hex.pm',
previewUrl: '/hexpm/l/plug.svg',
},
{
title: 'CocoaPods',
previewUrl: '/cocoapods/l/AFNetworking.svg',
@@ -1218,10 +1202,6 @@ const allBadgeExamples = [
title: 'Pub',
previewUrl: '/pub/v/box2d.svg',
},
{
title: 'Hex.pm',
previewUrl: '/hexpm/v/plug.svg',
},
{
title: 'GitHub tag (latest SemVer)',
previewUrl: '/github/tag/expressjs/express.svg',

View File

@@ -1,10 +1,7 @@
'use strict'
const LegacyService = require('../legacy-service')
const {
makeBadgeData: getBadgeData,
makeLabel: getLabel,
} = require('../../lib/badge-data')
const Joi = require('joi')
const BaseJsonService = require('../base-json')
const {
metric,
addv: versionText,
@@ -14,69 +11,176 @@ const {
downloadCount: downloadCountColor,
version: versionColor,
} = require('../../lib/color-formatters')
const { nonNegativeInteger } = require('../validators')
module.exports = class Hexpm extends LegacyService {
static registerLegacyRouteHandler({ camp, cache }) {
camp.route(
/^\/hexpm\/([^/]+)\/(.*)\.(svg|png|gif|jpg|json)$/,
cache((queryParams, match, sendBadge, request) => {
const info = match[1]
const repo = match[2] // eg, `httpotion`.
const format = match[3]
const apiUrl = 'https://hex.pm/api/packages/' + repo
const badgeData = getBadgeData('hex', queryParams)
request(apiUrl, (err, res, buffer) => {
if (err != null) {
badgeData.text[1] = 'inaccessible'
sendBadge(format, badgeData)
return
}
try {
const data = JSON.parse(buffer)
if (info.charAt(0) === 'd') {
badgeData.text[0] = getLabel('downloads', queryParams)
let downloads
switch (info.charAt(1)) {
case 'w':
downloads = data.downloads.week
badgeData.text[1] = metric(downloads) + '/week'
break
case 'd':
downloads = data.downloads.day
badgeData.text[1] = metric(downloads) + '/day'
break
case 't':
downloads = data.downloads.all
badgeData.text[1] = metric(downloads)
break
}
badgeData.colorscheme = downloadCountColor(downloads)
sendBadge(format, badgeData)
} else if (info === 'v') {
const version = data.releases[0].version
badgeData.text[1] = versionText(version)
badgeData.colorscheme = versionColor(version)
sendBadge(format, badgeData)
} else if (info === 'l') {
const license = (data.meta.licenses || []).join(', ')
badgeData.text[0] = getLabel(
maybePluralize('license', data.meta.licenses),
queryParams
)
if (license === '') {
badgeData.text[1] = 'Unknown'
} else {
badgeData.text[1] = license
badgeData.colorscheme = 'blue'
}
sendBadge(format, badgeData)
}
} catch (e) {
badgeData.text[1] = 'invalid'
sendBadge(format, badgeData)
}
})
})
)
const hexSchema = Joi.object({
downloads: Joi.object({
all: nonNegativeInteger,
week: nonNegativeInteger,
day: nonNegativeInteger,
}).required(),
meta: Joi.object({
licenses: Joi.array().required(),
}).required(),
releases: Joi.array()
.items(Joi.object({ version: Joi.string().required() }).required())
.required(),
}).required()
class BaseHexPmService extends BaseJsonService {
async fetch({ pkg }) {
return this._requestJson({
schema: hexSchema,
url: `https://hex.pm/api/packages/${pkg}`,
})
}
static get defaultBadgeData() {
return { label: 'hex' }
}
}
class HexPmLicense extends BaseHexPmService {
static render({ licenses }) {
if (licenses.length === 0) {
return {
label: 'license',
message: 'Unknown',
color: 'lightgrey',
}
}
return {
label: maybePluralize('license', licenses),
message: licenses.join(', '),
color: 'blue',
}
}
async handle({ pkg }) {
const json = await this.fetch({ pkg })
return this.constructor.render({ licenses: json.meta.licenses })
}
static get defaultBadgeData() {
return { label: 'license' }
}
static get category() {
return 'license'
}
static get url() {
return {
base: 'hexpm/l',
format: '(.+)',
capture: ['pkg'],
}
}
static get examples() {
return [
{
title: 'Hex.pm',
urlPattern: ':package',
exampleUrl: 'plug',
staticExample: this.render({ licenses: ['Apache 2'] }),
},
]
}
}
class HexPmVersion extends BaseHexPmService {
static render({ version }) {
return { message: versionText(version), color: versionColor(version) }
}
async handle({ pkg }) {
const json = await this.fetch({ pkg })
return this.constructor.render({ version: json.releases[0].version })
}
static get category() {
return 'version'
}
static get url() {
return {
base: 'hexpm/v',
format: '(.+)',
capture: ['pkg'],
}
}
static get examples() {
return [
{
title: 'Hex.pm',
urlPattern: ':package',
exampleUrl: 'plug',
staticExample: this.render({ version: '1.6.4' }),
},
]
}
}
function DownloadsForInterval(interval) {
const { base, messageSuffix } = {
day: {
base: 'hexpm/dd',
messageSuffix: '/day',
},
week: {
base: 'hexpm/dw',
messageSuffix: '/week',
},
all: {
base: 'hexpm/dt',
messageSuffix: '',
},
}[interval]
return class HexPmDownloads extends BaseHexPmService {
static render({ downloads }) {
return {
message: `${metric(downloads)}${messageSuffix}`,
color: downloadCountColor(downloads),
}
}
async handle({ pkg }) {
const json = await this.fetch({ pkg })
return this.constructor.render({ downloads: json.downloads[interval] })
}
static get defaultBadgeData() {
return { label: 'downloads' }
}
static get category() {
return 'downloads'
}
static get url() {
return {
base: base,
format: '(.+)',
capture: ['pkg'],
}
}
static get examples() {
return [
{
title: 'Hex.pm',
urlPattern: ':package',
exampleUrl: 'plug',
staticExample: this.render({ downloads: 85000 }),
},
]
}
}
}
const downloadsServices = ['day', 'week', 'all'].map(DownloadsForInterval)
module.exports = [...downloadsServices, HexPmLicense, HexPmVersion]

View File

@@ -3,6 +3,7 @@
const Joi = require('joi')
const ServiceTester = require('../service-tester')
const { isMetric, isMetricOverTimePeriod } = require('../test-validators')
const { colorScheme } = require('../test-helpers')
const isHexpmVersion = Joi.string().regex(/^v\d+.\d+.?\d?$/)
@@ -30,19 +31,45 @@ t.create('version')
.expectJSONTypes(Joi.object().keys({ name: 'hex', value: isHexpmVersion }))
t.create('license')
.get('/l/cowboy.json')
.get('/l/cowboy.json?style=_shields_test')
.expectJSONTypes(
Joi.object().keys({
name: 'license',
value: Joi.string().required(),
colorB: colorScheme.blue,
})
)
t.create('unknown repo')
.get('/l/this-repo-does-not-exist.json')
.expectJSON({ name: 'hex', value: 'invalid' })
t.create('license (multiple licenses)')
.get('/l/cowboy.json?style=_shields_test')
.intercept(nock =>
nock('https://hex.pm/')
.get('/api/packages/cowboy')
.reply(200, {
downloads: { all: 0, week: 0, day: 0 },
releases: [{ version: '1.0' }],
meta: { licenses: ['GPLv2', 'MIT'] },
})
)
.expectJSON({
name: 'licenses',
value: 'GPLv2, MIT',
colorB: colorScheme.blue,
})
t.create('connection error')
.get('/l/cowboy.json')
.networkOff()
.expectJSON({ name: 'hex', value: 'inaccessible' })
t.create('license (no license)')
.get('/l/cowboy.json?style=_shields_test')
.intercept(nock =>
nock('https://hex.pm/')
.get('/api/packages/cowboy')
.reply(200, {
downloads: { all: 0, week: 0, day: 0 },
releases: [{ version: '1.0' }],
meta: { licenses: [] },
})
)
.expectJSON({
name: 'license',
value: 'Unknown',
colorB: colorScheme.lightgrey,
})