Rewrite the DynamicJson badge (#2399)
This starts the rewrite of the dynamic badges. I've pulled into BaseService an initial version of the query param validation from #2325. I've extended from BaseJsonService to avoid duplicating the deserialization logic, though it means there is a bit of duplicated code among the three dynamic services. The way to unravel this would be to move the logic from `_requestJson` and friends from the base classes into functions so DynamicJson can inherit from BaseDynamic. Would that be worth it? This introduces a regression of #1446 for this badge. Close #2345
This commit is contained in:
39
services/dynamic/dynamic-helpers.js
Normal file
39
services/dynamic/dynamic-helpers.js
Normal file
@@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { optionalUrl } = require('../validators')
|
||||
|
||||
function createRoute(which) {
|
||||
return {
|
||||
base: `badge/dynamic/${which}`,
|
||||
pattern: '',
|
||||
queryParams: ['uri', 'url', 'query', 'prefix', 'suffix'],
|
||||
}
|
||||
}
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
url: optionalUrl.required(),
|
||||
query: Joi.string().required(),
|
||||
prefix: Joi.alternatives().try(Joi.string(), Joi.number()),
|
||||
suffix: Joi.alternatives().try(Joi.string(), Joi.number()),
|
||||
})
|
||||
.rename('uri', 'url', { ignoreUndefined: true, override: true })
|
||||
.required()
|
||||
|
||||
const errorMessages = {
|
||||
404: 'resource not found',
|
||||
}
|
||||
|
||||
function renderDynamicBadge({ values, prefix = '', suffix = '' }) {
|
||||
return {
|
||||
message: `${prefix}${values.join(', ')}${suffix}`,
|
||||
color: 'brightgreen',
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createRoute,
|
||||
queryParamSchema,
|
||||
errorMessages,
|
||||
renderDynamicBadge,
|
||||
}
|
||||
51
services/dynamic/dynamic-json.service.js
Normal file
51
services/dynamic/dynamic-json.service.js
Normal file
@@ -0,0 +1,51 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const jp = require('jsonpath')
|
||||
const BaseJsonService = require('../base-json')
|
||||
const { InvalidResponse } = require('../errors')
|
||||
const {
|
||||
createRoute,
|
||||
queryParamSchema,
|
||||
errorMessages,
|
||||
renderDynamicBadge,
|
||||
} = require('./dynamic-helpers')
|
||||
|
||||
module.exports = class DynamicJson extends BaseJsonService {
|
||||
static get category() {
|
||||
return 'dynamic'
|
||||
}
|
||||
|
||||
static get route() {
|
||||
return createRoute('json')
|
||||
}
|
||||
|
||||
static get defaultBadgeData() {
|
||||
return {
|
||||
label: 'custom badge',
|
||||
}
|
||||
}
|
||||
|
||||
async handle(namedParams, queryParams) {
|
||||
const {
|
||||
url,
|
||||
query: pathExpression,
|
||||
prefix,
|
||||
suffix,
|
||||
} = this.constructor._validateQueryParams(queryParams, queryParamSchema)
|
||||
|
||||
const data = await this._requestJson({
|
||||
schema: Joi.any(),
|
||||
url,
|
||||
errorMessages,
|
||||
})
|
||||
|
||||
const values = jp.query(data, pathExpression)
|
||||
|
||||
if (!values.length) {
|
||||
throw new InvalidResponse({ prettyMessage: 'no result' })
|
||||
}
|
||||
|
||||
return renderDynamicBadge({ values, prefix, suffix })
|
||||
}
|
||||
}
|
||||
156
services/dynamic/dynamic-json.tester.js
Normal file
156
services/dynamic/dynamic-json.tester.js
Normal file
@@ -0,0 +1,156 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { expect } = require('chai')
|
||||
const { colorScheme: colorsB } = require('../test-helpers')
|
||||
|
||||
const t = require('../create-service-tester')()
|
||||
module.exports = t
|
||||
|
||||
t.create('Connection error')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.name&label=Package Name&style=_shields_test'
|
||||
)
|
||||
.networkOff()
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'inaccessible',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('No URL specified')
|
||||
.get('.json?query=$.name&label=Package Name&style=_shields_test')
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'invalid query parameter: url',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('No query specified')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/package.json&label=Package Name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'invalid query parameter: query',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('Malformed url')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/%0package.json&query=$.name&label=Package Name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'invalid',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('JSON from url')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'shields.io',
|
||||
colorB: colorsB.brightgreen,
|
||||
})
|
||||
|
||||
t.create('JSON from uri (support uri query paramater)')
|
||||
.get(
|
||||
'.json?uri=https://github.com/badges/shields/raw/master/package.json&query=$.name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'shields.io',
|
||||
colorB: colorsB.brightgreen,
|
||||
})
|
||||
|
||||
t.create('JSON from url | multiple results')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/package.json&query=$..keywords[0:2:1]'
|
||||
)
|
||||
.expectJSON({ name: 'custom badge', value: 'GitHub, badge' })
|
||||
|
||||
t.create('JSON from url | caching with new query params')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.version'
|
||||
)
|
||||
.expectJSONTypes(
|
||||
Joi.object().keys({
|
||||
name: 'custom badge',
|
||||
value: Joi.string().regex(/^\d+(\.\d+)?(\.\d+)?$/),
|
||||
})
|
||||
)
|
||||
|
||||
t.create('JSON from url | with prefix & suffix & label')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.version&prefix=v&suffix= dev&label=Shields'
|
||||
)
|
||||
.expectJSONTypes(
|
||||
Joi.object().keys({
|
||||
name: 'Shields',
|
||||
value: Joi.string().regex(/^v\d+(\.\d+)?(\.\d+)?\sdev$/),
|
||||
})
|
||||
)
|
||||
|
||||
t.create('JSON from url | object doesnt exist')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.does_not_exist&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'no result',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('JSON from url | invalid url')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/notafile.json&query=$.version&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'resource not found',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('JSON from url | user color overrides default')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/package.json&query=$.name&colorB=10ADED&style=_shields_test'
|
||||
)
|
||||
.expectJSON({ name: 'custom badge', value: 'shields.io', colorB: '#10ADED' })
|
||||
|
||||
t.create('JSON from url | error color overrides default')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/notafile.json&query=$.version&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'resource not found',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
// FIXME This is a regression which should be fixed in BaseService.
|
||||
// t.create('JSON from url | error color overrides user specified')
|
||||
// .get('.json?query=$.version&colorB=10ADED&style=_shields_test')
|
||||
// .expectJSON({
|
||||
// name: 'custom badge',
|
||||
// value: 'invalid query parameter: url',
|
||||
// colorB: colorsB.red,
|
||||
// })
|
||||
|
||||
let headers
|
||||
t.create('JSON from url | request should set Accept header')
|
||||
.get('.json?url=https://json-test/api.json&query=$.name')
|
||||
.intercept(nock =>
|
||||
nock('https://json-test')
|
||||
.get('/api.json')
|
||||
.reply(200, function(uri, requestBody) {
|
||||
headers = this.req.headers
|
||||
return '{"name":"test"}'
|
||||
})
|
||||
)
|
||||
.expectJSON({ name: 'custom badge', value: 'test' })
|
||||
.after(() => {
|
||||
expect(headers).to.have.property('accept', 'application/json')
|
||||
})
|
||||
183
services/dynamic/dynamic-xml.tester.js
Normal file
183
services/dynamic/dynamic-xml.tester.js
Normal file
@@ -0,0 +1,183 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { expect } = require('chai')
|
||||
const ServiceTester = require('../service-tester')
|
||||
const { isSemver } = require('../test-validators')
|
||||
const { colorScheme: colorsB } = require('../test-helpers')
|
||||
|
||||
const t = new ServiceTester({
|
||||
id: 'dynamic-xml',
|
||||
title: 'User Defined XML Source Data',
|
||||
pathPrefix: '/badge/dynamic/xml',
|
||||
})
|
||||
module.exports = t
|
||||
|
||||
t.create('Connection error')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/addon/name&label=Package Name&style=_shields_test'
|
||||
)
|
||||
.networkOff()
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'inaccessible',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('No URL specified')
|
||||
.get('.json?query=//name&label=Package Name&style=_shields_test')
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'no url specified',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('No query specified')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&label=Package Name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'no query specified',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('XML from url')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/addon/name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'IndieGala Helper',
|
||||
colorB: colorsB.brightgreen,
|
||||
})
|
||||
|
||||
t.create('XML from uri (support uri query paramater)')
|
||||
.get(
|
||||
'.json?uri=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/addon/name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'IndieGala Helper',
|
||||
colorB: colorsB.brightgreen,
|
||||
})
|
||||
|
||||
t.create('XML from url (attribute)')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/addon/reviews/@num'
|
||||
)
|
||||
.expectJSONTypes(
|
||||
Joi.object().keys({
|
||||
name: 'custom badge',
|
||||
value: Joi.string().regex(/^\d+$/),
|
||||
})
|
||||
)
|
||||
|
||||
t.create('XML from url | multiple results')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/addon/compatible_applications/application/name'
|
||||
)
|
||||
.expectJSONTypes(
|
||||
Joi.object().keys({
|
||||
name: 'custom badge',
|
||||
value: Joi.string().regex(
|
||||
/^Firefox( for Android)?,\sFirefox( for Android)?$/
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
t.create('XML from url | caching with new query params')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/addon/version'
|
||||
)
|
||||
.expectJSONTypes(
|
||||
Joi.object().keys({
|
||||
name: 'custom badge',
|
||||
value: isSemver,
|
||||
})
|
||||
)
|
||||
|
||||
t.create('XML from url | with prefix & suffix & label')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=//version&prefix=v&suffix= dev&label=IndieGala Helper'
|
||||
)
|
||||
.expectJSONTypes(
|
||||
Joi.object().keys({
|
||||
name: 'IndieGala Helper',
|
||||
value: Joi.string().regex(/^v\d+(\.\d+)?(\.\d+)?\sdev$/),
|
||||
})
|
||||
)
|
||||
|
||||
t.create('XML from url | query doesnt exist')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/does/not/exist&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'no result',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('XML from url | query doesnt exist (attribute)')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/does/not/@exist&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'no result',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('XML from url | invalid url')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/notafile.xml&query=//version&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'resource not found',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('XML from url | user color overrides default')
|
||||
.get(
|
||||
'.json?url=https://services.addons.mozilla.org/en-US/firefox/api/1.5/addon/707078&query=/addon/name&colorB=10ADED&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'IndieGala Helper',
|
||||
colorB: '#10ADED',
|
||||
})
|
||||
|
||||
t.create('XML from url | error color overrides default')
|
||||
.get(
|
||||
'.json?url=https://github.com/badges/shields/raw/master/notafile.xml&query=//version&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'resource not found',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('XML from url | error color overrides user specified')
|
||||
.get('.json?query=//version&colorB=10ADED&style=_shields_test')
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'no url specified',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
let headers
|
||||
t.create('XML from url | request should set Accept header')
|
||||
.get('.json?url=https://xml-test/api.xml&query=/name')
|
||||
.intercept(nock =>
|
||||
nock('https://xml-test')
|
||||
.get('/api.xml')
|
||||
.reply(200, function(uri, requestBody) {
|
||||
headers = this.req.headers
|
||||
return '<?xml version="1.0" encoding="utf-8" ?><name>dynamic xml</name>'
|
||||
})
|
||||
)
|
||||
.expectJSON({ name: 'custom badge', value: 'dynamic xml' })
|
||||
.after(() => {
|
||||
expect(headers).to.have.property('accept', 'application/xml, text/xml')
|
||||
})
|
||||
122
services/dynamic/dynamic-yaml.tester.js
Normal file
122
services/dynamic/dynamic-yaml.tester.js
Normal file
@@ -0,0 +1,122 @@
|
||||
'use strict'
|
||||
|
||||
const ServiceTester = require('../service-tester')
|
||||
const { colorScheme: colorsB } = require('../test-helpers')
|
||||
|
||||
const t = new ServiceTester({
|
||||
id: 'dynamic-yaml',
|
||||
title: 'User Defined YAML Source Data',
|
||||
pathPrefix: '/badge/dynamic/yaml',
|
||||
})
|
||||
module.exports = t
|
||||
|
||||
t.create('Connection error')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name&label=Package Name&style=_shields_test'
|
||||
)
|
||||
.networkOff()
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'inaccessible',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('No URL specified')
|
||||
.get('.json?query=$.name&label=Package Name&style=_shields_test')
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'no url specified',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('No query specified')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&label=Package Name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'Package Name',
|
||||
value: 'no query specified',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
|
||||
t.create('YAML from url')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'coredns',
|
||||
colorB: colorsB.brightgreen,
|
||||
})
|
||||
|
||||
t.create('YAML from uri (support uri query paramater)')
|
||||
.get(
|
||||
'.json?uri=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'coredns',
|
||||
colorB: colorsB.brightgreen,
|
||||
})
|
||||
|
||||
t.create('YAML from url | multiple results')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$..keywords[0:2:1]'
|
||||
)
|
||||
.expectJSON({ name: 'custom badge', value: 'coredns, dns' })
|
||||
|
||||
t.create('YAML from url | caching with new query params')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.version'
|
||||
)
|
||||
.expectJSON({ name: 'custom badge', value: '0.8.0' })
|
||||
|
||||
t.create('YAML from url | with prefix & suffix & label')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.version&prefix=v&suffix= dev&label=Shields'
|
||||
)
|
||||
.expectJSON({ name: 'Shields', value: 'v0.8.0 dev' })
|
||||
|
||||
t.create('YAML from url | object doesnt exist')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.does_not_exist&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'no result',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('YAML from url | invalid url')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/notafile.yaml&query=$.version&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'resource not found',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('YAML from url | user color overrides default')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/Chart.yaml&query=$.name&colorB=10ADED&style=_shields_test'
|
||||
)
|
||||
.expectJSON({ name: 'custom badge', value: 'coredns', colorB: '#10ADED' })
|
||||
|
||||
t.create('YAML from url | error color overrides default')
|
||||
.get(
|
||||
'.json?url=https://raw.githubusercontent.com/kubernetes/charts/568291d6e476c39ca8322c30c3f601d0383d4760/stable/coredns/notafile.yaml&query=$.version&style=_shields_test'
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'resource not found',
|
||||
colorB: colorsB.lightgrey,
|
||||
})
|
||||
|
||||
t.create('YAML from url | error color overrides user specified')
|
||||
.get('.json?query=$.version&colorB=10ADED&style=_shields_test')
|
||||
.expectJSON({
|
||||
name: 'custom badge',
|
||||
value: 'no url specified',
|
||||
colorB: colorsB.red,
|
||||
})
|
||||
Reference in New Issue
Block a user