diff options
Diffstat (limited to 'api/scripts')
-rw-r--r-- | api/scripts/generate_openapi.js | 137 | ||||
-rw-r--r-- | api/scripts/generate_openapi_schema.ts | 99 | ||||
-rw-r--r-- | api/scripts/generate_schema.js (renamed from api/scripts/generate_body_schema.ts) | 28 | ||||
-rw-r--r-- | api/scripts/globalSetup.js | 15 | ||||
-rw-r--r-- | api/scripts/tsconfig-paths-bootstrap.js | 10 |
5 files changed, 156 insertions, 133 deletions
diff --git a/api/scripts/generate_openapi.js b/api/scripts/generate_openapi.js new file mode 100644 index 00000000..c9de9fa6 --- /dev/null +++ b/api/scripts/generate_openapi.js @@ -0,0 +1,137 @@ +// https://mermade.github.io/openapi-gui/# +// https://editor.swagger.io/ +const getRouteDescriptions = require("../jest/getRouteDescriptions"); +const path = require("path"); +const fs = require("fs"); +require("missing-native-js-functions"); + +const openapiPath = path.join(__dirname, "..", "assets", "openapi.json"); +const SchemaPath = path.join(__dirname, "..", "assets", "schemas.json"); +const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" })); +const specification = JSON.parse(fs.readFileSync(openapiPath, { encoding: "utf8" })); + +function combineSchemas(schemas) { + var definitions = {}; + + for (const name in schemas) { + definitions = { + ...definitions, + ...schemas[name].definitions, + [name]: { ...schemas[name], definitions: undefined, $schema: undefined } + }; + } + + for (const key in definitions) { + 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; + } + } + } + } + } + + return definitions; +} + +function getTag(key) { + return key.match(/\/([\w-]+)/)[1]; +} + +function apiRoutes() { + const routes = getRouteDescriptions(); + + const tags = Array.from(routes.keys()).map((x) => getTag(x)); + specification.tags = [...specification.tags.map((x) => x.name), ...tags].unique().map((x) => ({ name: x })); + + routes.forEach((route, pathAndMethod) => { + const [p, method] = pathAndMethod.split("|"); + const path = p.replace(/:(\w+)/g, "{$1}"); + + let obj = specification.paths[path]?.[method] || {}; + if (!obj.description) { + const permission = route.permission ? `##### Requires the \`\`${route.permission}\`\` permission\n` : ""; + const event = route.test?.event ? `##### Fires a \`\`${route.test?.event}\`\` event\n` : ""; + obj.description = permission + event; + } + if (route.body) { + obj.requestBody = { + required: true, + content: { + "application/json": { + schema: { $ref: `#/components/schemas/${route.body}` } + } + } + }.merge(obj.requestBody); + } + if (!obj.responses) { + obj.responses = { + default: { + description: "not documented" + } + }; + } + 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.test.body) schema = schema.allOf[0]; + + obj.responses = { + [status]: { + ...(route.test.response.body + ? { + description: obj.responses[status].description || "", + content: { + "application/json": { + schema: schema + } + } + } + : {}) + } + }.merge(obj.responses); + delete obj.responses.default; + } + if (p.includes(":")) { + obj.parameters = p.match(/:\w+/g)?.map((x) => ({ + name: x.replace(":", ""), + in: "path", + required: true, + schema: { type: "string" }, + description: x.replace(":", "") + })); + } + obj.tags = [...(obj.tags || []), getTag(p)].unique(); + + specification.paths[path] = { ...specification.paths[path], [method]: obj }; + }); +} + +function main() { + combineSchemas(schemas); + apiRoutes(); + + fs.writeFileSync( + openapiPath, + JSON.stringify(specification, null, 4).replaceAll("#/definitions", "#/components/schemas").replaceAll("bigint", "number") + ); +} + +main(); diff --git a/api/scripts/generate_openapi_schema.ts b/api/scripts/generate_openapi_schema.ts deleted file mode 100644 index 329aeaf4..00000000 --- a/api/scripts/generate_openapi_schema.ts +++ /dev/null @@ -1,99 +0,0 @@ -// https://mermade.github.io/openapi-gui/# -// https://editor.swagger.io/ -import path from "path"; -import fs from "fs"; -import * as TJS from "typescript-json-schema"; -import "missing-native-js-functions"; - -const settings: TJS.PartialArgs = { - required: true, - ignoreErrors: true, - excludePrivate: true, - defaultNumberType: "integer", - noExtraProps: true, - defaultProps: false -}; -const compilerOptions: TJS.CompilerOptions = { - strictNullChecks: false -}; -const openapiPath = path.join(__dirname, "..", "assets", "openapi.json"); -var specification = JSON.parse(fs.readFileSync(openapiPath, { encoding: "utf8" })); - -async function utilSchemas() { - const program = TJS.getProgramFromFiles([path.join(__dirname, "..", "..", "util", "src", "index.ts")], compilerOptions); - const generator = TJS.buildGenerator(program, settings); - - const schemas = ["UserPublic", "UserPrivate", "PublicConnectedAccount"]; - - // @ts-ignore - combineSchemas({ schemas, generator, program }); -} - -function combineSchemas(opts: { program: TJS.Program; generator: TJS.JsonSchemaGenerator; schemas: string[] }) { - var definitions: any = {}; - - for (const name of opts.schemas) { - const part = TJS.generateSchema(opts.program, name, settings, [], opts.generator as TJS.JsonSchemaGenerator); - if (!part) continue; - - definitions = { ...definitions, [name]: { ...part, definitions: undefined, $schema: undefined } }; - } - - for (const key in definitions) { - specification.components.schemas[key] = definitions[key]; - delete definitions[key].additionalProperties; - delete definitions[key].$schema; - } - - return definitions; -} - -function apiSchemas() { - const program = TJS.getProgramFromFiles([path.join(__dirname, "..", "src", "schema", "index.ts")], compilerOptions); - const generator = TJS.buildGenerator(program, settings); - - const schemas = [ - "BanCreateSchema", - "DmChannelCreateSchema", - "ChannelModifySchema", - "ChannelGuildPositionUpdateSchema", - "ChannelGuildPositionUpdateSchema", - "EmojiCreateSchema", - "GuildCreateSchema", - "GuildUpdateSchema", - "GuildTemplateCreateSchema", - "GuildUpdateWelcomeScreenSchema", - "InviteCreateSchema", - "MemberCreateSchema", - "MemberNickChangeSchema", - "MemberChangeSchema", - "MessageCreateSchema", - "RoleModifySchema", - "TemplateCreateSchema", - "TemplateModifySchema", - "UserModifySchema", - "UserSettingsSchema", - "WidgetModifySchema", - "" - ]; - - // @ts-ignore - combineSchemas({ schemas, generator, program }); -} - -function addDefaultResponses() { - Object.values(specification.paths).forEach((path: any) => Object.values(path).forEach((request: any) => {})); -} - -function main() { - addDefaultResponses(); - utilSchemas(); - apiSchemas(); - - fs.writeFileSync( - openapiPath, - JSON.stringify(specification, null, 4).replaceAll("#/definitions", "#/components/schemas").replaceAll("bigint", "number") - ); -} - -main(); diff --git a/api/scripts/generate_body_schema.ts b/api/scripts/generate_schema.js index 316e5a69..22d0b02e 100644 --- a/api/scripts/generate_body_schema.ts +++ b/api/scripts/generate_schema.js @@ -6,7 +6,7 @@ import * as TJS from "typescript-json-schema"; import "missing-native-js-functions"; const schemaPath = path.join(__dirname, "..", "assets", "schemas.json"); -const settings: TJS.PartialArgs = { +const settings = { required: true, ignoreErrors: true, excludePrivate: true, @@ -14,23 +14,34 @@ const settings: TJS.PartialArgs = { noExtraProps: true, defaultProps: false }; -const compilerOptions: TJS.CompilerOptions = { +const compilerOptions = { strictNullChecks: true }; -const ExcludedSchemas = ["DefaultSchema", "Schema", "EntitySchema"]; +const Excluded = [ + "DefaultSchema", + "Schema", + "EntitySchema", + "ServerResponse", + "Http2ServerResponse", + "global.Express.Response", + "Response", + "e.Response", + "request.Response", + "supertest.Response" +]; function main() { const program = TJS.getProgramFromFiles(walk(path.join(__dirname, "..", "src", "routes")), compilerOptions); const generator = TJS.buildGenerator(program, settings); if (!generator || !program) return; - const schemas = generator.getUserSymbols().filter((x) => x.endsWith("Schema") && !ExcludedSchemas.includes(x)); + const schemas = generator.getUserSymbols().filter((x) => (x.endsWith("Schema") || x.endsWith("Response")) && !Excluded.includes(x)); console.log(schemas); - var definitions: any = {}; + var definitions = {}; for (const name of schemas) { - const part = TJS.generateSchema(program, name, settings, [], generator as TJS.JsonSchemaGenerator); + const part = TJS.generateSchema(program, name, settings, [], generator); if (!part) continue; definitions = { ...definitions, [name]: { ...part } }; @@ -39,11 +50,10 @@ function main() { fs.writeFileSync(schemaPath, JSON.stringify(definitions, null, 4)); } -// #/definitions/ main(); -function walk(dir: string) { - var results = [] as string[]; +function walk(dir) { + var results = []; var list = fs.readdirSync(dir); list.forEach(function (file) { file = dir + "/" + file; diff --git a/api/scripts/globalSetup.js b/api/scripts/globalSetup.js deleted file mode 100644 index 98e70fb9..00000000 --- a/api/scripts/globalSetup.js +++ /dev/null @@ -1,15 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const { FosscordServer } = require("../dist/Server"); -const Server = new FosscordServer({ port: 3001 }); -global.server = Server; -module.exports = async () => { - try { - fs.unlinkSync(path.join(__dirname, "..", "database.db")); - } catch {} - return await Server.start(); -}; - -// afterAll(async () => { -// return await Server.stop(); -// }); diff --git a/api/scripts/tsconfig-paths-bootstrap.js b/api/scripts/tsconfig-paths-bootstrap.js deleted file mode 100644 index d6ad3c57..00000000 --- a/api/scripts/tsconfig-paths-bootstrap.js +++ /dev/null @@ -1,10 +0,0 @@ -const tsConfigPaths = require("tsconfig-paths"); -const path = require("path"); - -const cleanup = tsConfigPaths.register({ - baseUrl: path.join(__dirname, ".."), - paths: { - "@fosscord/api": ["dist/index.js"], - "@fosscord/api/*": ["dist/*"] - } -}); |