summary refs log tree commit diff
path: root/scripts/openapi.js
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/openapi.js')
-rw-r--r--scripts/openapi.js374
1 files changed, 187 insertions, 187 deletions
diff --git a/scripts/openapi.js b/scripts/openapi.js

index 39396d8d..5afee152 100644 --- a/scripts/openapi.js +++ b/scripts/openapi.js
@@ -32,227 +32,227 @@ const SchemaPath = path.join(__dirname, "..", "assets", "schemas.json"); const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" })); let specification = { - openapi: "3.1.0", - info: { - title: "Spacebar Server", - description: - "Spacebar is a Discord.com server implementation and extension, with the goal of complete feature parity with Discord.com, all while adding some additional goodies, security, privacy, and configuration options.", - 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: {}, + openapi: "3.1.0", + info: { + title: "Spacebar Server", + description: + "Spacebar is a Discord.com server implementation and extension, with the goal of complete feature parity with Discord.com, all while adding some additional goodies, security, privacy, and configuration options.", + 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: {}, }; const schemaRegEx = new RegExp(/^[\w.]+$/); function combineSchemas(schemas) { - let definitions = {}; + let definitions = {}; - for (const name in schemas) { - definitions = { - ...definitions, - ...schemas[name].definitions, - [name]: { - ...schemas[name], - definitions: undefined, - $schema: undefined, - }, - }; - } + for (const name in schemas) { + definitions = { + ...definitions, + ...schemas[name].definitions, + [name]: { + ...schemas[name], + definitions: undefined, + $schema: undefined, + }, + }; + } - for (const key in definitions) { - if (!schemaRegEx.test(key)) { - console.error(` \x1b[5m${bgRedBright("ERROR")}\x1b[25m Invalid schema name: ${key}, context:`, definitions[key]); - continue; - } - specification.components = specification.components || {}; - specification.components.schemas = specification.components.schemas || {}; - specification.components.schemas[key] = definitions[key]; - delete definitions[key].additionalProperties; - delete definitions[key].$schema; - const definition = definitions[key]; + for (const key in definitions) { + if (!schemaRegEx.test(key)) { + console.error(` \x1b[5m${bgRedBright("ERROR")}\x1b[25m Invalid schema name: ${key}, context:`, definitions[key]); + continue; + } + specification.components = specification.components || {}; + specification.components.schemas = specification.components.schemas || {}; + specification.components.schemas[key] = definitions[key]; + delete definitions[key].additionalProperties; + delete definitions[key].$schema; + const definition = definitions[key]; - if (typeof definition.properties === "object") { - for (const property of Object.values(definition.properties)) { - if (Array.isArray(property.type)) { - if (property.type.includes("null")) { - property.type = property.type.find((x) => x !== "null"); - property.nullable = true; - } - } - } - } - } + if (typeof definition.properties === "object") { + for (const property of Object.values(definition.properties)) { + if (Array.isArray(property.type)) { + if (property.type.includes("null")) { + property.type = property.type.find((x) => x !== "null"); + property.nullable = true; + } + } + } + } + } - return definitions; + return definitions; } function getTag(key) { - return key.match(/\/([\w-]+)/)[1]; + return key.match(/\/([\w-]+)/)[1]; } function apiRoutes(missingRoutes) { - const routes = getRouteDescriptions(); + const routes = getRouteDescriptions(); - // populate tags - const tags = Array.from(routes.keys()) - .map((x) => getTag(x)) - .sort((a, b) => a.localeCompare(b)); - specification.tags = [...new Set(tags)].map((x) => ({ name: x })); + // populate tags + const tags = Array.from(routes.keys()) + .map((x) => getTag(x)) + .sort((a, b) => a.localeCompare(b)); + specification.tags = [...new Set(tags)].map((x) => ({ name: x })); - routes.forEach((route, pathAndMethod) => { - const [p, method] = pathAndMethod.split("|"); - const path = p.replace(/:(\w+)/g, "{$1}"); + routes.forEach((route, pathAndMethod) => { + const [p, method] = pathAndMethod.split("|"); + const path = p.replace(/:(\w+)/g, "{$1}"); - let obj = specification.paths[path]?.[method] || {}; - obj["x-right-required"] = route.right; - obj["x-permission-required"] = route.permission; - obj["x-fires-event"] = route.event; + let obj = specification.paths[path]?.[method] || {}; + obj["x-right-required"] = route.right; + obj["x-permission-required"] = route.permission; + obj["x-fires-event"] = route.event; - if ( - !NO_AUTHORIZATION_ROUTES.some((x) => { - if (typeof x === "string") return (method.toUpperCase() + " " + path).startsWith(x); - return x.test(method.toUpperCase() + " " + path); - }) - ) { - obj.security = [{ bearer: [] }]; - } + if ( + !NO_AUTHORIZATION_ROUTES.some((x) => { + if (typeof x === "string") return (method.toUpperCase() + " " + path).startsWith(x); + return x.test(method.toUpperCase() + " " + path); + }) + ) { + obj.security = [{ bearer: [] }]; + } - if (route.description) obj.description = route.description; - if (route.summary) obj.summary = route.summary; - if (route.deprecated) obj.deprecated = route.deprecated; + 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.requestBody}`, - }, - }, - }, - }; - } + if (route.requestBody) { + obj.requestBody = { + required: true, + content: { + "application/json": { + schema: { + $ref: `#/components/schemas/${route.requestBody}`, + }, + }, + }, + }; + } - if (route.responses) { - obj.responses = {}; + if (route.responses) { + obj.responses = {}; - for (const [k, v] of Object.entries(route.responses)) { - if (v.body) - obj.responses[k] = { - description: obj?.responses?.[k]?.description || "", - content: { - "application/json": { - schema: { - $ref: `#/components/schemas/${v.body}`, - }, - }, - }, - }; - else - obj.responses[k] = { - description: obj?.responses?.[k]?.description || "No description available", - }; - } - } else { - obj.responses = { - default: { - description: "No description available", - }, - }; - } + for (const [k, v] of Object.entries(route.responses)) { + if (v.body) + obj.responses[k] = { + description: obj?.responses?.[k]?.description || "", + content: { + "application/json": { + schema: { + $ref: `#/components/schemas/${v.body}`, + }, + }, + }, + }; + else + obj.responses[k] = { + description: obj?.responses?.[k]?.description || "No description available", + }; + } + } else { + obj.responses = { + default: { + description: "No description available", + }, + }; + } - // handles path parameters - if (p.includes(":")) { - obj.parameters = p.match(/:\w+/g)?.map((x) => ({ - name: x.replace(":", ""), - in: "path", - required: true, - schema: { type: "string" }, - description: x.replace(":", ""), - })); - } + // handles path parameters + if (p.includes(":")) { + obj.parameters = p.match(/:\w+/g)?.map((x) => ({ + name: x.replace(":", ""), + in: "path", + required: true, + schema: { type: "string" }, + 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, - })); + 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.parameters = [...(obj.parameters || []), ...query]; + } - obj.tags = [...new Set([...(obj.tags || []), getTag(p)])]; + obj.tags = [...new Set([...(obj.tags || []), getTag(p)])]; - if (missingRoutes.additional.includes(path.replace(/\/$/, ""))) { - obj["x-badges"] = [ - { - label: "Spacebar-only", - color: "red", - }, - ]; - } + if (missingRoutes.additional.includes(path.replace(/\/$/, ""))) { + obj["x-badges"] = [ + { + label: "Spacebar-only", + color: "red", + }, + ]; + } - specification.paths[path] = Object.assign(specification.paths[path] || {}, { - [method]: obj, - }); - }); + specification.paths[path] = Object.assign(specification.paths[path] || {}, { + [method]: obj, + }); + }); } async function main() { - console.log("Generating OpenAPI Specification..."); + console.log("Generating OpenAPI Specification..."); - const routesRes = await fetch("https://github.com/spacebarchat/missing-routes/raw/main/missing.json", { - headers: { - Accept: "application/json", - }, - }); - const missingRoutes = await routesRes.json(); + const routesRes = await fetch("https://github.com/spacebarchat/missing-routes/raw/main/missing.json", { + headers: { + Accept: "application/json", + }, + }); + const missingRoutes = await routesRes.json(); - combineSchemas(schemas); - apiRoutes(missingRoutes); + combineSchemas(schemas); + apiRoutes(missingRoutes); - fs.writeFileSync(openapiPath, JSON.stringify(specification, null, 4).replaceAll("#/definitions", "#/components/schemas").replaceAll("bigint", "number")); - console.log("Wrote OpenAPI specification to", openapiPath); - const elapsedMs = Number(totalSw.elapsed().totalMilliseconds + "." + totalSw.elapsed().microseconds); - console.log( - "Specification contains", - Object.keys(specification.paths).length, - "paths and", - Object.keys(specification.components.schemas).length, - "schemas in", - elapsedMs, - "ms.", - ); + fs.writeFileSync(openapiPath, JSON.stringify(specification, null, 4).replaceAll("#/definitions", "#/components/schemas").replaceAll("bigint", "number")); + console.log("Wrote OpenAPI specification to", openapiPath); + const elapsedMs = Number(totalSw.elapsed().totalMilliseconds + "." + totalSw.elapsed().microseconds); + console.log( + "Specification contains", + Object.keys(specification.paths).length, + "paths and", + Object.keys(specification.components.schemas).length, + "schemas in", + elapsedMs, + "ms.", + ); } main();