diff options
Diffstat (limited to 'scripts/openapi.js')
-rw-r--r-- | scripts/openapi.js | 186 |
1 files changed, 109 insertions, 77 deletions
diff --git a/scripts/openapi.js b/scripts/openapi.js index 49d5dfde..8258a76c 100644 --- a/scripts/openapi.js +++ b/scripts/openapi.js @@ -27,34 +27,46 @@ require("missing-native-js-functions"); const openapiPath = path.join(__dirname, "..", "assets", "openapi.json"); const SchemaPath = path.join(__dirname, "..", "assets", "schemas.json"); -let schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" })); - -for (var schema in schemas) { - const part = schemas[schema]; - for (var key in part.properties) { - if (part.properties[key].anyOf) { - const nullIndex = part.properties[key].anyOf.findIndex( - (x) => x.type == "null", - ); - if (nullIndex != -1) { - part.properties[key].nullable = true; - part.properties[key].anyOf.splice(nullIndex, 1); - - if (part.properties[key].anyOf.length == 1) { - Object.assign( - part.properties[key], - part.properties[key].anyOf[0], - ); - delete part.properties[key].anyOf; - } - } - } - } -} - -const specification = JSON.parse( - fs.readFileSync(openapiPath, { encoding: "utf8" }), -); +const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" })); +// const specification = JSON.parse( +// fs.readFileSync(openapiPath, { encoding: "utf8" }), +// ); +let specification = { + openapi: "3.1.0", + info: { + title: "Spacebar Server", + description: + "Spacebar is a free open source selfhostable discord compatible chat, voice and video platform", + license: { + name: "AGPLV3", + url: "https://www.gnu.org/licenses/agpl-3.0.en.html", + }, + version: "1.0.0", + }, + externalDocs: { + description: "Spacebar Docs", + url: "https://docs.spacebar.chat", + }, + servers: [ + { + url: "https://old.server.spacebar.chat/api/", + description: "Official Spacebar Instance", + }, + ], + components: { + securitySchemes: { + bearer: { + type: "http", + scheme: "bearer", + description: "Bearer/Bot prefixes are not required.", + bearerFormat: "JWT", + in: "header", + }, + }, + }, + tags: [], + paths: {}, +}; function combineSchemas(schemas) { var definitions = {}; @@ -72,6 +84,11 @@ function combineSchemas(schemas) { } for (const key in definitions) { + const reg = new RegExp(/^[a-zA-Z0-9.\-_]+$/, "gm"); + if (!reg.test(key)) { + console.error(`Invalid schema name: ${key} (${reg.test(key)})`); + continue; + } specification.components = specification.components || {}; specification.components.schemas = specification.components.schemas || {}; @@ -102,30 +119,20 @@ function getTag(key) { function apiRoutes() { const routes = getRouteDescriptions(); - const tags = Array.from(routes.keys()).map((x) => getTag(x)); - specification.tags = specification.tags || []; - specification.tags = [...specification.tags.map((x) => x.name), ...tags] - .unique() - .map((x) => ({ name: x })); - - specification.components = specification.components || {}; - specification.components.securitySchemes = { - bearer: { - type: "http", - scheme: "bearer", - description: "Bearer/Bot prefixes are not required.", - }, - }; + // populate tags + const tags = Array.from(routes.keys()) + .map((x) => getTag(x)) + .sort((a, b) => a.localeCompare(b)); + specification.tags = tags.unique().map((x) => ({ name: x })); routes.forEach((route, pathAndMethod) => { const [p, method] = pathAndMethod.split("|"); const path = p.replace(/:(\w+)/g, "{$1}"); - specification.paths = specification.paths || {}; let obj = specification.paths[path]?.[method] || {}; obj["x-right-required"] = route.right; obj["x-permission-required"] = route.permission; - obj["x-fires-event"] = route.test?.event; + obj["x-fires-event"] = route.event; if ( !NO_AUTHORIZATION_ROUTES.some((x) => { @@ -136,48 +143,56 @@ function apiRoutes() { obj.security = [{ bearer: [] }]; } - if (route.body) { + if (route.description) obj.description = route.description; + if (route.summary) obj.summary = route.summary; + if (route.deprecated) obj.deprecated = route.deprecated; + + if (route.requestBody) { obj.requestBody = { required: true, content: { "application/json": { - schema: { $ref: `#/components/schemas/${route.body}` }, + schema: { + $ref: `#/components/schemas/${route.requestBody}`, + }, }, }, }.merge(obj.requestBody); } - if (route.test?.response) { - const status = route.test.response.status || 200; - let schema = { - allOf: [ - { - $ref: `#/components/schemas/${route.test.response.body}`, - }, - { - example: route.test.body, + if (route.responses) { + for (const [k, v] of Object.entries(route.responses)) { + let schema = { + $ref: `#/components/schemas/${v.body}`, + }; + + obj.responses = { + [k]: { + ...(v.body + ? { + description: + obj?.responses?.[k]?.description || "", + content: { + "application/json": { + schema: schema, + }, + }, + } + : { + description: "No description available", + }), }, - ], - }; - if (!route.test.body) schema = schema.allOf[0]; - + }.merge(obj.responses); + } + } else { obj.responses = { - [status]: { - ...(route.test.response.body - ? { - description: - obj?.responses?.[status]?.description || "", - content: { - "application/json": { - schema: schema, - }, - }, - } - : {}), + default: { + description: "No description available", }, - }.merge(obj.responses); - delete obj.responses.default; + }; } + + // handles path parameters if (p.includes(":")) { obj.parameters = p.match(/:\w+/g)?.map((x) => ({ name: x.replace(":", ""), @@ -187,16 +202,33 @@ function apiRoutes() { description: x.replace(":", ""), })); } + + if (route.query) { + // map to array + const query = Object.entries(route.query).map(([k, v]) => ({ + name: k, + in: "query", + required: v.required, + schema: { type: v.type }, + description: v.description, + })); + + obj.parameters = [...(obj.parameters || []), ...query]; + } + obj.tags = [...(obj.tags || []), getTag(p)].unique(); - specification.paths[path] = { - ...specification.paths[path], - [method]: obj, - }; + specification.paths[path] = Object.assign( + specification.paths[path] || {}, + { + [method]: obj, + }, + ); }); } function main() { + console.log("Generating OpenAPI Specification..."); combineSchemas(schemas); apiRoutes(); |