Add /openapi.json endpoint

pull/2163/head
Jérémy Rialland 2022-05-17 14:18:32 +02:00 committed by Johan Girod
parent b10f8b787a
commit 9a0b2564ed
5 changed files with 75 additions and 7 deletions

View File

@ -8,14 +8,14 @@
"type": "module",
"scripts": {
"validate": "yarn swagger-cli validate ./source/openapi.yaml",
"start": "yarn build:watch & wait-on ./dist && NODE_OPTIONS=--experimental-json-modules nodemon ./source/index.ts",
"start": "yarn clean && yarn build:watch & wait-on ./dist && NODE_OPTIONS=--experimental-json-modules nodemon -d 1s ./source/index.ts",
"build": "yarn build:openapi && yarn build:ts",
"build:watch": "yarn build:openapi:watch & yarn wait:openapi && yarn build:ts:watch --preserveWatchOutput",
"build:ts": "NODE_OPTIONS=--experimental-json-modules tsc",
"build:ts:watch": "yarn build:ts -w",
"wait:openapi": "wait-on ./source/openapi.json",
"build:openapi": "yarn run swagger-cli bundle ./source/openapi.yaml > ./source/openapi.json",
"build:openapi:watch": "nodemon -w ./source/openapi.yaml -x \"yarn build:openapi\"",
"build:openapi:watch": "nodemon -d 500ms -w ./source/openapi.yaml -x \"yarn build:openapi\"",
"clean": "rm -rf dist ./source/openapi.json"
},
"repository": {

View File

@ -4,8 +4,10 @@ import Koa from 'koa'
import rules from 'modele-social'
import Engine from 'publicodes'
import { koaMiddleware as publicodesAPI } from 'publicodes-api'
// @ts-ignore
import openapi from './openapi.json'
import { docRoutes } from './route/doc.js'
import { openapiRoutes } from './route/openapi.js'
type State = Koa.DefaultState
@ -16,11 +18,9 @@ const router = new Router<State, Context>()
app.use(cors())
const apiRoutes = publicodesAPI(() => new Engine(rules), {
customOpenapi: openapi,
})
const apiRoutes = publicodesAPI(() => new Engine(rules))
router.use('/v1', apiRoutes, docRoutes())
router.use('/v1', apiRoutes, docRoutes(), await openapiRoutes(openapi))
app.use(router.routes())
app.use(router.allowedMethods())

View File

@ -0,0 +1,27 @@
import Router from '@koa/router'
import { Context } from 'koa'
import { openapi } from 'publicodes-api'
import { mergeDeep } from '../utils.js'
/**
* /openapi.json route, merge customOpenapi with publicodes-api openapi json
* @param customOpenapi
* @returns
*/
export const openapiRoutes = async (
customOpenapi?: Record<string, unknown>
) => {
const router = new Router()
const publicodesOpenapi = await openapi()
const mergedOpenapi = customOpenapi
? mergeDeep(publicodesOpenapi, customOpenapi)
: publicodesOpenapi
router.get('/openapi.json', (ctx: Context) => {
ctx.type = 'application/json'
ctx.body = mergedOpenapi
})
return router.routes()
}

41
api/source/utils.ts Normal file
View File

@ -0,0 +1,41 @@
/**
* Simple object check.
* @param item
* @returns {boolean}
*/
export function isObject(item: unknown): item is Record<string, unknown> {
return !!item && typeof item === 'object' && !Array.isArray(item)
}
/**
* Deep merge two objects.
* @param target
* @param ...sources
*/
export function mergeDeep(
target: Record<string, unknown>,
...sources: Record<string, unknown>[]
): Record<string, unknown> {
if (!sources.length) {
return target
}
const source = sources.shift()
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) {
Object.assign(target, { [key]: {} })
}
mergeDeep(
target[key] as Record<string, unknown>,
source[key] as Record<string, unknown>
)
} else {
Object.assign(target, { [key]: source[key] })
}
}
}
return mergeDeep(target, ...sources)
}

View File

@ -24,7 +24,7 @@
"noFallthroughCasesInSwitch": true,
/* Module Resolution Options */
"moduleResolution": "Node",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"resolveJsonModule": true,