Files
shields/lib/regular-update.js
Paul Melnikow 5d63effabc Fix a crasher in production (#2313)
When I deployed 5e99aad2de to s0, shortly after Sentry picked up an unhandled error. I'm not sure which of the legacy badges this is in.

The bug was introduced just now, in #2257. Oops!

A test would have caught this, but I don't think it's worth wrapping tests around this difficult-to-test code. It makes more sense I think, to refactor the remaining badges that use it, replace it with something using async/await (maybe based on [node-cache](https://www.npmjs.com/package/node-cache), and test that.
2018-11-15 14:25:46 -05:00

102 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) {
cb(
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,
}