NODEDC_1C/llm_normalizer/backend/src/services/schemaValidator.ts

60 lines
2.0 KiB
TypeScript

import fs from "fs";
import path from "path";
import Ajv2020, { type ErrorObject, type ValidateFunction } from "ajv/dist/2020";
import { SCHEMAS_DIR } from "../config";
import type { NormalizedPayload, ValidationResult } from "../types/normalizer";
type SchemaVersion = "v1" | "v2" | "v2_0_1" | "v2_0_2";
const validators = new Map<SchemaVersion, ValidateFunction>();
function schemaPath(version: SchemaVersion): string {
if (version === "v1") {
return path.resolve(SCHEMAS_DIR, "normalized_query_v1.json");
}
if (version === "v2_0_1") {
return path.resolve(SCHEMAS_DIR, "normalized_query_v2_0_1.json");
}
if (version === "v2_0_2") {
return path.resolve(SCHEMAS_DIR, "normalized_query_v2_0_2.json");
}
return path.resolve(SCHEMAS_DIR, "normalized_query_v2.json");
}
function loadValidator(version: SchemaVersion): ValidateFunction {
const cached = validators.get(version);
if (cached) {
return cached;
}
const raw = fs.readFileSync(schemaPath(version), "utf-8");
const schema = JSON.parse(raw);
const ajv = new Ajv2020({ allErrors: true, strict: false });
const compiled = ajv.compile(schema);
validators.set(version, compiled);
return compiled;
}
function normalizeAjvErrors(errors: ErrorObject[] | null | undefined): string[] {
if (!errors || errors.length === 0) {
return [];
}
return errors.map((item) => `${item.instancePath || "/"} ${item.message ?? "validation error"}`.trim());
}
export function validateNormalized(payload: unknown, schemaVersion: SchemaVersion = "v1"): ValidationResult {
const check = loadValidator(schemaVersion);
const passed = check(payload);
return {
passed: Boolean(passed),
errors: passed ? [] : normalizeAjvErrors(check.errors)
};
}
export function assertNormalized(payload: unknown, schemaVersion: SchemaVersion = "v1"): NormalizedPayload {
const validation = validateNormalized(payload, schemaVersion);
if (!validation.passed) {
throw new Error(`Invalid normalized JSON: ${validation.errors.join("; ")}`);
}
return payload as NormalizedPayload;
}