updated react-openapi

This commit is contained in:
2026-06-17 21:03:08 +05:30
parent cd89eb4c88
commit 0a668cf98d
64 changed files with 2412 additions and 2921 deletions

View File

@@ -0,0 +1,53 @@
import type { FieldConfig } from "../types";
function resolveRef(ref: string): string | undefined {
return ref.split("/").pop();
}
export function extractFields(schemaName: string, schema: any, schemas: Record<string, any>): FieldConfig[] {
const props = schema.properties ?? {};
const requiredFields: string[] = schema.required ?? [];
return Object.entries(props)
.filter(([, prop]: [string, any]) => prop && typeof prop === "object")
.map(([name, prop]: [string, any]) => {
const isDirectRef = !!prop.$ref;
const isItemsRef = prop.type === "array" && !!prop.items?.$ref;
const isRef = isDirectRef || isItemsRef;
const refSchemaName = isDirectRef
? resolveRef(prop.$ref)
: isItemsRef
? resolveRef(prop.items.$ref)
: undefined;
const refSchema = refSchemaName ? schemas[refSchemaName] : undefined;
const inlineDisplayFormat = isRef && refSchema && !prop["x-fk"]
? refSchema["x-display-format"]
: undefined;
const field: FieldConfig = {
name,
label: prop["x-label"],
description: prop["x-description"] ?? prop["x-label"] ?? name,
type: isRef && refSchema ? "object" : (prop.type ?? "string"),
format: prop.format,
order: prop["x-order"],
hidden: prop["x-hidden"] ?? {},
filterable: prop["x-filterable"] ?? false,
sortable: prop["x-sortable"] ?? false,
readOnly: prop.readOnly ?? false,
required: requiredFields.includes(name),
enumValues: prop.enum,
fk: prop["x-fk"],
uiType: prop["x-ui-type"],
uploadUrl: prop["x-upload-url"],
refSchema: refSchemaName,
inlineDisplayFormat,
isArray: prop.type === "array",
};
return field;
});
}

View File

@@ -0,0 +1,32 @@
import type { FKFieldConfig, ResourceRelationship } from "../types";
export function extractRelationships(schema: any, schemas: Record<string, any>): ResourceRelationship[] {
const props = schema.properties ?? {};
const rels: ResourceRelationship[] = [];
for (const [name, _raw] of Object.entries(props)) {
const prop = _raw as any;
if (!prop || typeof prop !== "object") continue;
if (!prop["x-fk"]) continue;
const fkResource = prop["x-fk"].resource as string;
const targetEntry = Object.entries(schemas).find(([, s]) => s?.["x-resource"] === fkResource);
const targetSchemaName = targetEntry ? targetEntry[0] : fkResource;
const prefetch = prop["x-fk"].prefetch ?? false;
console.log(`[FK] extracted relationship: field="${name}" target="${fkResource}" prefetch=${prefetch} rawPrefetch=${prop["x-fk"].prefetch}`);
rels.push({
fieldName: name,
config: {
resource: fkResource,
prefetch,
},
targetSchemaName,
});
}
console.log(`[FK] total relationships extracted: ${rels.length}`);
return rels;
}

View File

@@ -0,0 +1,74 @@
import type { OpenApiSpec, ResourceConfig, FieldConfig, ResourceRelationship } from "../types";
import { extractFields } from "./field-config";
import { extractRelationships } from "./relationship-config";
function detectPagination(pathObj: any): { limitParam: string; offsetParam: string; defaultLimit: number } | null {
const params = pathObj?.get?.parameters ?? [];
const limit = params.find((p: any) => p.in === "query" && p.name === "limit");
const offset = params.find((p: any) => p.in === "query" && p.name === "offset");
if (limit && offset) {
return {
limitParam: "limit",
offsetParam: "offset",
defaultLimit: limit.schema.default,
};
}
return null;
}
function hasOperation(pathObj: any, method: string): boolean {
return !!pathObj?.[method];
}
function sortFields(fields: FieldConfig[]): FieldConfig[] {
return [...fields].sort((a, b) => {
const orderDiff = a.order - b.order;
if (orderDiff !== 0) return orderDiff;
return a.name.localeCompare(b.name);
});
}
export function buildResourceConfigs(spec: OpenApiSpec): ResourceConfig[] {
const schemas = spec.components?.schemas ?? {};
const paths = spec.paths ?? {};
const configs: ResourceConfig[] = [];
for (const [schemaName, schema] of Object.entries(schemas)) {
if (!schema || typeof schema !== "object") continue;
const resourceName = schema["x-resource"];
if (!resourceName || typeof resourceName !== "string") continue;
const resourcePath = `/${resourceName}`;
const itemPath = `${resourcePath}/{id}`;
const collectionPathObj = paths[resourcePath];
const itemPathObj = paths[itemPath];
const fields = extractFields(schemaName, schema, schemas);
const relationships = extractRelationships(schema, schemas);
const resource: ResourceConfig = {
name: resourceName,
schemaName,
path: resourcePath,
primaryKey: schema["x-primary-key"],
displayFormat: schema["x-display-format"],
listColumns: schema["x-list-columns"],
fields,
orderedFields: sortFields(fields),
operations: {
list: hasOperation(collectionPathObj, "get"),
get: hasOperation(itemPathObj, "get"),
create: hasOperation(collectionPathObj, "post"),
update: hasOperation(itemPathObj, "put"),
delete: hasOperation(itemPathObj, "delete"),
},
pagination: detectPagination(collectionPathObj),
relationships,
};
configs.push(resource);
}
return configs;
}