/**
* @module
*/
import Joi from 'joi'
import jp from 'jsonpath'
import { renderDynamicBadge, httpErrors } from '../dynamic-common.js'
import { InvalidParameter, InvalidResponse } from '../index.js'
/**
* Dynamic service class factory which wraps {@link module:core/base-service/base~BaseService} with support of {@link https://jsonpath.com/|JSONPath}.
*
* @param {Function} superclass class to extend
* @returns {Function} wrapped class
*/
export default superclass =>
class extends superclass {
static category = 'dynamic'
static defaultBadgeData = { label: 'custom badge' }
/**
* Request data from an upstream API, transform it to JSON and validate against a schema
*
* @param {object} attrs Refer to individual attrs
* @param {Joi} attrs.schema Joi schema to validate the response transformed to JSON
* @param {string} attrs.url URL to request
* @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/services/dynamic-common.js#L8)
* @returns {object} Parsed response
*/
async fetch({ schema, url, httpErrors }) {
throw new Error(
`fetch() function not implemented for ${this.constructor.name}`,
)
}
async handle(namedParams, { url, query: pathExpression, prefix, suffix }) {
const data = await this.fetch({
schema: Joi.any(),
url,
httpErrors,
})
// JSONPath only works on objects and arrays.
// https://github.com/badges/shields/issues/4018
if (typeof data !== 'object') {
throw new InvalidResponse({
prettyMessage: 'resource must contain an object or array',
})
}
let values
try {
values = jp.query(data, pathExpression)
} catch (e) {
const { message } = e
if (
message.startsWith('Lexical error') ||
message.startsWith('Parse error') ||
message.includes('Unexpected token')
) {
throw new InvalidParameter({
prettyMessage: 'unparseable jsonpath query',
})
} else {
throw e
}
}
if (!values.length) {
throw new InvalidResponse({ prettyMessage: 'no result' })
}
return renderDynamicBadge({ value: values, prefix, suffix })
}
}