feat: ERD for components and schemas
This commit is contained in:
102
server.js
102
server.js
@@ -151,6 +151,100 @@ function buildGraphFromPaths(pathsObject, spec) {
|
||||
return { nodes, links, endpoints, operations };
|
||||
}
|
||||
|
||||
function buildERDGraph(spec) {
|
||||
const components = spec.components || {};
|
||||
const schemas = components.schemas || {};
|
||||
const nodes = [];
|
||||
const links = [];
|
||||
const nodeMap = new Map();
|
||||
|
||||
// Create nodes for each schema
|
||||
for (const [schemaName, schema] of Object.entries(schemas)) {
|
||||
const resolved = resolveSchema(spec, schema) || {};
|
||||
const properties = resolved.properties || {};
|
||||
const requiredSet = new Set(Array.isArray(resolved.required) ? resolved.required : []);
|
||||
|
||||
const fields = [];
|
||||
for (const [propName, propSchema] of Object.entries(properties)) {
|
||||
const resolvedProp = resolveSchema(spec, propSchema) || {};
|
||||
const isRequired = requiredSet.has(propName);
|
||||
const propType = resolvedProp.type || 'object';
|
||||
let typeInfo = propType;
|
||||
|
||||
// Handle $ref in properties
|
||||
if (propSchema.$ref) {
|
||||
const refPath = propSchema.$ref.replace('#/components/schemas/', '');
|
||||
typeInfo = refPath;
|
||||
} else if (resolvedProp.$ref) {
|
||||
const refPath = resolvedProp.$ref.replace('#/components/schemas/', '');
|
||||
typeInfo = refPath;
|
||||
} else if (resolvedProp.items && resolvedProp.items.$ref) {
|
||||
const refPath = resolvedProp.items.$ref.replace('#/components/schemas/', '');
|
||||
typeInfo = `[${refPath}]`;
|
||||
} else if (resolvedProp.items && resolvedProp.items.type) {
|
||||
typeInfo = `[${resolvedProp.items.type}]`;
|
||||
} else if (Array.isArray(resolvedProp.enum)) {
|
||||
typeInfo = `enum(${resolvedProp.enum.length})`;
|
||||
}
|
||||
|
||||
fields.push({
|
||||
name: propName,
|
||||
type: typeInfo,
|
||||
required: isRequired
|
||||
});
|
||||
}
|
||||
|
||||
nodes.push({
|
||||
id: schemaName,
|
||||
name: schemaName,
|
||||
fields: fields,
|
||||
description: resolved.description || ''
|
||||
});
|
||||
nodeMap.set(schemaName, nodes[nodes.length - 1]);
|
||||
}
|
||||
|
||||
// Create links based on $ref relationships
|
||||
for (const node of nodes) {
|
||||
for (const field of node.fields) {
|
||||
// Check if field type references another schema
|
||||
const typeStr = String(field.type);
|
||||
if (typeStr && !typeStr.startsWith('[') && !typeStr.includes('enum') && nodeMap.has(typeStr)) {
|
||||
// Check if link already exists (avoid duplicates)
|
||||
const linkExists = links.some(l =>
|
||||
(l.source === node.id && l.target === typeStr) ||
|
||||
(l.target === node.id && l.source === typeStr)
|
||||
);
|
||||
if (!linkExists) {
|
||||
links.push({
|
||||
source: node.id,
|
||||
target: typeStr,
|
||||
field: field.name
|
||||
});
|
||||
}
|
||||
}
|
||||
// Handle array types like [SchemaName]
|
||||
const arrayMatch = typeStr.match(/^\[(.+)\]$/);
|
||||
if (arrayMatch && nodeMap.has(arrayMatch[1])) {
|
||||
const targetSchema = arrayMatch[1];
|
||||
const linkExists = links.some(l =>
|
||||
(l.source === node.id && l.target === targetSchema) ||
|
||||
(l.target === node.id && l.source === targetSchema)
|
||||
);
|
||||
if (!linkExists) {
|
||||
links.push({
|
||||
source: node.id,
|
||||
target: targetSchema,
|
||||
field: field.name,
|
||||
relationship: 'array'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { nodes, links };
|
||||
}
|
||||
|
||||
app.post('/api/parse', (req, res) => {
|
||||
try {
|
||||
const { yaml: yamlText } = req.body || {};
|
||||
@@ -164,8 +258,14 @@ app.post('/api/parse', (req, res) => {
|
||||
|
||||
const pathsObject = spec.paths || {};
|
||||
const graph = buildGraphFromPaths(pathsObject, spec);
|
||||
const erdGraph = buildERDGraph(spec);
|
||||
const servers = Array.isArray(spec.servers) ? spec.servers.map(s => s && s.url).filter(Boolean) : [];
|
||||
res.json({ graph, info: { title: spec.info && spec.info.title, version: spec.info && spec.info.version }, servers });
|
||||
res.json({
|
||||
graph,
|
||||
erdGraph,
|
||||
info: { title: spec.info && spec.info.title, version: spec.info && spec.info.version },
|
||||
servers
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: err.message || 'Failed to parse YAML' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user