Files
2026-04-03 20:54:37 +03:00

118 lines
5.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Пошаговая демонстрация: api-format → OpenAPI 3.0 (детерминированный режим).
*
* node demo-steps.mjs — все шаги подряд в консоли
* node demo-steps.mjs --pause — пауза после каждого шага (Enter)
*
* Результат также пишется в demo-output/openapi.json рядом со скриптом.
*/
import { execFileSync } from "node:child_process";
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { createInterface } from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const EXAMPLE = join(__dirname, "examples", "api-format.example.json");
const OUT_DIR = join(__dirname, "demo-output");
const OUT_OPENAPI = join(OUT_DIR, "openapi.json");
const usePause = process.argv.includes("--pause");
function banner(title) {
const line = "═".repeat(Math.min(60, title.length + 8));
console.log(`\n${line}\n ${title}\n${line}\n`);
}
async function pause(msg = "Нажми Enter, чтобы перейти к следующему шагу…") {
if (!usePause) return;
const rl = createInterface({ input, output });
await rl.question(msg);
rl.close();
}
async function main() {
console.clear?.();
banner("Шаг 0. Задача");
console.log(
"У нас есть описание API в СВОЁМ формате (api-format), не OpenAPI.\n" +
"Нужно получить стандартную спецификацию OpenAPI 3.0 — для Swagger, клиентов, AID.\n" +
"Сейчас покажем путь на учебном примере (детерминированный маппинг в convert.mjs).",
);
await pause();
banner("Шаг 1. Входной файл (фрагмент api-format)");
console.log(`Файл: ${EXAMPLE}\n`);
const rawIn = readFileSync(EXAMPLE, "utf8");
const apiFormat = JSON.parse(rawIn);
console.log(JSON.stringify(apiFormat, null, 2));
console.log(
"\n↑ Это НЕ OpenAPI. Здесь: версия формата, info, basePath, ресурс Equipment с полями и операциями CRUD.",
);
await pause();
banner("Шаг 2. Что делает конвертер (логика)");
console.log(`
• apiFormatVersion "1" → включается ветка toOpenApiDeterministic в convert.mjs
Ресурс Equipment → components.schemas.Equipment + пути /api/equipment и /api/equipment/{id}
• Поля (string, uuid, enum…) → JSON Schema в components.schemas
• listQuery → query-параметры (_start, _end, _sort, _order, q, status…)
• security bearer → components.securitySchemes + security на операциях
`);
await pause();
banner("Шаг 3. Запуск convert.mjs");
mkdirSync(OUT_DIR, { recursive: true });
const convertScript = join(__dirname, "convert.mjs");
console.log(`Команда:\n node convert.mjs --in examples/api-format.example.json --out demo-output/openapi.json\n`);
execFileSync(process.execPath, [convertScript, "--in", EXAMPLE, "--out", OUT_OPENAPI], {
stdio: "inherit",
});
console.log(`\nГотово. Файл: ${OUT_OPENAPI}`);
await pause();
banner("Шаг 4. Результат — структура OpenAPI");
const spec = JSON.parse(readFileSync(OUT_OPENAPI, "utf8"));
console.log(`openapi: ${spec.openapi}`);
console.log(`title: ${spec.info?.title}`);
console.log(`version: ${spec.info?.version}`);
console.log("\nПути (paths):");
for (const p of Object.keys(spec.paths || {}).sort()) {
const methods = Object.keys(spec.paths[p]).join(", ");
console.log(` ${p} [${methods}]`);
}
console.log("\nСхемы (components.schemas):", Object.keys(spec.components?.schemas || {}).join(", "));
await pause();
banner("Шаг 5. Фрагмент: GET список (одна операция)");
const listPath = Object.keys(spec.paths || {}).find((k) => k.endsWith("/equipment") && !k.includes("{"));
if (listPath && spec.paths[listPath]?.get) {
console.log(JSON.stringify({ [listPath]: { get: spec.paths[listPath].get } }, null, 2));
} else {
console.log("(путь списка не найден — открой demo-output/openapi.json)");
}
await pause();
banner("Шаг 6. Как проверить дальше");
console.log(`
1) Открой целиком: ${OUT_OPENAPI}
2) Валидация (из корня репозитория):
npx -y @apidevtools/swagger-cli validate tools/api-format-to-openapi/demo-output/openapi.json
3) Через Nest (сервер на 3001):
POST http://127.0.0.1:3001/aid/export/openapi
тело: { "apiFormat": <содержимое api-format.example.json>, "mode": "deterministic" }
4) Режим LLM (другой входной JSON):
node convert.mjs --mode llm --in your.json --out openapi.llm.json
(нужен OPENAI_API_KEY)
`);
console.log("Демо завершено.\n");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});