fix: pre-fetch URL spec then bundle+dereference to handle self-referencing $ref

This commit is contained in:
2026-04-02 15:00:32 +08:00
parent 6aaba810d8
commit afd8b444c7
2 changed files with 17 additions and 12 deletions

View File

@@ -115,8 +115,19 @@ function parseOpenApi3Endpoints(api: OpenApiDoc): { endpoints: ParsedEndpoint[];
} }
export async function parseOpenApiDocument(input: string | object): Promise<ParseResult> { export async function parseOpenApiDocument(input: string | object): Promise<ParseResult> {
// Parse and dereference all $refs inline let specInput: string | object = input;
const api = await SwaggerParser.dereference(input as any, {
// If input is a URL, fetch the content first so that swagger-parser
// works on a plain object and doesn't need network access for $ref resolution
if (typeof input === 'string' && input.startsWith('http')) {
const res = await fetch(input);
if (!res.ok) throw new Error(`Failed to fetch spec from URL: ${res.status} ${res.statusText}`);
specInput = await res.json();
}
// Bundle resolves all $refs into a single document, then dereference inlines them
const bundled = await SwaggerParser.bundle(specInput as any) as OpenAPI.Document;
const api = await SwaggerParser.dereference(bundled, {
dereference: { circular: 'ignore' }, dereference: { circular: 'ignore' },
}) as OpenAPI.Document; }) as OpenAPI.Document;

View File

@@ -31,20 +31,14 @@ export default function ImportDialog({ onClose }: { onClose: () => void }) {
setLoading(true); setLoading(true);
setError(''); setError('');
try { try {
let spec: unknown; let body: Record<string, unknown>;
if (mode === 'url') { if (mode === 'url') {
// Fetch from browser (can access local network) instead of letting server fetch body = { specUrl: url };
const res = await fetch(url);
if (!res.ok) throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`);
const text = await res.text();
try { spec = JSON.parse(text); } catch { spec = text; }
} else { } else {
try { spec = JSON.parse(fileContent); } catch { spec = fileContent; } try { body = { spec: JSON.parse(fileContent) }; } catch { body = { spec: fileContent }; }
} }
const data = await apiFetch<ImportResult>('/projects', { const data = await apiFetch<ImportResult>('/projects', {
method: 'POST', body: JSON.stringify({ spec }), method: 'POST', body: JSON.stringify(body),
}); });
setResult(data); setResult(data);
queryClient.invalidateQueries({ queryKey: ['projects'] }); queryClient.invalidateQueries({ queryKey: ['projects'] });