mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
chore(core): add supplementary openapi json (#4472)
* chore(core): support supplementary openapi json * refactor(core): copy json files to build
This commit is contained in:
parent
00e72714de
commit
e35d099c35
5 changed files with 70 additions and 7 deletions
13
.vscode/settings.json
vendored
13
.vscode/settings.json
vendored
|
@ -5,7 +5,10 @@
|
|||
"source.fixAll.stylelint": true
|
||||
}
|
||||
},
|
||||
"stylelint.validate": ["css", "scss"],
|
||||
"stylelint.validate": [
|
||||
"css",
|
||||
"scss"
|
||||
],
|
||||
"eslint.workingDirectories": [
|
||||
{
|
||||
"pattern": "./packages/*",
|
||||
|
@ -23,6 +26,14 @@
|
|||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true,
|
||||
},
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"packages/core/src/routes/*.openapi.json"
|
||||
],
|
||||
"url": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/schemas/v3.0/schema.json"
|
||||
}
|
||||
],
|
||||
"cSpell.words": [
|
||||
"Alipay",
|
||||
"CIAM",
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"precommit": "lint-staged",
|
||||
"copyfiles": "copyfiles -u 1 src/**/*.md build",
|
||||
"copyfiles": "copyfiles -u 1 src/routes/**/*.openapi.json build/",
|
||||
"build": "rm -rf build/ && tsc -p tsconfig.build.json && pnpm run copyfiles",
|
||||
"build:test": "rm -rf build/ && tsc -p tsconfig.test.json --sourcemap",
|
||||
"lint": "eslint --ext .ts src",
|
||||
|
|
15
packages/core/src/routes/status.openapi.json
Normal file
15
packages/core/src/routes/status.openapi.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"paths": {
|
||||
"/api/status": {
|
||||
"get": {
|
||||
"summary": "Health check",
|
||||
"description": "The traditional health check API. No authentication needed.\n\n> **Note**\n> Even if 204 is returned, it does not guarantee all the APIs are working properly since they may depend on additional resources or external services.",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "The Logto core service is healthy."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,8 +51,6 @@ describe('GET /swagger.json', () => {
|
|||
|
||||
it('should contain the specific paths', async () => {
|
||||
const response = await mockSwaggerRequest.get('/swagger.json');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
expect(Object.entries(response.body.paths)).toHaveLength(2);
|
||||
expect(response.body.paths).toMatchObject({
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
'/api/mock': {
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { httpCodeToMessage } from '@logto/core-kit';
|
||||
import { conditionalArray, deduplicate, toTitle } from '@silverhand/essentials';
|
||||
import deepmerge from 'deepmerge';
|
||||
import type { IMiddleware } from 'koa-router';
|
||||
import type Router from 'koa-router';
|
||||
import type { OpenAPIV3 } from 'openapi-types';
|
||||
|
@ -146,6 +151,27 @@ const isManagementApiRouter = ({ stack }: Router) =>
|
|||
.filter(({ path }) => !path.includes('.*'))
|
||||
.some(({ stack }) => stack.some((function_) => isKoaAuthMiddleware(function_)));
|
||||
|
||||
/**
|
||||
* Recursively find all supplement files (files end with `.openapi.json`) for the given
|
||||
* directory.
|
||||
*/
|
||||
/* eslint-disable @silverhand/fp/no-mutating-methods, no-await-in-loop */
|
||||
const findSupplementFiles = async (directory: string) => {
|
||||
const result: string[] = [];
|
||||
|
||||
for (const file of await fs.readdir(directory)) {
|
||||
const stats = await fs.stat(path.join(directory, file));
|
||||
if (stats.isDirectory()) {
|
||||
result.push(...(await findSupplementFiles(path.join(directory, file))));
|
||||
} else if (file.endsWith('.openapi.json')) {
|
||||
result.push(path.join(directory, file));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
/* eslint-enable @silverhand/fp/no-mutating-methods, no-await-in-loop */
|
||||
|
||||
// Keep using `any` to accept various custom context types.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export default function swaggerRoutes<T extends AnonymousRouter, R extends Router<unknown, any>>(
|
||||
|
@ -185,17 +211,30 @@ export default function swaggerRoutes<T extends AnonymousRouter, R extends Route
|
|||
pathMap.set(path, { ...pathMap.get(path), [method]: operation });
|
||||
}
|
||||
|
||||
const document: OpenAPIV3.Document = {
|
||||
// Current path should be the root directory of routes files.
|
||||
const supplementPaths = await findSupplementFiles(path.dirname(fileURLToPath(import.meta.url)));
|
||||
const supplementDocuments = await Promise.all(
|
||||
supplementPaths.map(
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
async (path) => JSON.parse(await fs.readFile(path, 'utf8')) as Record<string, unknown>
|
||||
)
|
||||
);
|
||||
|
||||
const baseDocument: OpenAPIV3.Document = {
|
||||
openapi: '3.0.1',
|
||||
info: {
|
||||
title: 'Logto Core',
|
||||
version: '0.1.0',
|
||||
description: 'Management APIs for Logto core service.',
|
||||
version: 'Cloud',
|
||||
},
|
||||
paths: Object.fromEntries(pathMap),
|
||||
components: { schemas: translationSchemas },
|
||||
};
|
||||
|
||||
ctx.body = document;
|
||||
ctx.body = supplementDocuments.reduce(
|
||||
(document, supplement) => deepmerge(document, supplement),
|
||||
baseDocument
|
||||
);
|
||||
|
||||
return next();
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue