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

210 lines
4.5 KiB
TypeScript
Raw Normal View History

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