fix(evobgp): проверять каталог и сохранять навигацию при ошибках API
This commit is contained in:
1 parent
a0263c22a5
commit
0d2affaf6b
7 files changed
+77
-4
No files matched your search
@@ -0,0 +1,17 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { test } from "node:test"
|
||||
import { parseEvoBgpCatalog } from "./evobgp-catalog"
|
||||
const empty = { fetchedAt: "2026-10-01T00:00:00Z", modules: [], domains: [], ipRanges: [], asns: [], communities: [] }
|
||||
const domain = { id: "domain-1", domain: "example.test", resolvedIp: "—", asn: "—", purpose: "", filter: "", updated: "—", enabled: false }
|
||||
test("known empty and false stay explicit without invented rows", () => {
|
||||
assert.deepEqual(parseEvoBgpCatalog(empty), empty)
|
||||
assert.equal(parseEvoBgpCatalog({ ...empty, domains: [domain] }).domains[0].enabled, false)
|
||||
})
|
||||
test("malformed or partial catalogue never reaches consumers as a snapshot", () => {
|
||||
for (const raw of [{}, null, { ...empty, domains: undefined }, { ...empty, asns: {} }, { ...empty, fetchedAt: "bad" }, { ...empty, domains: [null] }, { ...empty, domains: [{ ...domain, enabled: undefined }] }, { ...empty, domains: [domain, domain] }]) assert.throws(() => parseEvoBgpCatalog(raw), /Каталог Evolution не подтверждён/)
|
||||
})
|
||||
test("ASN and community zero counts are preserved, invalid counts rejected", () => {
|
||||
const asn = { id: "asn-1", asn: "AS1", org: "", country: "", prefixes: 0, filter: "", updated: "—", enabled: false }
|
||||
assert.equal(parseEvoBgpCatalog({ ...empty, asns: [asn] }).asns[0].prefixes, 0)
|
||||
assert.throws(() => parseEvoBgpCatalog({ ...empty, asns: [{ ...asn, prefixes: -1 }] }))
|
||||
})
|
||||
@@ -0,0 +1,22 @@
|
||||
import { z } from "zod"
|
||||
import type { EvoBgpCatalogSnapshot } from "./evobgp-context"
|
||||
const text = z.string()
|
||||
const id = text.min(1)
|
||||
const count = z.number().int().nonnegative().safe()
|
||||
function unique<T extends z.ZodType<{ id: string }>>(row: T) {
|
||||
return z.array(row).refine(rows => new Set(rows.map(item => item.id)).size === rows.length)
|
||||
}
|
||||
const base = { id, asn: text, purpose: text, filter: text, updated: text, enabled: z.boolean() }
|
||||
const schema = z.object({
|
||||
fetchedAt: text.refine(value => Number.isFinite(Date.parse(value))),
|
||||
modules: unique(z.object({ id, name: text, type: text })),
|
||||
domains: unique(z.object({ ...base, domain: text, resolvedIp: text })),
|
||||
ipRanges: unique(z.object({ ...base, cidr: text, country: text })),
|
||||
asns: unique(z.object({ id, asn: text, org: text, country: text, prefixes: count, filter: text, updated: text, enabled: z.boolean() })),
|
||||
communities: unique(z.object({ id, value: text, name: text, description: text, type: z.enum(["standard", "no-export", "no-advertise", "local-as", "custom"]), filterIds: z.array(text), serverCount: count, prefixCount: count, action: z.enum(["permit", "deny", "local-pref", "metric"]), actionValue: z.number().finite().optional(), enabled: z.boolean() })),
|
||||
})
|
||||
export function parseEvoBgpCatalog(raw: unknown): EvoBgpCatalogSnapshot {
|
||||
const parsed = schema.safeParse(raw)
|
||||
if (!parsed.success) throw new Error("Каталог Evolution не подтверждён: получен некорректный ответ сервера")
|
||||
return parsed.data
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from "react"
|
||||
import { useDataSource } from "@/lib/data-source"
|
||||
import type { Domain, IpRange, Asn } from "@/lib/data"
|
||||
import { parseEvoBgpCatalog } from "@/lib/evobgp-catalog"
|
||||
import { ApiClientError, requestJson } from "@/shared/api/http-client"
|
||||
|
||||
export interface EvoBgpCommunityRow {
|
||||
@@ -93,12 +94,12 @@ export function EvoBGPProvider({ children }: { children: React.ReactNode }) {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
const data = await requestJson<EvoBgpCatalogSnapshot>(
|
||||
const data = await requestJson<unknown>(
|
||||
backendUrl,
|
||||
"/api/evobgp/catalog",
|
||||
{ method: "POST" },
|
||||
)
|
||||
setSnapshot(data)
|
||||
setSnapshot(parseEvoBgpCatalog(data))
|
||||
} catch (e) {
|
||||
setSnapshot(null)
|
||||
setError(errorMessage(e, "Ошибка загрузки"))
|
||||
|
||||
Reference in new issue
Block a user