* log to sentry if upstream service responds with 429 * allow services to decide which error(s) to log, default to 429 * don't log 429s from endpoint or dynamic badges * supress 429s from uptime robot badges * supress 429s from weblate if not calling default server * cache opencollective badges for longer * cache discord badges for longer * cache github workflow badges for longer
104 lines
3.6 KiB
JavaScript
104 lines
3.6 KiB
JavaScript
/**
|
|
* @module
|
|
*/
|
|
|
|
// See available emoji at http://emoji.muan.co/
|
|
import emojic from 'emojic'
|
|
import BaseService from './base.js'
|
|
import trace from './trace.js'
|
|
import { InvalidResponse } from './errors.js'
|
|
|
|
const defaultValueMatcher = />([^<>]+)<\/text><\/g>/
|
|
const leadingWhitespace = /(?:\r\n\s*|\r\s*|\n\s*)/g
|
|
|
|
/**
|
|
* Services which scrape data from another SVG badge
|
|
* should extend BaseSvgScrapingService
|
|
*
|
|
* @abstract
|
|
*/
|
|
class BaseSvgScrapingService extends BaseService {
|
|
/**
|
|
* Extract a value from SVG
|
|
*
|
|
* @param {string} svg SVG to parse
|
|
* @param {RegExp} [valueMatcher=defaultValueMatcher]
|
|
* RegExp to match the value we want to parse from the SVG
|
|
* @returns {string} Matched value
|
|
*/
|
|
static valueFromSvgBadge(svg, valueMatcher = defaultValueMatcher) {
|
|
if (typeof svg !== 'string') {
|
|
throw new TypeError('Parameter should be a string')
|
|
}
|
|
const stripped = svg.replace(leadingWhitespace, '')
|
|
const match = valueMatcher.exec(stripped)
|
|
if (match) {
|
|
return match[1]
|
|
} else {
|
|
throw new InvalidResponse({
|
|
prettyMessage: 'unparseable svg response',
|
|
underlyingError: Error(`Can't get value from SVG:\n${svg}`),
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Request data from an endpoint serving SVG,
|
|
* parse a value from it and validate against a schema
|
|
*
|
|
* @param {object} attrs Refer to individual attrs
|
|
* @param {Joi} attrs.schema Joi schema to validate the response against
|
|
* @param {RegExp} attrs.valueMatcher
|
|
* RegExp to match the value we want to parse from the SVG
|
|
* @param {string} attrs.url URL to request
|
|
* @param {object} [attrs.options={}] Options to pass to got. See
|
|
* [documentation](https://github.com/sindresorhus/got/blob/main/documentation/2-options.md)
|
|
* @param {object} [attrs.httpErrors={}] Key-value map of status codes
|
|
* and custom error messages e.g: `{ 404: 'package not found' }`.
|
|
* This can be used to extend or override the
|
|
* [default](https://github.com/badges/shields/blob/master/core/base-service/check-error-response.js#L5)
|
|
* @param {object} [attrs.systemErrors={}] Key-value map of got network exception codes
|
|
* and an object of params to pass when we construct an Inaccessible exception object
|
|
* e.g: `{ ECONNRESET: { prettyMessage: 'connection reset' } }`.
|
|
* See {@link https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md#errorcodes got error codes}
|
|
* for allowed keys
|
|
* and {@link module:core/base-service/errors~RuntimeErrorProps} for allowed values
|
|
* @param {number[]} [attrs.logErrors=[429]] An array of http error codes
|
|
* that will be logged (to sentry, if configured).
|
|
* @returns {object} Parsed response
|
|
* @see https://github.com/sindresorhus/got/blob/main/documentation/2-options.md
|
|
*/
|
|
async _requestSvg({
|
|
schema,
|
|
valueMatcher,
|
|
url,
|
|
options = {},
|
|
httpErrors = {},
|
|
systemErrors = {},
|
|
logErrors = [429],
|
|
}) {
|
|
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
|
const mergedOptions = {
|
|
...{ headers: { Accept: 'image/svg+xml' } },
|
|
...options,
|
|
}
|
|
const { buffer } = await this._request({
|
|
url,
|
|
options: mergedOptions,
|
|
httpErrors,
|
|
systemErrors,
|
|
logErrors,
|
|
})
|
|
logTrace(emojic.dart, 'Response SVG', buffer)
|
|
const data = {
|
|
message: this.constructor.valueFromSvgBadge(buffer, valueMatcher),
|
|
}
|
|
logTrace(emojic.dart, 'Response SVG (before validation)', data, {
|
|
deep: true,
|
|
})
|
|
return this.constructor._validate(data, schema)
|
|
}
|
|
}
|
|
|
|
export default BaseSvgScrapingService
|