Refactor code structure for improved readability and maintainability; removed redundant code blocks and optimized function calls.
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled

This commit is contained in:
master
2025-11-20 07:50:52 +02:00
parent 616ec73133
commit 10212d67c0
473 changed files with 316758 additions and 388 deletions

View File

@@ -0,0 +1,139 @@
#!/usr/bin/env node
// Verifies every OpenAPI operation has at least one request example and one response example.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { parse } from 'yaml';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const ROOT = path.resolve(__dirname, '..');
const OAS_ROOT = path.join(ROOT, 'src', 'Api', 'StellaOps.Api.OpenApi');
async function main() {
if (!fs.existsSync(OAS_ROOT)) {
console.log('[api:examples] no OpenAPI directory found; skipping');
return;
}
const files = await findYamlFiles(OAS_ROOT);
if (files.length === 0) {
console.log('[api:examples] no OpenAPI files found; skipping');
return;
}
const failures = [];
for (const relative of files) {
const fullPath = path.join(OAS_ROOT, relative);
const content = fs.readFileSync(fullPath, 'utf8');
let doc;
try {
doc = parse(content, { prettyErrors: true });
} catch (err) {
failures.push({ file: relative, path: '', method: '', reason: `YAML parse error: ${err.message}` });
continue;
}
const paths = doc?.paths || {};
for (const [route, methods] of Object.entries(paths)) {
for (const [method, operation] of Object.entries(methods || {})) {
if (!isHttpMethod(method)) continue;
const hasRequestExample = operation?.requestBody ? hasExample(operation.requestBody) : true;
const hasResponseExample = Object.values(operation?.responses || {}).some(resp => hasExample(resp));
if (!hasRequestExample || !hasResponseExample) {
const missing = [];
if (!hasRequestExample) missing.push('request');
if (!hasResponseExample) missing.push('response');
failures.push({ file: relative, path: route, method, reason: `missing ${missing.join(' & ')} example` });
}
}
}
}
if (failures.length > 0) {
console.error('[api:examples] found operations without examples:');
for (const f of failures) {
const locus = [f.file, f.path, f.method.toUpperCase()].filter(Boolean).join(' ');
console.error(` - ${locus}: ${f.reason}`);
}
process.exit(1);
}
console.log('[api:examples] all operations contain request and response examples');
}
async function findYamlFiles(root) {
const results = [];
async function walk(dir) {
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
await walk(full);
} else if (entry.isFile() && entry.name.toLowerCase().endsWith('.yaml')) {
results.push(path.relative(root, full));
}
}
}
await walk(root);
return results;
}
function isHttpMethod(method) {
return ['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'trace'].includes(method.toLowerCase());
}
function hasExample(node) {
if (!node) return false;
// request/response objects may include content -> mediaType -> schema/example/examples
const content = node.content || {};
for (const media of Object.values(content)) {
if (!media) continue;
if (media.example !== undefined) return true;
if (media.examples && Object.keys(media.examples).length > 0) return true;
if (media.schema && hasSchemaExample(media.schema)) return true;
}
// response objects may have "examples" directly (non-standard but allowed by spectral rules)
if (node.examples && Object.keys(node.examples).length > 0) return true;
return false;
}
function hasSchemaExample(schema) {
if (!schema) return false;
if (schema.example !== undefined) return true;
if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) return true;
// Recurse into allOf/oneOf/anyOf
const composites = ['allOf', 'oneOf', 'anyOf'];
for (const key of composites) {
if (Array.isArray(schema[key])) {
if (schema[key].some(hasSchemaExample)) return true;
}
}
// For objects, check properties
if (schema.type === 'object' && schema.properties) {
for (const value of Object.values(schema.properties)) {
if (hasSchemaExample(value)) return true;
}
}
// For arrays, check items
if (schema.type === 'array' && schema.items) {
return hasSchemaExample(schema.items);
}
return false;
}
main().catch(err => {
console.error('[api:examples] fatal error', err);
process.exit(1);
});