Source: core/base-service/openapi.js

  1. /**
  2. * Functions for publishing the shields.io URL schema as an OpenAPI Document
  3. *
  4. * @module
  5. */
  6. const baseUrl = process.env.BASE_URL
  7. const globalParamRefs = [
  8. { $ref: '#/components/parameters/style' },
  9. { $ref: '#/components/parameters/logo' },
  10. { $ref: '#/components/parameters/logoColor' },
  11. { $ref: '#/components/parameters/logoSize' },
  12. { $ref: '#/components/parameters/label' },
  13. { $ref: '#/components/parameters/labelColor' },
  14. { $ref: '#/components/parameters/color' },
  15. { $ref: '#/components/parameters/cacheSeconds' },
  16. { $ref: '#/components/parameters/link' },
  17. ]
  18. function getCodeSamples(altText) {
  19. return [
  20. {
  21. lang: 'URL',
  22. label: 'URL',
  23. source: '$url',
  24. },
  25. {
  26. lang: 'Markdown',
  27. label: 'Markdown',
  28. source: `![${altText}]($url)`,
  29. },
  30. {
  31. lang: 'reStructuredText',
  32. label: 'rSt',
  33. source: `.. image:: $url\n :alt: ${altText}`,
  34. },
  35. {
  36. lang: 'AsciiDoc',
  37. label: 'AsciiDoc',
  38. source: `image:$url[${altText}]`,
  39. },
  40. {
  41. lang: 'HTML',
  42. label: 'HTML',
  43. source: `<img alt="${altText}" src="$url">`,
  44. },
  45. ]
  46. }
  47. function getEnum(pattern, paramName) {
  48. const re = new RegExp(`${paramName}\\(([A-Za-z0-9_\\-|]+)\\)`)
  49. const match = pattern.match(re)
  50. if (match === null) {
  51. return undefined
  52. }
  53. if (!match[1].includes('|')) {
  54. return undefined
  55. }
  56. return match[1].split('|')
  57. }
  58. function addGlobalProperties(endpoints) {
  59. const paths = {}
  60. for (const key of Object.keys(endpoints)) {
  61. paths[key] = endpoints[key]
  62. paths[key].get.parameters = [
  63. ...paths[key].get.parameters,
  64. ...globalParamRefs,
  65. ]
  66. paths[key].get['x-code-samples'] = getCodeSamples(paths[key].get.summary)
  67. }
  68. return paths
  69. }
  70. function sortPaths(obj) {
  71. const entries = Object.entries(obj)
  72. entries.sort((a, b) => a[1].get.summary.localeCompare(b[1].get.summary))
  73. return Object.fromEntries(entries)
  74. }
  75. function services2openapi(services, sort) {
  76. const paths = {}
  77. for (const service of services) {
  78. for (const [key, value] of Object.entries(
  79. addGlobalProperties(service.openApi),
  80. )) {
  81. if (key in paths) {
  82. throw new Error(`Conflicting route: ${key}`)
  83. }
  84. paths[key] = value
  85. }
  86. }
  87. return sort ? sortPaths(paths) : paths
  88. }
  89. function category2openapi({ category, services, sort = false }) {
  90. const spec = {
  91. openapi: '3.0.0',
  92. info: {
  93. version: '1.0.0',
  94. title: category.name,
  95. license: {
  96. name: 'CC0',
  97. },
  98. },
  99. servers: baseUrl ? [{ url: baseUrl }] : undefined,
  100. components: {
  101. parameters: {
  102. style: {
  103. name: 'style',
  104. in: 'query',
  105. required: false,
  106. description: `If not specified, the default style for this badge is "${
  107. category.name.toLowerCase() === 'social' ? 'social' : 'flat'
  108. }".`,
  109. schema: {
  110. type: 'string',
  111. enum: ['flat', 'flat-square', 'plastic', 'for-the-badge', 'social'],
  112. },
  113. example: 'flat',
  114. },
  115. logo: {
  116. name: 'logo',
  117. in: 'query',
  118. required: false,
  119. description:
  120. 'Icon slug from simple-icons. You can click the icon title on <a href="https://simpleicons.org/" rel="noopener noreferrer" target="_blank">simple-icons</a> to copy the slug or they can be found in the <a href="https://github.com/simple-icons/simple-icons/blob/master/slugs.md">slugs.md file</a> in the simple-icons repository. <a href="/docs/logos">Further info</a>.',
  121. schema: {
  122. type: 'string',
  123. },
  124. example: 'appveyor',
  125. },
  126. logoColor: {
  127. name: 'logoColor',
  128. in: 'query',
  129. required: false,
  130. description:
  131. 'The color of the logo (hex, rgb, rgba, hsl, hsla and css named colors supported). Supported for simple-icons logos but not for custom logos.',
  132. schema: {
  133. type: 'string',
  134. },
  135. example: 'violet',
  136. },
  137. logoSize: {
  138. name: 'logoSize',
  139. in: 'query',
  140. required: false,
  141. description:
  142. 'Make icons adaptively resize by setting `auto`. Useful for some wider logos like `amd` and `amg`. Supported for simple-icons logos but not for custom logos.',
  143. schema: {
  144. type: 'string',
  145. },
  146. example: 'auto',
  147. },
  148. label: {
  149. name: 'label',
  150. in: 'query',
  151. required: false,
  152. description:
  153. 'Override the default left-hand-side text (<a href="https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding">URL-Encoding</a> needed for spaces or special characters!)',
  154. schema: {
  155. type: 'string',
  156. },
  157. example: 'healthiness',
  158. },
  159. labelColor: {
  160. name: 'labelColor',
  161. in: 'query',
  162. required: false,
  163. description:
  164. 'Background color of the left part (hex, rgb, rgba, hsl, hsla and css named colors supported).',
  165. schema: {
  166. type: 'string',
  167. },
  168. example: 'abcdef',
  169. },
  170. color: {
  171. name: 'color',
  172. in: 'query',
  173. required: false,
  174. description:
  175. 'Background color of the right part (hex, rgb, rgba, hsl, hsla and css named colors supported).',
  176. schema: {
  177. type: 'string',
  178. },
  179. example: 'fedcba',
  180. },
  181. cacheSeconds: {
  182. name: 'cacheSeconds',
  183. in: 'query',
  184. required: false,
  185. description:
  186. 'HTTP cache lifetime (rules are applied to infer a default value on a per-badge basis, any values specified below the default will be ignored).',
  187. schema: {
  188. type: 'string',
  189. },
  190. example: '3600',
  191. },
  192. link: {
  193. name: 'link',
  194. in: 'query',
  195. required: false,
  196. description:
  197. 'Specify what clicking on the left/right of a badge should do. Note that this only works when integrating your badge in an `<object>` HTML tag, but not an `<img>` tag or a markup language.',
  198. style: 'form',
  199. explode: true,
  200. schema: {
  201. type: 'array',
  202. maxItems: 2,
  203. items: {
  204. type: 'string',
  205. },
  206. },
  207. },
  208. },
  209. },
  210. paths: services2openapi(services, sort),
  211. }
  212. return spec
  213. }
  214. /**
  215. * Helper function for assembling an OpenAPI path parameter object
  216. *
  217. * @param {module:core/base-service/openapi~PathParamInput} param Input param
  218. * @returns {module:core/base-service/openapi~OpenApiParam} OpenAPI Parameter Object
  219. * @see https://swagger.io/specification/#parameter-object
  220. */
  221. function pathParam({
  222. name,
  223. example,
  224. schema = { type: 'string' },
  225. description,
  226. }) {
  227. return { name, in: 'path', required: true, schema, example, description }
  228. }
  229. /**
  230. * Helper function for assembling an array of OpenAPI path parameter objects
  231. * The code
  232. * ```
  233. * const params = pathParams(
  234. * { name: 'name1', example: 'example1' },
  235. * { name: 'name2', example: 'example2' },
  236. * )
  237. * ```
  238. * is equivalent to
  239. * ```
  240. * const params = [
  241. * pathParam({ name: 'name1', example: 'example1' }),
  242. * pathParam({ name: 'name2', example: 'example2' }),
  243. * ]
  244. * ```
  245. *
  246. * @param {...module:core/base-service/openapi~PathParamInput} params Input params
  247. * @returns {Array.<module:core/base-service/openapi~OpenApiParam>} Array of OpenAPI Parameter Objects
  248. * @see {@link module:core/base-service/openapi~pathParam}
  249. */
  250. function pathParams(...params) {
  251. return params.map(param => pathParam(param))
  252. }
  253. /**
  254. * Helper function for assembling an OpenAPI query parameter object
  255. *
  256. * @param {module:core/base-service/openapi~QueryParamInput} param Input param
  257. * @returns {module:core/base-service/openapi~OpenApiParam} OpenAPI Parameter Object
  258. * @see https://swagger.io/specification/#parameter-object
  259. */
  260. function queryParam({
  261. name,
  262. example,
  263. schema = { type: 'string' },
  264. required = false,
  265. description,
  266. }) {
  267. const param = { name, in: 'query', required, schema, example, description }
  268. if (example === null && schema.type === 'boolean') {
  269. param.allowEmptyValue = true
  270. }
  271. return param
  272. }
  273. /**
  274. * Helper function for assembling an array of OpenAPI query parameter objects
  275. * The code
  276. * ```
  277. * const params = queryParams(
  278. * { name: 'name1', example: 'example1' },
  279. * { name: 'name2', example: 'example2' },
  280. * )
  281. * ```
  282. * is equivalent to
  283. * ```
  284. * const params = [
  285. * queryParam({ name: 'name1', example: 'example1' }),
  286. * queryParam({ name: 'name2', example: 'example2' }),
  287. * ]
  288. * ```
  289. *
  290. * @param {...module:core/base-service/openapi~QueryParamInput} params Input params
  291. * @returns {Array.<module:core/base-service/openapi~OpenApiParam>} Array of OpenAPI Parameter Objects
  292. * @see {@link module:core/base-service/openapi~queryParam}
  293. */
  294. function queryParams(...params) {
  295. return params.map(param => queryParam(param))
  296. }
  297. /**
  298. * @typedef {object} PathParamInput
  299. * @property {string} name The name of the parameter. Parameter names are case sensitive
  300. * @property {string} example Example of a valid value for this parameter
  301. * @property {object} [schema={ type: 'string' }] Parameter schema.
  302. * An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
  303. * specifying the parameter type.
  304. * Normally this should be omitted as all path parameters are strings.
  305. * Use this when we also want to pass an enum of valid parameters
  306. * to be presented as a drop-down in the frontend. e.g:
  307. * `{'type': 'string', 'enum': ['github', 'bitbucket'}` (Optional)
  308. * @property {string} description A brief description of the parameter (Optional)
  309. */
  310. /**
  311. * @typedef {object} QueryParamInput
  312. * @property {string} name The name of the parameter. Parameter names are case sensitive
  313. * @property {string|null} example Example of a valid value for this parameter
  314. * @property {object} [schema={ type: 'string' }] Parameter schema.
  315. * An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
  316. * specifying the parameter type. This can normally be omitted.
  317. * Query params are usually strings. (Optional)
  318. * @property {boolean} [required=false] Determines whether this parameter is mandatory (Optional)
  319. * @property {string} description A brief description of the parameter (Optional)
  320. */
  321. /**
  322. * OpenAPI Parameter Object
  323. *
  324. * @typedef {object} OpenApiParam
  325. * @property {string} name The name of the parameter
  326. * @property {string|null} example Example of a valid value for this parameter
  327. * @property {('path'|'query')} in The location of the parameter
  328. * @property {object} schema Parameter schema.
  329. * An [OpenAPI Schema object](https://swagger.io/specification/#schema-object)
  330. * specifying the parameter type.
  331. * @property {boolean} required Determines whether this parameter is mandatory
  332. * @property {string} description A brief description of the parameter
  333. * @property {boolean} allowEmptyValue If true, allows the ability to pass an empty value to this parameter
  334. */
  335. export {
  336. category2openapi,
  337. getEnum,
  338. pathParam,
  339. pathParams,
  340. queryParam,
  341. queryParams,
  342. }