-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into validate-service-data
- Loading branch information
Showing
23 changed files
with
470 additions
and
472 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,109 +1,114 @@ | ||
'use strict' | ||
|
||
const LegacyService = require('../legacy-service') | ||
const { | ||
makeBadgeData: getBadgeData, | ||
makeLabel: getLabel, | ||
} = require('../../lib/badge-data') | ||
const { checkErrorResponse } = require('../../lib/error-helper') | ||
const Joi = require('joi') | ||
const BaseJsonService = require('../base-json') | ||
const { metric } = require('../../lib/text-formatters') | ||
const { addv: versionText } = require('../../lib/text-formatters') | ||
const { | ||
downloadCount: downloadCountColor, | ||
} = require('../../lib/color-formatters') | ||
const { nonNegativeInteger } = require('../validators') | ||
|
||
// This legacy service should be rewritten to use e.g. BaseJsonService. | ||
// | ||
// Tips for rewriting: | ||
// https://github.com/badges/shields/blob/master/doc/rewriting-services.md | ||
// | ||
// Do not base new services on this code. | ||
module.exports = class DubDownload extends LegacyService { | ||
static get category() { | ||
return 'downloads' | ||
} | ||
const schema = Joi.object({ | ||
downloads: Joi.object({ | ||
total: nonNegativeInteger, | ||
monthly: nonNegativeInteger, | ||
weekly: nonNegativeInteger, | ||
daily: nonNegativeInteger, | ||
}).required(), | ||
}) | ||
|
||
static get route() { | ||
return { | ||
base: 'dub', | ||
function DownloadsForInterval(interval) { | ||
const { base, messageSuffix } = { | ||
daily: { | ||
base: 'dub/dd', | ||
messageSuffix: '/day', | ||
}, | ||
weekly: { | ||
base: 'dub/dw', | ||
messageSuffix: '/week', | ||
}, | ||
monthly: { | ||
base: 'dub/dm', | ||
messageSuffix: '/month', | ||
}, | ||
total: { | ||
base: 'dub/dt', | ||
messageSuffix: '', | ||
}, | ||
}[interval] | ||
|
||
return class DubDownloads extends BaseJsonService { | ||
static render({ downloads, version }) { | ||
const label = version ? `downloads@${version}` : 'downloads' | ||
return { | ||
label, | ||
message: `${metric(downloads)}${messageSuffix}`, | ||
color: downloadCountColor(downloads), | ||
} | ||
} | ||
} | ||
|
||
static get examples() { | ||
return [ | ||
{ | ||
title: 'DUB', | ||
previewUrl: 'dd/vibe-d', | ||
}, | ||
{ | ||
title: 'DUB', | ||
previewUrl: 'dw/vibe-d', | ||
}, | ||
{ | ||
title: 'DUB', | ||
previewUrl: 'dm/vibe-d/latest', | ||
}, | ||
{ | ||
title: 'DUB', | ||
previewUrl: 'dt/vibe-d/0.8.4', | ||
}, | ||
] | ||
} | ||
async fetch({ packageName, version }) { | ||
let url = `https://code.dlang.org/api/packages/${packageName}` | ||
if (version) { | ||
url += `/${version}` | ||
} | ||
url += '/stats' | ||
return this._requestJson({ schema, url }) | ||
} | ||
|
||
static registerLegacyRouteHandler({ camp, cache }) { | ||
camp.route( | ||
/^\/dub\/(dd|dw|dm|dt)\/([^/]+)(?:\/([^/]+))?\.(svg|png|gif|jpg|json)$/, | ||
cache((data, match, sendBadge, request) => { | ||
const info = match[1] // downloads (dd - daily, dw - weekly, dm - monthly, dt - total) | ||
const pkg = match[2] // package name, e.g. vibe-d | ||
const version = match[3] // version (1.2.3 or latest) | ||
const format = match[4] | ||
let apiUrl = `https://code.dlang.org/api/packages/${pkg}` | ||
if (version) { | ||
apiUrl += `/${version}` | ||
} | ||
apiUrl += '/stats' | ||
const badgeData = getBadgeData('dub', data) | ||
request(apiUrl, (err, res, buffer) => { | ||
if (checkErrorResponse(badgeData, err, res)) { | ||
sendBadge(format, badgeData) | ||
return | ||
} | ||
try { | ||
const parsedData = JSON.parse(buffer) | ||
if (info.charAt(0) === 'd') { | ||
badgeData.text[0] = getLabel('downloads', data) | ||
let downloads | ||
switch (info.charAt(1)) { | ||
case 'm': | ||
downloads = parsedData.downloads.monthly | ||
badgeData.text[1] = `${metric(downloads)}/month` | ||
break | ||
case 'w': | ||
downloads = parsedData.downloads.weekly | ||
badgeData.text[1] = `${metric(downloads)}/week` | ||
break | ||
case 'd': | ||
downloads = parsedData.downloads.daily | ||
badgeData.text[1] = `${metric(downloads)}/day` | ||
break | ||
case 't': | ||
downloads = parsedData.downloads.total | ||
badgeData.text[1] = metric(downloads) | ||
break | ||
} | ||
if (version) { | ||
badgeData.text[1] += ` ${versionText(version)}` | ||
} | ||
badgeData.colorscheme = downloadCountColor(downloads) | ||
sendBadge(format, badgeData) | ||
} | ||
} catch (e) { | ||
badgeData.text[1] = 'invalid' | ||
sendBadge(format, badgeData) | ||
} | ||
}) | ||
async handle({ packageName, version }) { | ||
const data = await this.fetch({ packageName, version }) | ||
return this.constructor.render({ | ||
downloads: data.downloads[interval], | ||
version, | ||
}) | ||
) | ||
} | ||
|
||
static get defaultBadgeData() { | ||
return { label: 'downloads' } | ||
} | ||
|
||
static get category() { | ||
return 'downloads' | ||
} | ||
|
||
static get route() { | ||
return { | ||
base, | ||
pattern: ':packageName/:version*', | ||
} | ||
} | ||
|
||
static get examples() { | ||
let examples = [ | ||
{ | ||
title: 'DUB', | ||
pattern: ':packageName', | ||
namedParams: { packageName: 'vibe-d' }, | ||
staticPreview: this.render({ downloads: 5000 }), | ||
}, | ||
] | ||
if (interval === 'monthly') { | ||
examples = examples.concat([ | ||
{ | ||
title: 'DUB (version)', | ||
pattern: ':packageName/:version', | ||
namedParams: { packageName: 'vibe-d', version: '0.8.4' }, | ||
staticPreview: this.render({ downloads: 100, version: '0.8.4' }), | ||
}, | ||
{ | ||
title: 'DUB (latest)', | ||
pattern: ':packageName/:version', | ||
namedParams: { packageName: 'vibe-d', version: 'latest' }, | ||
staticPreview: this.render({ downloads: 100, version: 'latest' }), | ||
}, | ||
]) | ||
} | ||
return examples | ||
} | ||
} | ||
} | ||
|
||
module.exports = ['daily', 'weekly', 'monthly', 'total'].map( | ||
DownloadsForInterval | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
'use strict' | ||
|
||
const Joi = require('joi') | ||
const ServiceTester = require('../service-tester') | ||
const { colorScheme } = require('../test-helpers') | ||
const { isMetric, isMetricOverTimePeriod } = require('../test-validators') | ||
|
||
const isDownloadsColor = Joi.equal( | ||
colorScheme.red, | ||
colorScheme.yellow, | ||
colorScheme.yellowgreen, | ||
colorScheme.green, | ||
colorScheme.brightgreen | ||
) | ||
|
||
const t = (module.exports = new ServiceTester({ | ||
id: 'dub', | ||
title: 'DubDownloads', | ||
})) | ||
|
||
t.create('total downloads (valid)') | ||
.get('/dt/vibe-d.json?style=_shields_test') | ||
.expectJSONTypes( | ||
Joi.object().keys({ | ||
name: 'downloads', | ||
value: isMetric, | ||
colorB: isDownloadsColor, | ||
}) | ||
) | ||
|
||
t.create('total downloads, specific version (valid)') | ||
.get('/dt/vibe-d/0.8.4.json?style=_shields_test') | ||
.expectJSONTypes( | ||
Joi.object().keys({ | ||
name: 'downloads@0.8.4', | ||
value: Joi.string().regex(/^[1-9][0-9]*[kMGTPEZY]?$/), | ||
colorB: isDownloadsColor, | ||
}) | ||
) | ||
.timeout(15000) | ||
|
||
t.create('total downloads, latest version (valid)') | ||
.get('/dt/vibe-d/latest.json?style=_shields_test') | ||
.expectJSONTypes( | ||
Joi.object().keys({ | ||
name: 'downloads@latest', | ||
value: Joi.string().regex(/^[1-9][0-9]*[kMGTPEZY]?$/), | ||
colorB: isDownloadsColor, | ||
}) | ||
) | ||
|
||
t.create('daily downloads (valid)') | ||
.get('/dd/vibe-d.json?style=_shields_test') | ||
.expectJSONTypes( | ||
Joi.object().keys({ | ||
name: 'downloads', | ||
value: isMetricOverTimePeriod, | ||
colorB: isDownloadsColor, | ||
}) | ||
) | ||
|
||
t.create('weekly downloads (valid)') | ||
.get('/dw/vibe-d.json?style=_shields_test') | ||
.expectJSONTypes( | ||
Joi.object().keys({ | ||
name: 'downloads', | ||
value: isMetricOverTimePeriod, | ||
colorB: isDownloadsColor, | ||
}) | ||
) | ||
|
||
t.create('monthly downloads (valid)') | ||
.get('/dm/vibe-d.json?style=_shields_test') | ||
.expectJSONTypes( | ||
Joi.object().keys({ | ||
name: 'downloads', | ||
value: isMetricOverTimePeriod, | ||
colorB: isDownloadsColor, | ||
}) | ||
) | ||
|
||
t.create('total downloads (not found)') | ||
.get('/dt/not-a-package.json') | ||
.expectJSON({ name: 'downloads', value: 'not found' }) |
Oops, something went wrong.