const pveapi = require('./source') const yaml = require('js-yaml') const paths = {} const models = {} const responses = {} const tags = [] const capitalizeFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1) function generateOpId(method, path){ let operation = path.split("/").map(capitalizeFirst).join('').replace(/[\-\_]/g, '') operation = operation.replace(/\{[a-z]*\}/g, 'Single') const prefix = (() => { switch (method) { case "post": return "create" case "put": return "update" case "patch": return "update" default: return method } })() return prefix + operation } const mapping = require('./mapping') function filterSchema(p){ const schema = { type: p.type } if(p.items) schema.items = filterSchema(p.items) if(p.properties){ schema.properties = {} Object.keys(p.properties).forEach(name => schema.properties[name] = filterSchema(p.properties[name])) } return schema } function buildResponseSchema(source){ const schema = { type: source.type || 'string', description: source.description || '' } if(schema.type === 'null') schema.type = 'string' if(source.type === 'array' && source.items) schema.items = buildResponseSchema(source.items) if(source.type === 'object' && source.properties){ schema.properties = {} Object.keys(source.properties || {}).forEach(k => { if(k.endsWith('[n]')){ const nk = k.substr(0, k.length-3) for(let i=0; i<30; i++){ schema.properties[nk+i] = buildResponseSchema(source.properties[k]) } }else{ schema.properties[k] = buildResponseSchema(source.properties[k]) } }) } return schema } function parseInfo(path, method, info){ let id = generateOpId(method, path); id = mapping[id] || id const sourceProperties = (info.parameters && info.parameters.properties) ? Object.keys(info.parameters.properties).map(k => ({ name: k, ...info.parameters.properties[k] })) : [] const properties = [] sourceProperties.forEach(p => { if(p.name.endsWith('[n]')){ const nk = p.name.substr(0, p.name.length-3); for(let i=0; i<30; i++){ properties.push({ ...JSON.parse(JSON.stringify(p)), name: nk+i }) } }else{ properties.push(p) } }); const requestName = capitalizeFirst(id) + 'Request' const responseName = capitalizeFirst(id) + 'Response' paths[path][method] = { operationId: id, summary: id, description: info.description || id, tags: [path.substr(1).split('/')[0]], parameters: properties.filter(p => path.includes('{'+p.name+'}')).map(p => ({ name: p.name, in: 'path', required: true, description: p.name, schema: { type: p.type } })), responses: { '200': { $ref: '#/components/responses/'+responseName } } } paths[path][method].tags.forEach(t => { if(!tags.includes(t)) tags.push(t) }) responses[responseName] = { description: responseName, content: { 'application/json': { schema: { type: 'object', properties: { errors: { type: 'array', items: { type: 'string' } }, data: buildResponseSchema(info.returns) } } } } } if(method === 'post' || method === 'put'){ models[requestName] = { title: requestName, type: 'object', properties: {}, required: [] } properties.filter(p => !path.includes('{'+p.name+'}')).forEach(p => { models[requestName].properties[p.name] = filterSchema(p) if(p.optional !== 1){ models[requestName].required.push(p.name) } }) if(models[requestName].required.length < 1) delete models[requestName].required paths[path][method].requestBody = { content: { 'application/json': { schema: { $ref: '#/components/schemas/'+requestName } } } } }else{ properties.filter(p => !path.includes('{'+p.name+'}')).map(p => ({ name: p.name, in: 'query', required: p.optional !== 1, description: p.name, schema: { type: p.type } })) } } function parsePath(source){ if(source.info && Object.keys(source.info).length > 0){ paths[source.path] = {} Object.keys(source.info).forEach(method => parseInfo(source.path, method.toLowerCase(), source.info[method])) } if(source.children) source.children.forEach(parsePath) } pveapi.forEach(parsePath) const spec = { openapi: '3.0.0', info: { title: 'ProxMox VE API', version: '2.0', description: 'ProxMox VE API', contact: { name: 'LUMASERV Support Team', email: 'support@lumaserv.com' } }, servers: [ { description: 'local', url: 'https://cluster.local:8006/api2/json' } ], tags: tags.map(t => ({ name: t })), paths: paths, components: { schemas: models, responses: responses } } const fs = require('fs') fs.writeFileSync('../reference/spec.v2.yaml', yaml.safeDump(spec))