"use client"; import { CustomDropdown } from "@/src/components/ui/custom-dropdown"; import type { DropdownOption } from "@/src/components/ui/custom-dropdown"; import React, { useState, useEffect } from "react"; // Define schema type to match the actual structure from the API interface SchemaFile { id: string; relativePath: string; apiSchema?: string | null; _sys: { filename: string; }; } // Interface for parsed Swagger/OpenAPI details interface SchemaDetails { title?: string; version?: string; endpointCount: number; endpoints: Endpoint[]; } // Interface for endpoint details interface Endpoint { path: string; method: string; summary: string; operationId?: string; } // Parse Swagger/OpenAPI JSON to extract details const parseSwaggerJson = (jsonContent: string): SchemaDetails => { try { const parsed = JSON.parse(jsonContent); // Extract endpoints const endpoints: Endpoint[] = []; if (parsed.paths) { for (const path of Object.keys(parsed.paths)) { const pathObj = parsed.paths[path]; for (const method of Object.keys(pathObj)) { const operation = pathObj[method]; endpoints.push({ path, method: method.toUpperCase(), summary: operation.summary || `${method.toUpperCase()} ${path}`, operationId: operation.operationId, }); } } } return { title: parsed.info?.title || "Unknown API", version: parsed.info?.version || "Unknown Version", endpointCount: endpoints.length, endpoints, }; } catch (error) { return { title: "Error Parsing Schema", version: "Unknown", endpointCount: 0, endpoints: [], }; } }; const getSchemas = async () => { try { const { schemas } = await fetchSchemas(); if (schemas) { // Convert API response into our simpler SchemaFile interface const schemaFiles: SchemaFile[] = schemas.map((schema) => ({ id: schema.id, relativePath: schema.filename, apiSchema: schema.apiSchema, _sys: { filename: schema.displayName, }, })); return schemaFiles; } return []; } catch (error) { return []; } }; const fetchSchemas = async () => { const response = await fetch("/api/list-api-schemas"); return await response.json(); }; const getSchemaDetails = async (schemaPath: string, schemas: SchemaFile[]) => { try { // Find the selected schema const selectedSchema = schemas.find((s) => s.relativePath === schemaPath); if (selectedSchema?.apiSchema) { const details = parseSwaggerJson(selectedSchema.apiSchema); return details; } // If the schema content isn't in the current data, fetch it const { schemas: data } = await fetchSchemas(); if (data?.apiSchema) { const details = parseSwaggerJson(data.apiSchema); return details; } } catch (error) { return null; } }; // Custom field for selecting an API schema file const SchemaSelector = (props: any) => { const { input, field } = props; const [schemas, setSchemas] = useState([]); const [loading, setLoading] = useState(true); const [schemaDetails, setSchemaDetails] = useState( null ); const [loadingDetails, setLoadingDetails] = useState(false); const [selectedEndpoint, setSelectedEndpoint] = useState(""); // Fetch schema details when a schema is selected useEffect(() => { if (!input.value) { setSchemaDetails(null); return; } const parts = input.value.split("|"); if (parts.length > 1) { setSelectedEndpoint(parts[1]); } else { setSelectedEndpoint(""); } const fetchSchemaDetails = async () => { setLoadingDetails(true); const details = await getSchemaDetails( input.value.split("|")[0], schemas ); setSchemaDetails(details); setLoadingDetails(false); }; fetchSchemaDetails(); }, [input.value, schemas]); // Fetch available schema files when component mounts useEffect(() => { const fetchSchemas = async () => { setLoading(true); const schemas = await getSchemas(); setSchemas(schemas); setLoading(false); }; fetchSchemas(); }, []); const handleSchemaChange = async (schemaPath: string) => { // Reset endpoint selection when schema changes if (!schemaDetails) { setLoadingDetails(true); const details = await getSchemaDetails( input.value.split("|")[0], schemas ); setSchemaDetails(details); setLoadingDetails(false); } setSelectedEndpoint(""); input.onChange(schemaPath); }; const handleEndpointChange = (endpoint: string) => { setSelectedEndpoint(endpoint); // Extract just the schema path const schemaPath = input.value.split("|")[0]; // Combine schema path and endpoint input.onChange(endpoint ? `${schemaPath}|${endpoint}` : schemaPath); }; // Helper function to create a unique endpoint identifier const createEndpointId = (endpoint: Endpoint) => { return `${endpoint.method}:${endpoint.path}`; }; return (
{loading ? (
Loading schemas...
) : schemas.length === 0 ? (
No API schema files found. Please upload one in the Content Manager.
) : (
{/* Schema selector dropdown */} ((schema) => ({ value: schema.relativePath, label: schema._sys.filename, })), ]} placeholder="Select a schema" /> {input.value && (
Selected schema:{" "} { schemas.find( (s) => s.relativePath === input.value.split("|")[0] )?._sys.filename }
{loadingDetails ? (
Loading schema details...
) : schemaDetails ? ( <>
API Name
{schemaDetails.title}
Version
{schemaDetails.version}
Endpoints
{schemaDetails.endpointCount}
{/* Endpoint selector */} {schemaDetails.endpoints.length > 0 && (
{/* Endpoint selector dropdown */} a.path.localeCompare(b.path)) .map((endpoint) => ({ value: createEndpointId(endpoint), label: `${endpoint.method} ${endpoint.path} ${ endpoint.summary ? `- ${endpoint.summary}` : "" }`, })), ]} placeholder="All Endpoints" contentClassName="bg-white border border-blue-300" />
)} ) : (
Unable to load schema details
)}
)}
)} {field.description && (

{field.description}

)}

Note: To add more schema files, go to the Content Manager and add files to the API Schema collection.

); }; export const ApiReferenceTemplate = { name: "apiReference", label: "API Reference", ui: { defaultItem: { schemaFile: "test-doc.json", }, }, fields: [ { type: "string", name: "schemaFile", label: "API Schema", description: "Select a Swagger/OpenAPI schema file to display in this component. Optionally select a specific endpoint to display.", ui: { component: SchemaSelector, }, }, ], };