mirror of
https://github.com/badges/shields.git
synced 2025-04-18 19:44:04 +03:00
Convert examples
arrays to openApi
objects (part 1) (#9320)
* add helper functions for generating Open API path/query params with defaults * tweak Open API schema - make description optional - allow null example + allowEmptyValue (for boolean query params) * convert examples --> openApi in amo * convert examples --> openApi in ansible * convert examples --> openApi in appveyor build/job * add re-usable Open API query param for test-results badges we can use these for all the 'test results' badges * convert examples --> openApi in appveyor tests * DRY up existing dynamic/endpoint param definitions * DRY up queryParam * allow enum param in serviceDefinition schema * improve misleading param name * check route and openApi are consistent on service load * fix mistake in ansible role route * documentation --> description * add pathParams and queryParams helpers +docstrings * give everything a search-friendly summary, check for duplicate summary * prettier fixup
This commit is contained in:
parent
1bfda7a54b
commit
57c2ba0d68
@ -189,6 +189,22 @@ class BaseService {
|
||||
this.examples.forEach((example, index) =>
|
||||
validateExample(example, index, this),
|
||||
)
|
||||
|
||||
// ensure openApi spec matches route
|
||||
if (this.openApi) {
|
||||
const preparedRoute = prepareRoute(this.route)
|
||||
for (const [key, value] of Object.entries(this.openApi)) {
|
||||
let example = key
|
||||
for (const param of value.get.parameters) {
|
||||
example = example.replace(`{${param.name}}`, param.example)
|
||||
}
|
||||
if (!example.match(preparedRoute.regex)) {
|
||||
throw new Error(
|
||||
`Inconsistent Open Api spec and Route found for service ${this.name}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getDefinition() {
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
Deprecated,
|
||||
ImproperlyConfigured,
|
||||
} from './errors.js'
|
||||
import { pathParam, pathParams, queryParam, queryParams } from './openapi.js'
|
||||
|
||||
export {
|
||||
BaseService,
|
||||
@ -32,4 +33,8 @@ export {
|
||||
InvalidParameter,
|
||||
ImproperlyConfigured,
|
||||
Deprecated,
|
||||
pathParam,
|
||||
pathParams,
|
||||
queryParam,
|
||||
queryParams,
|
||||
}
|
||||
|
@ -83,6 +83,18 @@ async function loadServiceClasses(servicePaths) {
|
||||
},
|
||||
)
|
||||
|
||||
const routeSummaries = []
|
||||
serviceClasses.forEach(function (serviceClass) {
|
||||
if (serviceClass.openApi) {
|
||||
for (const route of Object.values(serviceClass.openApi)) {
|
||||
routeSummaries.push(route.get.summary)
|
||||
}
|
||||
}
|
||||
})
|
||||
assertNamesUnique(routeSummaries, {
|
||||
message: 'Duplicate route summary found',
|
||||
})
|
||||
|
||||
return serviceClasses
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
/**
|
||||
* Functions for publishing the shields.io URL schema as an OpenAPI Document
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
const baseUrl = process.env.BASE_URL
|
||||
const globalParamRefs = [
|
||||
{ $ref: '#/components/parameters/style' },
|
||||
@ -332,4 +338,132 @@ function category2openapi(category, services) {
|
||||
return spec
|
||||
}
|
||||
|
||||
export { category2openapi }
|
||||
/**
|
||||
* Helper function for assembling an OpenAPI path parameter object
|
||||
*
|
||||
* @param {module:core/base-service/openapi~PathParamInput} param Input param
|
||||
* @returns {module:core/base-service/openapi~OpenApiParam} OpenAPI Parameter Object
|
||||
* @see https://swagger.io/specification/#parameter-object
|
||||
*/
|
||||
function pathParam({
|
||||
name,
|
||||
example,
|
||||
schema = { type: 'string' },
|
||||
description,
|
||||
}) {
|
||||
return { name, in: 'path', required: true, schema, example, description }
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for assembling an array of OpenAPI path parameter objects
|
||||
* The code
|
||||
* ```
|
||||
* const params = pathParams(
|
||||
* { name: 'name1', example: 'example1' },
|
||||
* { name: 'name2', example: 'example2' },
|
||||
* )
|
||||
* ```
|
||||
* is equivilent to
|
||||
* ```
|
||||
* const params = [
|
||||
* pathParam({ name: 'name1', example: 'example1' }),
|
||||
* pathParam({ name: 'name2', example: 'example2' }),
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param {...module:core/base-service/openapi~PathParamInput} params Input params
|
||||
* @returns {Array.<module:core/base-service/openapi~OpenApiParam>} Array of OpenAPI Parameter Objects
|
||||
* @see {@link module:core/base-service/openapi~pathParam}
|
||||
*/
|
||||
function pathParams(...params) {
|
||||
return params.map(param => pathParam(param))
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for assembling an OpenAPI query parameter object
|
||||
*
|
||||
* @param {module:core/base-service/openapi~QueryParamInput} param Input param
|
||||
* @returns {module:core/base-service/openapi~OpenApiParam} OpenAPI Parameter Object
|
||||
* @see https://swagger.io/specification/#parameter-object
|
||||
*/
|
||||
function queryParam({
|
||||
name,
|
||||
example,
|
||||
schema = { type: 'string' },
|
||||
required = false,
|
||||
description,
|
||||
}) {
|
||||
const param = { name, in: 'query', required, schema, example, description }
|
||||
if (example === null && schema.type === 'boolean') {
|
||||
param.allowEmptyValue = true
|
||||
}
|
||||
return param
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for assembling an array of OpenAPI query parameter objects
|
||||
* The code
|
||||
* ```
|
||||
* const params = queryParams(
|
||||
* { name: 'name1', example: 'example1' },
|
||||
* { name: 'name2', example: 'example2' },
|
||||
* )
|
||||
* ```
|
||||
* is equivilent to
|
||||
* ```
|
||||
* const params = [
|
||||
* queryParam({ name: 'name1', example: 'example1' }),
|
||||
* queryParams({ name: 'name2', example: 'example2' }),
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* @param {...module:core/base-service/openapi~QueryParamInput} params Input params
|
||||
* @returns {Array.<module:core/base-service/openapi~OpenApiParam>} Array of OpenAPI Parameter Objects
|
||||
* @see {@link module:core/base-service/openapi~queryParam}
|
||||
*/
|
||||
function queryParams(...params) {
|
||||
return params.map(param => queryParam(param))
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} PathParamInput
|
||||
* @property {string} name The name of the parameter. Parameter names are case sensitive
|
||||
* @property {string} example Example of a valid value for this parameter
|
||||
* @property {object} [schema={ type: 'string' }] Parameter schema.
|
||||
* An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
|
||||
* specifying the parameter type.
|
||||
* Normally this should be omitted as all path parameters are strings.
|
||||
* Use this when we also want to pass an enum of valid parameters
|
||||
* to be presented as a drop-down in the frontend. e.g:
|
||||
* `{'type': 'string', 'enum': ['github', 'bitbucket'}` (Optional)
|
||||
* @property {string} description A brief description of the parameter (Optional)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} QueryParamInput
|
||||
* @property {string} name The name of the parameter. Parameter names are case sensitive
|
||||
* @property {string|null} example Example of a valid value for this parameter
|
||||
* @property {object} [schema={ type: 'string' }] Parameter schema.
|
||||
* An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
|
||||
* specifying the parameter type. This can normally be omitted.
|
||||
* Query params are usually strings. (Optional)
|
||||
* @property {boolean} [required=false] Determines whether this parameter is mandatory (Optional)
|
||||
* @property {string} description A brief description of the parameter (Optional)
|
||||
*/
|
||||
|
||||
/**
|
||||
* OpenAPI Parameter Object
|
||||
*
|
||||
* @typedef {object} OpenApiParam
|
||||
* @property {string} name The name of the parameter
|
||||
* @property {string|null} example Example of a valid value for this parameter
|
||||
* @property {('path'|'query')} in The location of the parameter
|
||||
* @property {object} schema Parameter schema.
|
||||
* An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
|
||||
* specifying the parameter type.
|
||||
* @property {boolean} required Determines whether this parameter is mandatory
|
||||
* @property {string} description A brief description of the parameter
|
||||
* @property {boolean} allowEmptyValue If true, allows the ability to pass an empty value to this parameter
|
||||
*/
|
||||
|
||||
export { category2openapi, pathParam, pathParams, queryParam, queryParams }
|
||||
|
@ -1,5 +1,11 @@
|
||||
import chai from 'chai'
|
||||
import { category2openapi } from './openapi.js'
|
||||
import {
|
||||
category2openapi,
|
||||
pathParam,
|
||||
pathParams,
|
||||
queryParam,
|
||||
queryParams,
|
||||
} from './openapi.js'
|
||||
import BaseJsonService from './base-json.js'
|
||||
const { expect } = chai
|
||||
|
||||
@ -376,3 +382,148 @@ describe('category2openapi', function () {
|
||||
).to.deep.equal(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('pathParam, pathParams', function () {
|
||||
it('generates a pathParam with defaults', function () {
|
||||
const input = { name: 'name', example: 'example' }
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
example: 'example',
|
||||
description: undefined,
|
||||
}
|
||||
expect(pathParam(input)).to.deep.equal(expected)
|
||||
expect(pathParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates a pathParam with custom args', function () {
|
||||
const input = {
|
||||
name: 'name',
|
||||
example: true,
|
||||
schema: { type: 'boolean' },
|
||||
description: 'long desc',
|
||||
}
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'boolean',
|
||||
},
|
||||
example: true,
|
||||
description: 'long desc',
|
||||
}
|
||||
expect(pathParam(input)).to.deep.equal(expected)
|
||||
expect(pathParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates multiple pathParams', function () {
|
||||
expect(
|
||||
pathParams(
|
||||
{ name: 'name1', example: 'example1' },
|
||||
{ name: 'name2', example: 'example2' },
|
||||
),
|
||||
).to.deep.equal([
|
||||
{
|
||||
name: 'name1',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
example: 'example1',
|
||||
description: undefined,
|
||||
},
|
||||
{
|
||||
name: 'name2',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
},
|
||||
example: 'example2',
|
||||
description: undefined,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('queryParam, queryParams', function () {
|
||||
it('generates a queryParam with defaults', function () {
|
||||
const input = { name: 'name', example: 'example' }
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: 'example',
|
||||
description: undefined,
|
||||
}
|
||||
expect(queryParam(input)).to.deep.equal(expected)
|
||||
expect(queryParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates queryParam with custom args', function () {
|
||||
const input = {
|
||||
name: 'name',
|
||||
example: 'example',
|
||||
required: true,
|
||||
description: 'long desc',
|
||||
}
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: 'example',
|
||||
description: 'long desc',
|
||||
}
|
||||
expect(queryParam(input)).to.deep.equal(expected)
|
||||
expect(queryParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates a queryParam with boolean/null example', function () {
|
||||
const input = { name: 'name', example: null, schema: { type: 'boolean' } }
|
||||
const expected = {
|
||||
name: 'name',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'boolean' },
|
||||
allowEmptyValue: true,
|
||||
example: null,
|
||||
description: undefined,
|
||||
}
|
||||
expect(queryParam(input)).to.deep.equal(expected)
|
||||
expect(queryParams(input)[0]).to.deep.equal(expected)
|
||||
})
|
||||
|
||||
it('generates multiple queryParams', function () {
|
||||
expect(
|
||||
queryParams(
|
||||
{ name: 'name1', example: 'example1' },
|
||||
{ name: 'name2', example: 'example2' },
|
||||
),
|
||||
).to.deep.equal([
|
||||
{
|
||||
name: 'name1',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: 'example1',
|
||||
description: undefined,
|
||||
},
|
||||
{
|
||||
name: 'name2',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: 'example2',
|
||||
description: undefined,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
@ -48,7 +48,7 @@ const serviceDefinition = Joi.object({
|
||||
Joi.object({
|
||||
get: Joi.object({
|
||||
summary: Joi.string().required(),
|
||||
description: Joi.string().required(),
|
||||
description: Joi.string(),
|
||||
parameters: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
@ -56,8 +56,12 @@ const serviceDefinition = Joi.object({
|
||||
description: Joi.string(),
|
||||
in: Joi.string().valid('query', 'path').required(),
|
||||
required: Joi.boolean().required(),
|
||||
schema: Joi.object({ type: Joi.string().required() }).required(),
|
||||
example: Joi.string(),
|
||||
schema: Joi.object({
|
||||
type: Joi.string().required(),
|
||||
enum: Joi.array(),
|
||||
}).required(),
|
||||
allowEmptyValue: Joi.boolean(),
|
||||
example: Joi.string().allow(null),
|
||||
}),
|
||||
)
|
||||
.min(1)
|
||||
@ -67,8 +71,8 @@ const serviceDefinition = Joi.object({
|
||||
),
|
||||
}).required()
|
||||
|
||||
function assertValidServiceDefinition(example, message = undefined) {
|
||||
Joi.assert(example, serviceDefinition, message)
|
||||
function assertValidServiceDefinition(service, message = undefined) {
|
||||
Joi.assert(service, serviceDefinition, message)
|
||||
}
|
||||
|
||||
const serviceDefinitionExport = Joi.object({
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { renderDownloadsBadge } from '../downloads.js'
|
||||
import { redirector } from '../index.js'
|
||||
import { BaseAmoService, keywords } from './amo-base.js'
|
||||
import { redirector, pathParams } from '../index.js'
|
||||
import { BaseAmoService } from './amo-base.js'
|
||||
|
||||
const documentation = `
|
||||
const description = `
|
||||
Previously \`amo/d\` provided a “total downloads” badge. However,
|
||||
[updates to the v3 API](https://github.com/badges/shields/issues/3079)
|
||||
only give us weekly downloads. The route \`amo/d\` redirects to \`amo/dw\`.
|
||||
@ -12,15 +12,15 @@ class AmoWeeklyDownloads extends BaseAmoService {
|
||||
static category = 'downloads'
|
||||
static route = { base: 'amo/dw', pattern: ':addonId' }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Mozilla Add-on',
|
||||
namedParams: { addonId: 'dustman' },
|
||||
staticPreview: this.render({ downloads: 120 }),
|
||||
keywords,
|
||||
documentation,
|
||||
static openApi = {
|
||||
'/amo/dw/{addonId}': {
|
||||
get: {
|
||||
summary: 'Mozilla Add-on Downloads',
|
||||
description,
|
||||
parameters: pathParams({ name: 'addonId', example: 'dustman' }),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static _cacheLength = 21600
|
||||
|
||||
|
@ -1,27 +1,26 @@
|
||||
import { starRating } from '../text-formatters.js'
|
||||
import { floorCount as floorCountColor } from '../color-formatters.js'
|
||||
import { BaseAmoService, keywords } from './amo-base.js'
|
||||
import { pathParams } from '../index.js'
|
||||
import { BaseAmoService } from './amo-base.js'
|
||||
|
||||
export default class AmoRating extends BaseAmoService {
|
||||
static category = 'rating'
|
||||
static route = { base: 'amo', pattern: ':format(stars|rating)/:addonId' }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Mozilla Add-on',
|
||||
pattern: 'rating/:addonId',
|
||||
namedParams: { addonId: 'dustman' },
|
||||
staticPreview: this.render({ format: 'rating', rating: 4 }),
|
||||
keywords,
|
||||
static openApi = {
|
||||
'/amo/rating/{addonId}': {
|
||||
get: {
|
||||
summary: 'Mozilla Add-on Rating',
|
||||
parameters: pathParams({ name: 'addonId', example: 'dustman' }),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Mozilla Add-on',
|
||||
pattern: 'stars/:addonId',
|
||||
namedParams: { addonId: 'dustman' },
|
||||
staticPreview: this.render({ format: 'stars', rating: 4 }),
|
||||
keywords,
|
||||
'/amo/stars/{addonId}': {
|
||||
get: {
|
||||
summary: 'Mozilla Add-on Stars',
|
||||
parameters: pathParams({ name: 'addonId', example: 'dustman' }),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static _cacheLength = 7200
|
||||
|
||||
|
@ -1,18 +1,19 @@
|
||||
import { renderDownloadsBadge } from '../downloads.js'
|
||||
import { BaseAmoService, keywords } from './amo-base.js'
|
||||
import { pathParams } from '../index.js'
|
||||
import { BaseAmoService } from './amo-base.js'
|
||||
|
||||
export default class AmoUsers extends BaseAmoService {
|
||||
static category = 'downloads'
|
||||
static route = { base: 'amo/users', pattern: ':addonId' }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Mozilla Add-on',
|
||||
namedParams: { addonId: 'dustman' },
|
||||
staticPreview: this.render({ users: 750 }),
|
||||
keywords,
|
||||
static openApi = {
|
||||
'/amo/users/{addonId}': {
|
||||
get: {
|
||||
summary: 'Mozilla Add-on Users',
|
||||
parameters: pathParams({ name: 'addonId', example: 'dustman' }),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static _cacheLength = 21600
|
||||
|
||||
|
@ -1,18 +1,19 @@
|
||||
import { renderVersionBadge } from '../version.js'
|
||||
import { BaseAmoService, keywords } from './amo-base.js'
|
||||
import { pathParams } from '../index.js'
|
||||
import { BaseAmoService } from './amo-base.js'
|
||||
|
||||
export default class AmoVersion extends BaseAmoService {
|
||||
static category = 'version'
|
||||
static route = { base: 'amo/v', pattern: ':addonId' }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Mozilla Add-on',
|
||||
namedParams: { addonId: 'dustman' },
|
||||
staticPreview: renderVersionBadge({ version: '2.1.0' }),
|
||||
keywords,
|
||||
static openApi = {
|
||||
'/amo/v/{addonId}': {
|
||||
get: {
|
||||
summary: 'Mozilla Add-on Version',
|
||||
parameters: pathParams({ name: 'addonId', example: 'dustman' }),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
async handle({ addonId }) {
|
||||
const data = await this.fetch({ addonId })
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Joi from 'joi'
|
||||
import { BaseJsonService } from '../index.js'
|
||||
import { BaseJsonService, pathParams } from '../index.js'
|
||||
|
||||
const ansibleCollectionSchema = Joi.object({
|
||||
name: Joi.string().required(),
|
||||
@ -12,15 +12,14 @@ class AnsibleGalaxyCollectionName extends BaseJsonService {
|
||||
static category = 'other'
|
||||
static route = { base: 'ansible/collection', pattern: ':collectionId' }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Ansible Collection',
|
||||
namedParams: { collectionId: '278' },
|
||||
staticPreview: this.render({
|
||||
name: 'community.general',
|
||||
}),
|
||||
static openApi = {
|
||||
'/ansible/collection/{collectionId}': {
|
||||
get: {
|
||||
summary: 'Ansible Collection',
|
||||
parameters: pathParams({ name: 'collectionId', example: '278' }),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static defaultBadgeData = { label: 'collection' }
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Joi from 'joi'
|
||||
import { floorCount } from '../color-formatters.js'
|
||||
import { BaseJsonService, InvalidResponse } from '../index.js'
|
||||
import { BaseJsonService, InvalidResponse, pathParams } from '../index.js'
|
||||
|
||||
const ansibleContentSchema = Joi.object({
|
||||
quality_score: Joi.number().allow(null).required(),
|
||||
@ -20,15 +20,14 @@ export default class AnsibleGalaxyContentQualityScore extends AnsibleGalaxyConte
|
||||
static category = 'analysis'
|
||||
static route = { base: 'ansible/quality', pattern: ':projectId' }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Ansible Quality Score',
|
||||
namedParams: {
|
||||
projectId: '432',
|
||||
static openApi = {
|
||||
'/ansible/quality/{projectId}': {
|
||||
get: {
|
||||
summary: 'Ansible Quality Score',
|
||||
parameters: pathParams({ name: 'projectId', example: '432' }),
|
||||
},
|
||||
staticPreview: this.render({ qualityScore: 4.125 }),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static defaultBadgeData = { label: 'quality' }
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Joi from 'joi'
|
||||
import { renderDownloadsBadge } from '../downloads.js'
|
||||
import { nonNegativeInteger } from '../validators.js'
|
||||
import { BaseJsonService } from '../index.js'
|
||||
import { BaseJsonService, pathParams } from '../index.js'
|
||||
|
||||
const ansibleRoleSchema = Joi.object({
|
||||
download_count: nonNegativeInteger,
|
||||
@ -27,13 +27,14 @@ class AnsibleGalaxyRoleDownloads extends AnsibleGalaxyRole {
|
||||
static category = 'downloads'
|
||||
static route = { base: 'ansible/role/d', pattern: ':roleId' }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Ansible Role',
|
||||
namedParams: { roleId: '3078' },
|
||||
staticPreview: renderDownloadsBadge({ downloads: 76 }),
|
||||
static openApi = {
|
||||
'/ansible/role/d/{roleId}': {
|
||||
get: {
|
||||
summary: 'Ansible Role',
|
||||
parameters: pathParams({ name: 'roleId', example: '3078' }),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static defaultBadgeData = { label: 'role downloads' }
|
||||
|
||||
@ -47,15 +48,14 @@ class AnsibleGalaxyRoleName extends AnsibleGalaxyRole {
|
||||
static category = 'other'
|
||||
static route = { base: 'ansible/role', pattern: ':roleId' }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Ansible Role',
|
||||
namedParams: { roleId: '3078' },
|
||||
staticPreview: this.render({
|
||||
name: 'ansible-roles.sublimetext3_packagecontrol',
|
||||
}),
|
||||
static openApi = {
|
||||
'/ansible/role/{roleId}': {
|
||||
get: {
|
||||
summary: 'Ansible Galaxy Role Name',
|
||||
parameters: pathParams({ name: 'roleId', example: '3078' }),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static defaultBadgeData = { label: 'role' }
|
||||
|
||||
|
@ -1,23 +1,31 @@
|
||||
import { renderBuildStatusBadge } from '../build-status.js'
|
||||
import { pathParams } from '../index.js'
|
||||
import AppVeyorBase from './appveyor-base.js'
|
||||
|
||||
export default class AppVeyorBuild extends AppVeyorBase {
|
||||
static route = this.buildRoute('appveyor/build')
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'AppVeyor',
|
||||
pattern: ':user/:repo',
|
||||
namedParams: { user: 'gruntjs', repo: 'grunt' },
|
||||
staticPreview: this.render({ status: 'success' }),
|
||||
static openApi = {
|
||||
'/appveyor/build/{user}/{repo}': {
|
||||
get: {
|
||||
summary: 'AppVeyor Build',
|
||||
parameters: pathParams(
|
||||
{ name: 'user', example: 'gruntjs' },
|
||||
{ name: 'repo', example: 'grunt' },
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'AppVeyor branch',
|
||||
pattern: ':user/:repo/:branch',
|
||||
namedParams: { user: 'gruntjs', repo: 'grunt', branch: 'master' },
|
||||
staticPreview: this.render({ status: 'success' }),
|
||||
'/appveyor/build/{user}/{repo}/{branch}': {
|
||||
get: {
|
||||
summary: 'AppVeyor Build (with branch)',
|
||||
parameters: pathParams(
|
||||
{ name: 'user', example: 'gruntjs' },
|
||||
{ name: 'repo', example: 'grunt' },
|
||||
{ name: 'branch', example: 'master' },
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static render({ status }) {
|
||||
return renderBuildStatusBadge({ status })
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { renderBuildStatusBadge } from '../build-status.js'
|
||||
import { NotFound } from '../index.js'
|
||||
import { NotFound, pathParams } from '../index.js'
|
||||
import AppVeyorBase from './appveyor-base.js'
|
||||
|
||||
export default class AppVeyorJobBuild extends AppVeyorBase {
|
||||
@ -8,29 +8,29 @@ export default class AppVeyorJobBuild extends AppVeyorBase {
|
||||
pattern: ':user/:repo/:job/:branch*',
|
||||
}
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'AppVeyor Job',
|
||||
pattern: ':user/:repo/:job',
|
||||
namedParams: {
|
||||
user: 'wpmgprostotema',
|
||||
repo: 'voicetranscoder',
|
||||
job: 'Linux',
|
||||
static openApi = {
|
||||
'/appveyor/job/build/{user}/{repo}/{job}': {
|
||||
get: {
|
||||
summary: 'AppVeyor Job',
|
||||
parameters: pathParams(
|
||||
{ name: 'user', example: 'wpmgprostotema' },
|
||||
{ name: 'repo', example: 'voicetranscoder' },
|
||||
{ name: 'job', example: 'Linux' },
|
||||
),
|
||||
},
|
||||
staticPreview: renderBuildStatusBadge({ status: 'success' }),
|
||||
},
|
||||
{
|
||||
title: 'AppVeyor Job branch',
|
||||
pattern: ':user/:repo/:job/:branch',
|
||||
namedParams: {
|
||||
user: 'wpmgprostotema',
|
||||
repo: 'voicetranscoder',
|
||||
job: 'Windows',
|
||||
branch: 'master',
|
||||
'/appveyor/job/build/{user}/{repo}/{job}/{branch}': {
|
||||
get: {
|
||||
summary: 'AppVeyor Job (with branch)',
|
||||
parameters: pathParams(
|
||||
{ name: 'user', example: 'wpmgprostotema' },
|
||||
{ name: 'repo', example: 'voicetranscoder' },
|
||||
{ name: 'job', example: 'Windows' },
|
||||
{ name: 'branch', example: 'master' },
|
||||
),
|
||||
},
|
||||
staticPreview: renderBuildStatusBadge({ status: 'success' }),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
transform({ data, jobName }) {
|
||||
if (!('build' in data)) {
|
||||
|
@ -1,18 +1,12 @@
|
||||
import {
|
||||
testResultQueryParamSchema,
|
||||
testResultOpenApiQueryParams,
|
||||
renderTestResultBadge,
|
||||
documentation,
|
||||
documentation as description,
|
||||
} from '../test-results.js'
|
||||
import { pathParams } from '../index.js'
|
||||
import AppVeyorBase from './appveyor-base.js'
|
||||
|
||||
const commonPreviewProps = {
|
||||
passed: 477,
|
||||
failed: 2,
|
||||
skipped: 0,
|
||||
total: 479,
|
||||
isCompact: false,
|
||||
}
|
||||
|
||||
export default class AppVeyorTests extends AppVeyorBase {
|
||||
static category = 'test-results'
|
||||
static route = {
|
||||
@ -20,63 +14,35 @@ export default class AppVeyorTests extends AppVeyorBase {
|
||||
queryParamSchema: testResultQueryParamSchema,
|
||||
}
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'AppVeyor tests',
|
||||
pattern: ':user/:repo',
|
||||
namedParams: {
|
||||
user: 'NZSmartie',
|
||||
repo: 'coap-net-iu0to',
|
||||
static openApi = {
|
||||
'/appveyor/tests/{user}/{repo}': {
|
||||
get: {
|
||||
summary: 'AppVeyor tests',
|
||||
description,
|
||||
parameters: [
|
||||
...pathParams(
|
||||
{ name: 'user', example: 'NZSmartie' },
|
||||
{ name: 'repo', example: 'coap-net-iu0to' },
|
||||
),
|
||||
...testResultOpenApiQueryParams,
|
||||
],
|
||||
},
|
||||
staticPreview: this.render(commonPreviewProps),
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'AppVeyor tests (branch)',
|
||||
pattern: ':user/:repo/:branch',
|
||||
namedParams: {
|
||||
user: 'NZSmartie',
|
||||
repo: 'coap-net-iu0to',
|
||||
branch: 'master',
|
||||
'/appveyor/tests/{user}/{repo}/{branch}': {
|
||||
get: {
|
||||
summary: 'AppVeyor tests (with branch)',
|
||||
description,
|
||||
parameters: [
|
||||
...pathParams(
|
||||
{ name: 'user', example: 'NZSmartie' },
|
||||
{ name: 'repo', example: 'coap-net-iu0to' },
|
||||
{ name: 'branch', example: 'master' },
|
||||
),
|
||||
...testResultOpenApiQueryParams,
|
||||
],
|
||||
},
|
||||
staticPreview: this.render(commonPreviewProps),
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'AppVeyor tests (compact)',
|
||||
pattern: ':user/:repo',
|
||||
namedParams: {
|
||||
user: 'NZSmartie',
|
||||
repo: 'coap-net-iu0to',
|
||||
},
|
||||
queryParams: { compact_message: null },
|
||||
staticPreview: this.render({
|
||||
...commonPreviewProps,
|
||||
isCompact: true,
|
||||
}),
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'AppVeyor tests with custom labels',
|
||||
pattern: ':user/:repo',
|
||||
namedParams: {
|
||||
user: 'NZSmartie',
|
||||
repo: 'coap-net-iu0to',
|
||||
},
|
||||
queryParams: {
|
||||
passed_label: 'good',
|
||||
failed_label: 'bad',
|
||||
skipped_label: 'n/a',
|
||||
},
|
||||
staticPreview: this.render({
|
||||
...commonPreviewProps,
|
||||
passedLabel: 'good',
|
||||
failedLabel: 'bad',
|
||||
skippedLabel: 'n/a',
|
||||
}),
|
||||
documentation,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static defaultBadgeData = {
|
||||
label: 'tests',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { MetricNames } from '../../core/base-service/metric-helper.js'
|
||||
import { BaseJsonService } from '../index.js'
|
||||
import { BaseJsonService, queryParams } from '../index.js'
|
||||
import { createRoute } from './dynamic-helpers.js'
|
||||
import jsonPath from './json-path.js'
|
||||
|
||||
@ -14,13 +14,11 @@ export default class DynamicJson extends jsonPath(BaseJsonService) {
|
||||
The Dynamic JSON Badge allows you to extract an arbitrary value from any
|
||||
JSON Document using a JSONPath selector and show it on a badge.
|
||||
</p>`,
|
||||
parameters: [
|
||||
parameters: queryParams(
|
||||
{
|
||||
name: 'url',
|
||||
description: 'The URL to a JSON document',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example:
|
||||
'https://github.com/badges/shields/raw/master/package.json',
|
||||
},
|
||||
@ -28,28 +26,20 @@ export default class DynamicJson extends jsonPath(BaseJsonService) {
|
||||
name: 'query',
|
||||
description:
|
||||
'A <a href="https://jsonpath.com/">JSONPath</a> expression that will be used to query the document',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: '$.name',
|
||||
},
|
||||
{
|
||||
name: 'prefix',
|
||||
description: 'Optional prefix to append to the value',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: '[',
|
||||
},
|
||||
{
|
||||
name: 'suffix',
|
||||
description: 'Optional suffix to append to the value',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: ']',
|
||||
},
|
||||
],
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -2,7 +2,12 @@ import { DOMParser } from '@xmldom/xmldom'
|
||||
import xpath from 'xpath'
|
||||
import { MetricNames } from '../../core/base-service/metric-helper.js'
|
||||
import { renderDynamicBadge, httpErrors } from '../dynamic-common.js'
|
||||
import { BaseService, InvalidResponse, InvalidParameter } from '../index.js'
|
||||
import {
|
||||
BaseService,
|
||||
InvalidResponse,
|
||||
InvalidParameter,
|
||||
queryParams,
|
||||
} from '../index.js'
|
||||
import { createRoute } from './dynamic-helpers.js'
|
||||
|
||||
// This service extends BaseService because it uses a different XML parser
|
||||
@ -23,41 +28,31 @@ export default class DynamicXml extends BaseService {
|
||||
The Dynamic XML Badge allows you to extract an arbitrary value from any
|
||||
XML Document using an XPath selector and show it on a badge.
|
||||
</p>`,
|
||||
parameters: [
|
||||
parameters: queryParams(
|
||||
{
|
||||
name: 'url',
|
||||
description: 'The URL to a XML document',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: 'https://httpbin.org/xml',
|
||||
},
|
||||
{
|
||||
name: 'query',
|
||||
description:
|
||||
'A <a href="http://xpather.com/">XPath</a> expression that will be used to query the document',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: '//slideshow/slide[1]/title',
|
||||
},
|
||||
{
|
||||
name: 'prefix',
|
||||
description: 'Optional prefix to append to the value',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: '[',
|
||||
},
|
||||
{
|
||||
name: 'suffix',
|
||||
description: 'Optional suffix to append to the value',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: ']',
|
||||
},
|
||||
],
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { MetricNames } from '../../core/base-service/metric-helper.js'
|
||||
import { BaseYamlService } from '../index.js'
|
||||
import { BaseYamlService, queryParams } from '../index.js'
|
||||
import { createRoute } from './dynamic-helpers.js'
|
||||
import jsonPath from './json-path.js'
|
||||
|
||||
@ -14,13 +14,11 @@ export default class DynamicYaml extends jsonPath(BaseYamlService) {
|
||||
The Dynamic YAML Badge allows you to extract an arbitrary value from any
|
||||
YAML Document using a JSONPath selector and show it on a badge.
|
||||
</p>`,
|
||||
parameters: [
|
||||
parameters: queryParams(
|
||||
{
|
||||
name: 'url',
|
||||
description: 'The URL to a YAML document',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example:
|
||||
'https://raw.githubusercontent.com/badges/shields/master/.github/dependabot.yml',
|
||||
},
|
||||
@ -28,28 +26,20 @@ export default class DynamicYaml extends jsonPath(BaseYamlService) {
|
||||
name: 'query',
|
||||
description:
|
||||
'A <a href="https://jsonpath.com/">JSONPath</a> expression that will be used to query the document',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: '$.version',
|
||||
},
|
||||
{
|
||||
name: 'prefix',
|
||||
description: 'Optional prefix to append to the value',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: '[',
|
||||
},
|
||||
{
|
||||
name: 'suffix',
|
||||
description: 'Optional suffix to append to the value',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: { type: 'string' },
|
||||
example: ']',
|
||||
},
|
||||
],
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import Joi from 'joi'
|
||||
import { httpErrors } from '../dynamic-common.js'
|
||||
import { optionalUrl } from '../validators.js'
|
||||
import { fetchEndpointData } from '../endpoint-common.js'
|
||||
import { BaseJsonService, InvalidParameter } from '../index.js'
|
||||
import { BaseJsonService, InvalidParameter, queryParams } from '../index.js'
|
||||
|
||||
const blockedDomains = ['github.com', 'shields.io']
|
||||
|
||||
@ -135,16 +135,12 @@ export default class Endpoint extends BaseJsonService {
|
||||
get: {
|
||||
summary: 'Endpoint Badge',
|
||||
description,
|
||||
parameters: [
|
||||
{
|
||||
name: 'url',
|
||||
description: 'The URL to your JSON endpoint',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: { type: 'string' },
|
||||
example: 'https://shields.redsparr0w.com/2473/monday',
|
||||
},
|
||||
],
|
||||
parameters: queryParams({
|
||||
name: 'url',
|
||||
description: 'The URL to your JSON endpoint',
|
||||
required: true,
|
||||
example: 'https://shields.redsparr0w.com/2473/monday',
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Joi from 'joi'
|
||||
import { queryParams } from './index.js'
|
||||
|
||||
const testResultQueryParamSchema = Joi.object({
|
||||
compact_message: Joi.equal(''),
|
||||
@ -7,6 +8,17 @@ const testResultQueryParamSchema = Joi.object({
|
||||
skipped_label: Joi.string(),
|
||||
}).required()
|
||||
|
||||
const testResultOpenApiQueryParams = queryParams(
|
||||
{
|
||||
name: 'compact_message',
|
||||
example: null,
|
||||
schema: { type: 'boolean' },
|
||||
},
|
||||
{ name: 'passed_label', example: 'good' },
|
||||
{ name: 'failed_label', example: 'bad' },
|
||||
{ name: 'skipped_label', example: 'n/a' },
|
||||
)
|
||||
|
||||
function renderTestResultMessage({
|
||||
passed,
|
||||
failed,
|
||||
@ -89,13 +101,13 @@ const documentation = `
|
||||
|
||||
<p>
|
||||
For example, if you want to use a different terminology:
|
||||
<br>
|
||||
<br />
|
||||
<code>?passed_label=good&failed_label=bad&skipped_label=n%2Fa</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Or symbols:
|
||||
<br>
|
||||
<br />
|
||||
<code>?compact_message&passed_label=💃&failed_label=🤦♀️&skipped_label=🤷</code>
|
||||
</p>
|
||||
|
||||
@ -106,6 +118,7 @@ const documentation = `
|
||||
|
||||
export {
|
||||
testResultQueryParamSchema,
|
||||
testResultOpenApiQueryParams,
|
||||
renderTestResultMessage,
|
||||
renderTestResultBadge,
|
||||
documentation,
|
||||
|
Loading…
x
Reference in New Issue
Block a user