import React, { useEffect, useState, useMemo } from "react"; import type { ProfileOperation, SpecConfiguration, ResourceConfig, ValidationMessage } from "../types"; import { AppContext } from "./AppContext"; import { loadSpec } from "../spec-loader"; import { validateSpec } from "../spec-validator"; import { buildResourceConfigs } from "../transformers/resource-config"; import { initApi } from "../hooks/useApi"; interface AppProviderProps { specConfiguration: SpecConfiguration; children: React.ReactNode; } function extractProfileOperations(spec: any): ProfileOperation[] { const ops: ProfileOperation[] = []; const paths = spec.paths ?? {}; for (const [path, methods] of Object.entries>(paths)) { for (const [method, operation] of Object.entries(methods)) { if (method.startsWith("x-")) continue; if (operation["x-profile"] === true) { ops.push({ path, method: method.toUpperCase(), operationId: operation.operationId ?? "", summary: operation.summary, }); } } } return ops; } export function AppProvider({ specConfiguration, children }: AppProviderProps) { const [loading, setLoading] = useState(true); const [resources, setResources] = useState([]); const [profileOperations, setProfileOperations] = useState([]); const [schemas, setSchemas] = useState>({}); const [errors, setErrors] = useState([]); const [warnings, setWarnings] = useState([]); const profileComponents = specConfiguration.profileComponents ?? {}; useEffect(() => { let cancelled = false; async function init() { try { setLoading(true); const spec = await loadSpec(specConfiguration.specUrl); const allMessages = validateSpec(spec, specConfiguration); const errs = allMessages.filter((m) => m.type === "error"); const warns = allMessages.filter((m) => m.type === "warning"); if (!cancelled) { setErrors(errs); setWarnings(warns); setSchemas(spec.components?.schemas ?? {}); setProfileOperations(extractProfileOperations(spec)); } if (errs.length === 0) { const configs = buildResourceConfigs(spec); if (!cancelled) { setResources(configs); } const baseUrl = specConfiguration.baseApiUrl ?? spec.servers?.[0]?.url ?? ""; if (baseUrl) { initApi(baseUrl, specConfiguration.getToken); } } } catch (e: any) { if (!cancelled) { const lines = (e.message ?? "Failed to load spec").split("\n"); setErrors(lines.map((msg: string) => ({ type: "error" as const, message: msg }))); } } finally { if (!cancelled) { setLoading(false); } } } init(); return () => { cancelled = true; }; }, [specConfiguration.specUrl]); const value = useMemo( () => ({ config: specConfiguration, resources, profileOperations, profileComponents, schemas, loading, errors, warnings, }), [specConfiguration, resources, profileOperations, profileComponents, schemas, loading, errors, warnings] ); return React.createElement(AppContext.Provider, { value }, children); }