mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-03 03:03:24 -04:00
feat(config-builder): add draft validation utilities
This commit is contained in:
34
apps/config-builder/src/lib/validation.test.ts
Normal file
34
apps/config-builder/src/lib/validation.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { validateConfigDraft } from "./validation.ts";
|
||||
|
||||
describe("validateConfigDraft", () => {
|
||||
it("accepts empty drafts", () => {
|
||||
const result = validateConfigDraft({});
|
||||
expect(result.valid).toBe(true);
|
||||
expect(result.issues).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("collects issues by path and section", () => {
|
||||
const result = validateConfigDraft({
|
||||
gateway: {
|
||||
auth: {
|
||||
token: 123,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.issues.length).toBeGreaterThan(0);
|
||||
expect(result.issuesByPath["gateway.auth.token"]?.length).toBeGreaterThan(0);
|
||||
expect(result.sectionErrorCounts.gateway).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("tracks root-level schema issues", () => {
|
||||
const result = validateConfigDraft({
|
||||
__unexpected__: true,
|
||||
});
|
||||
|
||||
expect(result.valid).toBe(false);
|
||||
expect(result.sectionErrorCounts.root).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
73
apps/config-builder/src/lib/validation.ts
Normal file
73
apps/config-builder/src/lib/validation.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { OpenClawSchema } from "@openclaw/config/zod-schema.ts";
|
||||
import type { ConfigDraft } from "./config-store.ts";
|
||||
|
||||
export type ValidationIssue = {
|
||||
path: string;
|
||||
section: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type ValidationResult = {
|
||||
valid: boolean;
|
||||
issues: ValidationIssue[];
|
||||
issuesByPath: Record<string, string[]>;
|
||||
sectionErrorCounts: Record<string, number>;
|
||||
};
|
||||
|
||||
function issuePath(path: Array<string | number>): string {
|
||||
if (path.length === 0) {
|
||||
return "";
|
||||
}
|
||||
return path
|
||||
.map((segment) => (typeof segment === "number" ? String(segment) : segment))
|
||||
.join(".");
|
||||
}
|
||||
|
||||
function issueSection(path: string): string {
|
||||
if (!path) {
|
||||
return "root";
|
||||
}
|
||||
const [section] = path.split(".");
|
||||
return section?.trim() || "root";
|
||||
}
|
||||
|
||||
export function validateConfigDraft(config: ConfigDraft): ValidationResult {
|
||||
const parsed = OpenClawSchema.safeParse(config);
|
||||
if (parsed.success) {
|
||||
return {
|
||||
valid: true,
|
||||
issues: [],
|
||||
issuesByPath: {},
|
||||
sectionErrorCounts: {},
|
||||
};
|
||||
}
|
||||
|
||||
const issues: ValidationIssue[] = parsed.error.issues.map((issue) => {
|
||||
const path = issuePath(issue.path);
|
||||
return {
|
||||
path,
|
||||
section: issueSection(path),
|
||||
message: issue.message,
|
||||
};
|
||||
});
|
||||
|
||||
const issuesByPath: Record<string, string[]> = {};
|
||||
const sectionErrorCounts: Record<string, number> = {};
|
||||
|
||||
for (const issue of issues) {
|
||||
const key = issue.path;
|
||||
if (!issuesByPath[key]) {
|
||||
issuesByPath[key] = [];
|
||||
}
|
||||
issuesByPath[key].push(issue.message);
|
||||
|
||||
sectionErrorCounts[issue.section] = (sectionErrorCounts[issue.section] ?? 0) + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
issues,
|
||||
issuesByPath,
|
||||
sectionErrorCounts,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user