summary refs log tree commit diff
path: root/api/scripts/generate_openapi_schema.ts
diff options
context:
space:
mode:
Diffstat (limited to 'api/scripts/generate_openapi_schema.ts')
-rw-r--r--api/scripts/generate_openapi_schema.ts191
1 files changed, 191 insertions, 0 deletions
diff --git a/api/scripts/generate_openapi_schema.ts b/api/scripts/generate_openapi_schema.ts
new file mode 100644

index 00000000..c45a43eb --- /dev/null +++ b/api/scripts/generate_openapi_schema.ts
@@ -0,0 +1,191 @@ +// 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 generateSchemas() { + const program = TJS.getProgramFromFiles([path.join(__dirname, "..", "..", "util", "src", "index.ts")], compilerOptions); + const generator = TJS.buildGenerator(program, settings); + + const schemas = [ + "Application", + "Attachment", + "Message", + "AuditLog", + "Ban", + "Channel", + "Emoji", + "Guild", + "Invite", + "ReadState", + "Recipient", + "Relationship", + "Role", + "Sticker", + "Team", + "TeamMember", + "Template", + "VoiceState", + "Webhook", + "User", + "UserPublic" + ]; + + // @ts-ignore + const definitions = combineSchemas({ schemas, generator, program }); + + for (const key in definitions) { + specification.components.schemas[key] = definitions[key]; + delete definitions[key].additionalProperties; + } +} + +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, ...part.definitions, [name]: { ...part, definitions: undefined, $schema: undefined } }; + } + + return definitions; +} + +function generateBodies() { + 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 + const definitions = combineSchemas({ schemas, generator, program }); + + for (const key in definitions) { + specification.components.requestBodies[key] = { + content: { + "application/json": { schema: definitions[key] } + }, + description: "" + }; + + delete definitions[key].additionalProperties; + delete definitions[key].$schema; + } +} + +function addDefaultResponses() { + Object.values(specification.paths).forEach((path: any) => + Object.values(path).forEach((request: any) => { + if (!request.responses?.["401"]) { + request.responses["401"] = { + description: "Unauthorized", + content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } + }; + } + if (!request.responses?.["429"]) { + request.responses["429"] = { + description: "Rate limit exceeded", + content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } }, + headers: { + "X-RateLimit-Bucket": { + description: + "A unique string denoting the rate limit being encountered (non-inclusive of major parameters in the route path)", + schema: { type: "string" } + }, + "X-Rate-Limit-Limit": { + description: "The number of allowed requests in the current period", + schema: { + type: "integer" + } + }, + "X-Rate-Limit-Remaining": { + description: "The number of remaining requests in the current period", + schema: { + type: "integer" + } + }, + "X-Rate-Limit-Reset": { + description: "Date when current period is over in seconds since the Unix epoch", + schema: { + type: "integer" + } + }, + "X-Rate-Limit-Reset-After": { + description: "Number of seconds when current period will reset (can have decimal)", + schema: { + type: "number" + } + }, + "Retry-After": { + description: "Same as X-Rate-Limit-Reset-After but an integer", + schema: { + type: "integer" + } + }, + "X-RateLimit-Global": { + description: "Indicates whether or not all requests from your ip are rate limited", + schema: { + type: "boolean" + } + } + } + }; + } + }) + ); +} + +function main() { + addDefaultResponses(); + generateSchemas(); + specification = JSON.parse(JSON.stringify(specification).replaceAll("#/definitions", "#/components/schemas")); + + generateBodies(); + + fs.writeFileSync( + openapiPath, + JSON.stringify(specification, null, 4).replaceAll("#/definitions", "#/components/requestBodies").replaceAll("bigint", "number") + ); +} + +main();