The NuGet badge examples are straggling in all-badge-examples. Rather than move them as is, I thought it made more sense to refactor the services and see if they could be generated. I didn't take that on here; this is a straight rewrite of the badges. The old implementations were fairly difficult to follow. The new implementations are complicated too, though I hope much more readable. Though the NuGet behaviors could be consolidated into a single flag, I split `withTenant` and `withFeed` into separate flags, thinking naming the behaviors makes the implementations easier to understand. I defaulted these to true, thinking that really this is really a MyGet implementation which is generalized to NuGet. Though maybe it makes more sense to have the MyGet style as the default. Probably it doesn't matter much either way. I added a helper class ServiceUrlBuilder to construct the Shields service URL. It's useful in this complex case where the URL must be built up conditionally. This might be useful in a couple other places. I also wrote a new service to handle the Powershell badges. They've diverged a little bit from the Nuget v2. There's a bit of shared code which I factored out. If the XML Nuget APIs are more reliable, we could consider switching everything else over to them, though for now I would like to get this merged and get #2078 fixed. Fix #2078
100 lines
2.1 KiB
JavaScript
100 lines
2.1 KiB
JavaScript
'use strict'
|
|
|
|
const { Inaccessible, InvalidResponse } = require('../services/errors')
|
|
|
|
// Map from URL to { timestamp: last fetch time, data: data }.
|
|
let regularUpdateCache = Object.create(null)
|
|
|
|
// url: a string, scraper: a function that takes string data at that URL.
|
|
// interval: number in milliseconds.
|
|
// cb: a callback function that takes an error and data returned by the scraper.
|
|
//
|
|
// To use this from a service:
|
|
//
|
|
// const { promisify } = require('util')
|
|
// const { regularUpdate } = require('../../lib/regular-update')
|
|
//
|
|
// function getThing() {
|
|
// return promisify(regularUpdate)({
|
|
// url: ...,
|
|
// ...
|
|
// })
|
|
// }
|
|
//
|
|
// in handle():
|
|
//
|
|
// const thing = await getThing()
|
|
|
|
function regularUpdate(
|
|
{
|
|
url,
|
|
intervalMillis,
|
|
json = true,
|
|
scraper = buffer => buffer,
|
|
options = {},
|
|
request = require('request'),
|
|
},
|
|
cb
|
|
) {
|
|
const timestamp = Date.now()
|
|
const cached = regularUpdateCache[url]
|
|
if (cached != null && timestamp - cached.timestamp < intervalMillis) {
|
|
cb(null, cached.data)
|
|
return
|
|
}
|
|
request(url, options, (err, res, buffer) => {
|
|
if (err != null) {
|
|
cb(
|
|
new Inaccessible({
|
|
prettyMessage: 'intermediate resource inaccessible',
|
|
underlyingError: err,
|
|
})
|
|
)
|
|
return
|
|
}
|
|
|
|
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
throw new InvalidResponse({
|
|
prettyMessage: 'intermediate resource inaccessible',
|
|
})
|
|
}
|
|
|
|
let reqData
|
|
if (json) {
|
|
try {
|
|
reqData = JSON.parse(buffer)
|
|
} catch (e) {
|
|
cb(
|
|
new InvalidResponse({
|
|
prettyMessage: 'unparseable intermediate json response',
|
|
underlyingError: e,
|
|
})
|
|
)
|
|
return
|
|
}
|
|
} else {
|
|
reqData = buffer
|
|
}
|
|
|
|
let data
|
|
try {
|
|
data = scraper(reqData)
|
|
} catch (e) {
|
|
cb(e)
|
|
return
|
|
}
|
|
|
|
regularUpdateCache[url] = { timestamp, data }
|
|
cb(null, data)
|
|
})
|
|
}
|
|
|
|
function clearRegularUpdateCache() {
|
|
regularUpdateCache = Object.create(null)
|
|
}
|
|
|
|
module.exports = {
|
|
regularUpdate,
|
|
clearRegularUpdateCache,
|
|
}
|