108 lines
3.7 KiB
TypeScript
108 lines
3.7 KiB
TypeScript
import type { OpenApiSpec, ResourceConfig, FieldConfig } 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);
|
|
});
|
|
}
|
|
|
|
function formatDisplayName(name: string): string {
|
|
return name.split(/[-_]/).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
|
|
}
|
|
|
|
const SSE_RECEIVED_FIELD: FieldConfig = {
|
|
name: "_received_at",
|
|
label: "Received",
|
|
description: "Timestamp when the event was received",
|
|
type: "string",
|
|
format: "date-time",
|
|
order: 0,
|
|
hidden: {},
|
|
filterable: false,
|
|
sortable: true,
|
|
readOnly: true,
|
|
required: false,
|
|
isArray: false,
|
|
};
|
|
|
|
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 hasSSE = collectionPathObj?.get?.["x-sse"] === true;
|
|
|
|
const resource: ResourceConfig = {
|
|
name: resourceName,
|
|
schemaName,
|
|
displayName: formatDisplayName(resourceName),
|
|
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") || hasOperation(itemPathObj, "patch"),
|
|
delete: hasOperation(itemPathObj, "delete"),
|
|
},
|
|
updateMethod: hasOperation(itemPathObj, "patch") && !hasOperation(itemPathObj, "put") ? "patch" : "put",
|
|
pagination: detectPagination(collectionPathObj),
|
|
relationships,
|
|
streaming: hasSSE || undefined,
|
|
};
|
|
|
|
if (hasSSE) {
|
|
resource.operations = { list: true, get: false, create: false, update: false, delete: false };
|
|
resource.updateMethod = "put";
|
|
resource.pagination = null;
|
|
resource.relationships = [];
|
|
resource.fields = [SSE_RECEIVED_FIELD, ...fields.map((f) => ({ ...f, readOnly: true }))];
|
|
resource.orderedFields = sortFields(resource.fields);
|
|
resource.listColumns = ["_received_at", ...resource.listColumns];
|
|
resource.primaryKey = "_received_at";
|
|
}
|
|
|
|
configs.push(resource);
|
|
}
|
|
|
|
return configs;
|
|
} |