iceshrimp-legacy/packages/backend/src/server/api/openapi/gen-spec.ts

227 lines
4.6 KiB
TypeScript
Raw Normal View History

2023-01-13 05:40:33 +01:00
import endpoints from "../endpoints.js";
import config from "@/config/index.js";
import { errors as basicErrors } from "./errors.js";
import { schemas, convertSchemaToOpenApiSchema } from "./schemas.js";
2019-02-23 20:08:08 +01:00
2022-06-14 11:54:55 +02:00
export function genOpenapiSpec() {
2019-02-23 20:08:08 +01:00
const spec = {
2023-01-13 05:40:33 +01:00
openapi: "3.0.0",
2019-02-23 20:08:08 +01:00
info: {
2023-01-13 05:40:33 +01:00
version: "v1",
title: "Calckey API",
"x-logo": { url: "/static-assets/api-doc.png" },
2019-02-23 20:08:08 +01:00
},
externalDocs: {
2023-01-13 05:40:33 +01:00
description: "Repository",
url: "https://codeberg.org/calckey/calckey",
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
servers: [
{
url: config.apiUrl,
},
],
2019-02-23 20:08:08 +01:00
paths: {} as any,
components: {
schemas: schemas,
securitySchemes: {
ApiKeyAuth: {
2023-01-13 05:40:33 +01:00
type: "apiKey",
in: "body",
name: "i",
2021-12-09 15:58:30 +01:00
},
// TODO: change this to oauth2 when the remaining oauth stuff is set up
Bearer: {
2023-01-13 05:40:33 +01:00
type: "http",
scheme: "bearer",
},
2021-12-09 15:58:30 +01:00
},
},
2019-02-23 20:08:08 +01:00
};
2023-01-13 05:40:33 +01:00
for (const endpoint of endpoints.filter((ep) => !ep.meta.secure)) {
2019-02-23 20:08:08 +01:00
const errors = {} as any;
if (endpoint.meta.errors) {
for (const e of Object.values(endpoint.meta.errors)) {
errors[e.code] = {
value: {
2021-12-09 15:58:30 +01:00
error: e,
},
2019-02-23 20:08:08 +01:00
};
}
}
2023-01-13 05:40:33 +01:00
const resSchema = endpoint.meta.res
? convertSchemaToOpenApiSchema(endpoint.meta.res)
: {};
let desc =
(endpoint.meta.description
? endpoint.meta.description
: "No description provided.") + "\n\n";
desc += `**Credential required**: *${
endpoint.meta.requireCredential ? "Yes" : "No"
}*`;
if (endpoint.meta.kind) {
const kind = endpoint.meta.kind;
desc += ` / **Permission**: *${kind}*`;
}
2019-02-25 01:37:22 +01:00
2023-01-13 05:40:33 +01:00
const requestType = endpoint.meta.requireFile
? "multipart/form-data"
: "application/json";
const schema = endpoint.params;
if (endpoint.meta.requireFile) {
schema.properties.file = {
2023-01-13 05:40:33 +01:00
type: "string",
format: "binary",
description: "The file contents.",
};
2023-01-13 05:40:33 +01:00
schema.required.push("file");
}
const security = [
{
ApiKeyAuth: [],
},
{
Bearer: [],
},
];
if (!endpoint.meta.requireCredential) {
// add this to make authentication optional
security.push({});
}
2019-02-23 20:08:08 +01:00
const info = {
operationId: endpoint.name,
summary: endpoint.name,
2019-02-25 01:37:22 +01:00
description: desc,
2019-02-23 20:08:08 +01:00
externalDocs: {
2023-01-13 05:40:33 +01:00
description: "Source code",
url: `https://codeberg.org/calckey/calckey/src/branch/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`,
2019-02-23 20:08:08 +01:00
},
tags: endpoint.meta.tags || undefined,
security,
2019-02-23 20:08:08 +01:00
requestBody: {
required: true,
content: {
[requestType]: {
schema,
2021-12-09 15:58:30 +01:00
},
},
2019-02-23 20:08:08 +01:00
},
responses: {
2023-01-13 05:40:33 +01:00
...(endpoint.meta.res
? {
"200": {
description: "OK (with results)",
content: {
"application/json": {
schema: resSchema,
},
},
2021-12-09 15:58:30 +01:00
},
2023-01-13 05:40:33 +01:00
}
: {
"204": {
description: "OK (without any results)",
},
}),
"400": {
description: "Client error",
2019-02-23 20:08:08 +01:00
content: {
2023-01-13 05:40:33 +01:00
"application/json": {
2019-02-23 20:08:08 +01:00
schema: {
2023-01-13 05:40:33 +01:00
$ref: "#/components/schemas/Error",
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
examples: { ...errors, ...basicErrors["400"] },
2021-12-09 15:58:30 +01:00
},
},
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
"401": {
description: "Authentication error",
2019-02-23 20:08:08 +01:00
content: {
2023-01-13 05:40:33 +01:00
"application/json": {
2019-02-23 20:08:08 +01:00
schema: {
2023-01-13 05:40:33 +01:00
$ref: "#/components/schemas/Error",
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
examples: basicErrors["401"],
2021-12-09 15:58:30 +01:00
},
},
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
"403": {
description: "Forbidden error",
2019-02-23 20:08:08 +01:00
content: {
2023-01-13 05:40:33 +01:00
"application/json": {
2019-02-23 20:08:08 +01:00
schema: {
2023-01-13 05:40:33 +01:00
$ref: "#/components/schemas/Error",
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
examples: basicErrors["403"],
2021-12-09 15:58:30 +01:00
},
},
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
"418": {
description: "I'm Calc",
2019-02-23 20:08:08 +01:00
content: {
2023-01-13 05:40:33 +01:00
"application/json": {
2019-02-23 20:08:08 +01:00
schema: {
2023-01-13 05:40:33 +01:00
$ref: "#/components/schemas/Error",
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
examples: basicErrors["418"],
2021-12-09 15:58:30 +01:00
},
},
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
...(endpoint.meta.limit
? {
"429": {
description: "To many requests",
content: {
"application/json": {
schema: {
$ref: "#/components/schemas/Error",
},
examples: basicErrors["429"],
},
2019-02-23 20:08:08 +01:00
},
2021-12-09 15:58:30 +01:00
},
2023-01-13 05:40:33 +01:00
}
: {}),
"500": {
description: "Internal server error",
2019-02-23 20:08:08 +01:00
content: {
2023-01-13 05:40:33 +01:00
"application/json": {
2019-02-23 20:08:08 +01:00
schema: {
2023-01-13 05:40:33 +01:00
$ref: "#/components/schemas/Error",
2019-02-23 20:08:08 +01:00
},
2023-01-13 05:40:33 +01:00
examples: basicErrors["500"],
2021-12-09 15:58:30 +01:00
},
},
2019-02-23 20:08:08 +01:00
},
2021-12-09 15:58:30 +01:00
},
2019-02-23 20:08:08 +01:00
};
const path = {
2021-12-09 15:58:30 +01:00
post: info,
2019-02-23 20:08:08 +01:00
};
if (endpoint.meta.allowGet) {
path.get = { ...info };
// API Key authentication is not permitted for GET requests
2023-01-13 05:40:33 +01:00
path.get.security = path.get.security.filter(
(elem) => !Object.prototype.hasOwnProperty.call(elem, "ApiKeyAuth"),
);
}
2023-01-13 05:40:33 +01:00
spec.paths[`/${endpoint.name}`] = path;
2019-02-23 20:08:08 +01:00
}
return spec;
}