mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-01-26 23:12:06 -05:00
208 lines
5.5 KiB
TypeScript
208 lines
5.5 KiB
TypeScript
import * as fs from 'fs'
|
||
import * as yaml from 'js-yaml'
|
||
|
||
const API_PARAMS_CONFIG_FILE = 'api-params.yaml'
|
||
const API_EXAMPLES_CONFIG_FILE = 'api-examples.yaml'
|
||
|
||
interface Response {
|
||
$ref: string
|
||
}
|
||
|
||
interface Parameter {
|
||
type: string
|
||
required: boolean
|
||
format: string
|
||
example: any
|
||
name: string
|
||
description: string
|
||
in: string
|
||
}
|
||
|
||
interface VerbData {
|
||
tags: string[]
|
||
summary: string
|
||
description: string | undefined
|
||
operationId: string
|
||
parameters: Parameter[]
|
||
responses: {[status:string]:Response}
|
||
}
|
||
|
||
interface Item {
|
||
$ref: string
|
||
}
|
||
|
||
interface AdditionalProperties {
|
||
$ref: string
|
||
}
|
||
|
||
interface Property {
|
||
description: string
|
||
type: string
|
||
items: Item
|
||
example: any
|
||
additionalProperties: AdditionalProperties
|
||
}
|
||
|
||
interface Definition {
|
||
type: string
|
||
title: string
|
||
required: string[]
|
||
properties: {[property:string]:Property}
|
||
example: string
|
||
examples: string[]
|
||
}
|
||
|
||
interface OpenApi {
|
||
paths: {[path:string]:{[verb:string]:VerbData}}
|
||
definitions: {[type:string]:Definition}
|
||
}
|
||
|
||
interface Param {
|
||
description: string
|
||
type: string
|
||
}
|
||
|
||
interface ParamsConfig {
|
||
params: {[param:string]:Param}
|
||
}
|
||
|
||
interface ExamplesConfigExamples {
|
||
refs: {[id:string]:any}
|
||
inject: {[id:string]:{[property:string]:any}}
|
||
}
|
||
|
||
interface ExamplesConfig {
|
||
examples: ExamplesConfigExamples
|
||
}
|
||
|
||
let inputData = ''
|
||
|
||
process.stdin.on('data', (chunk) => {
|
||
inputData += chunk.toString()
|
||
})
|
||
|
||
const usedExamples = new Set<string>()
|
||
const unresolvedExampleReferences = new Set<string>()
|
||
|
||
function processDescription(description: string|null|undefined): string|null|undefined {
|
||
if (description !== null && description !== undefined) {
|
||
return description.split("\n").map(line => line.replace(/^(\s*)![\*\-]?/, '$1*')).join("\n")
|
||
} else {
|
||
return description
|
||
}
|
||
}
|
||
|
||
process.stdin.on('end', () => {
|
||
try {
|
||
const paramsConfig = yaml.load(fs.readFileSync(API_PARAMS_CONFIG_FILE, 'utf8')) as ParamsConfig
|
||
const params = paramsConfig.params || {}
|
||
|
||
const examplesConfig = yaml.load(fs.readFileSync(API_EXAMPLES_CONFIG_FILE, 'utf8')) as ExamplesConfig
|
||
const exampleRefs = examplesConfig.examples.refs
|
||
const exampleInjects = examplesConfig.examples.inject
|
||
|
||
const data = yaml.load(inputData) as OpenApi
|
||
|
||
for (const path in data.paths) {
|
||
const pathData = data.paths[path]
|
||
|
||
for (const param in params) {
|
||
if (path.includes(`{${param}}`)) {
|
||
const paramsData = params[param] as Param
|
||
for (const verb in pathData) {
|
||
const verbData = pathData[verb]
|
||
verbData.parameters ??= []
|
||
verbData.parameters.push({
|
||
name: param,
|
||
required: true,
|
||
type: paramsData.type !== undefined ? paramsData.type : 'string',
|
||
in: 'path',
|
||
description: paramsData.description,
|
||
} as Parameter)
|
||
}
|
||
}
|
||
}
|
||
|
||
// do some magic with the formatting of endpoint descriptions:
|
||
for (const verb in pathData) {
|
||
const verbData = pathData[verb]
|
||
verbData.description = processDescription(verbData.description)
|
||
}
|
||
}
|
||
|
||
for (const def in data.definitions) {
|
||
const defData = data.definitions[def]
|
||
|
||
if (def.startsWith('TypeOf')) {
|
||
const value = def.substring('TypeOf'.length)
|
||
defData.title = value
|
||
defData.example = value
|
||
}
|
||
|
||
const injects = exampleInjects[def] || {}
|
||
if (defData.properties !== null && defData.properties !== undefined) {
|
||
for (const prop in defData.properties as any) {
|
||
const propData = defData.properties[prop]
|
||
|
||
const inject = injects[prop]
|
||
if (inject !== null && inject !== undefined) {
|
||
propData.example = inject
|
||
}
|
||
|
||
if (propData.example !== null && propData.example !== undefined) {
|
||
if (typeof propData.example === 'string' && (propData.example as string).startsWith('$')) {
|
||
const exampleId = propData.example.substring(1)
|
||
const value = exampleRefs[exampleId]
|
||
if (value === null || value === undefined) {
|
||
unresolvedExampleReferences.add(exampleId)
|
||
} else {
|
||
usedExamples.add(exampleId)
|
||
propData.example = value
|
||
}
|
||
}
|
||
}
|
||
|
||
propData.description = processDescription(propData.description)
|
||
}
|
||
} else {
|
||
if (typeof(injects) === 'string') {
|
||
defData.example = injects
|
||
} else if (Array.isArray(injects)) {
|
||
defData.examples = injects
|
||
}
|
||
}
|
||
}
|
||
|
||
process.stdout.write(yaml.dump(data))
|
||
process.stdout.write("\n")
|
||
|
||
if (unresolvedExampleReferences.size > 0) {
|
||
console.error(`\x1b[33;1m⚠️ WARNING: unresolved example references not contained in ${API_PARAMS_CONFIG_FILE}:\x1b[0m`)
|
||
unresolvedExampleReferences.forEach(item => {
|
||
console.error(` - ${item}`)
|
||
})
|
||
console.error()
|
||
}
|
||
|
||
const unusedExampleReferences = new Set<string>(Object.keys(exampleRefs))
|
||
usedExamples.forEach(item => {
|
||
unusedExampleReferences.delete(item)
|
||
})
|
||
|
||
if (unusedExampleReferences.size > 0) {
|
||
console.error(`\x1b[33;1m⚠️ WARNING: unused examples in ${API_EXAMPLES_CONFIG_FILE}:\x1b[0m`)
|
||
unusedExampleReferences.forEach(item => {
|
||
console.error(` - ${item}`)
|
||
})
|
||
console.error()
|
||
}
|
||
|
||
} catch (error) {
|
||
if (error instanceof Error) {
|
||
console.error(`Error occured while post-processing OpenAPI: ${error.message}`)
|
||
} else {
|
||
console.error("Unknown error occurred")
|
||
}
|
||
}
|
||
})
|