feat(devices): canonical Zod device-record schema + mutation-path validation (JG-06)#50
Conversation
…idation (JG-06) Extract a typed source-of-truth for a device record, outside prose and the CDB key/value codec, with the CDB as one rendering of it: - src/devices-schema.ts: `deviceRecordSchema` (Zod) + `DeviceRecord` type + `parseDeviceRecord()` (throws `cdb/invalid-record` CentrsError). Permissive on target/profile *formats* and lenient on `recordType` — the decoder preserves types centrs does not name yet (`recordTypeName` → `unknown(N)`), so the model must round-trip them; the strict record-type *name* check stays at the `add` CLI boundary. `isKnownRecordType`/`recordTypeNames` exported for that check. - addDevice/setDevice validate the logical record before rendering to CDB, so a blank target or wrong field type fails for every caller (CLI, MCP, direct API, future JSON/YAML import) — not just the CLI's own guards. setDevice stays lenient on the inherited recordType, so editing an unusual existing record never regresses. - Cataloged the new code + generated docs/errors/cdb/invalid-record.md. Device-records-first per the plan; settings + the read/decode path are out of scope until this stays clean. Groundwork for the devices-edit TUI (JG-22) and a JSON/YAML device representation (JG-19). Local CDB only — no CHR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 42 minutes and 33 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds a canonical, Zod-backed schema for WinBox CDB device records and wires it into the device mutation path so invalid records fail consistently with a cataloged CentrsError.
Changes:
- Introduces
deviceRecordSchema+parseDeviceRecord()to validate logical device records and throwcdb/invalid-record. - Applies schema validation in
addDeviceandsetDevicebefore rendering records to CDB bytes. - Registers the new error code in the catalog and scaffolds its docs page; adds unit tests for the schema.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/devices-schema.test.ts | Adds unit coverage for the new schema and error behavior. |
| src/devices.ts | Validates mutation inputs via the canonical device-record schema before CDB encoding. |
| src/devices-schema.ts | Adds the canonical Zod schema, helpers (isKnownRecordType, recordTypeNames), and parseDeviceRecord() error mapping. |
| src/core/error-catalog.ts | Registers the new cdb/invalid-record error code. |
| docs/errors/cdb/invalid-record.md | Adds the required docs stub for the new error code. |
| function remediationFor(field: string): string { | ||
| switch (field) { | ||
| case "target": | ||
| return "Pass a non-blank address or MAC (optionally host:port) as the target."; | ||
| case "recordType": | ||
| return `Record type must be a non-negative integer; the named types are ${recordTypeNames.join(", ")}.`; | ||
| default: | ||
| return "Check the device-record field types: strings for user/password/session/comment/group/profile, a boolean for savedPassword."; | ||
| } | ||
| } |
| throw new CentrsError({ | ||
| code: "cdb/invalid-record", | ||
| summary: `Invalid device record: ${field} — ${issue?.message ?? "failed validation"}.`, | ||
| remediation: remediationFor(field), | ||
| context: { field }, | ||
| }); |
| // Validate the logical record against the canonical schema before rendering | ||
| // it to CDB bytes — a blank target or wrong field type fails here for every | ||
| // caller (CLI, MCP, direct API), not just the CLI's own guards. | ||
| const validated = parseDeviceRecord({ | ||
| recordType: args.recordType ?? winBoxCdbRecordType.ipAdmin, |
…se, wiring tests - parseDeviceRecord: a non-object input (empty Zod issue path) now blames the whole record with a `(record)` remediation pointing at recordType + target, and the flattened Zod issues ride `causeData` (summarized, not raw) so schema failures are debuggable beyond the human summary. - Add mutation-path wiring tests (devices-mutate.test.ts): addDevice rejects a blank target and setDevice rejects a wrong-typed field, both `cdb/invalid-record` — guarding the gate from accidental removal. Plus a schema test for the non-object `(record)` + causeData path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@coderabbitai please review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Get PR details and files changed
gh pr view 50 --repo tikoci/centrs --json title,body,files,additions,deletions,baseRefName,headRefNameLength of output: 3021 🏁 Script executed: #!/bin/bash
# Get the diff for the PR
gh pr diff 50 --repo tikoci/centrsLength of output: 14491
🔴
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
What
JG-06 (Stage 4): a canonical Zod model for a WinBox-CDB device record — the
typed source of truth, living outside prose and the CDB key/value codec, with the
CDB as one rendering of it.
src/devices-schema.ts—deviceRecordSchema+DeviceRecordtype +parseDeviceRecord()(throws acdb/invalid-recordCentrsError). It ispermissive about target/profile formats (a target may be IPv4/IPv6/MAC,
optionally
host:port; a profile may be a<none>/own sentinel) andlenient about
recordType— the CDB decoder preserves record types centrsdoes not name yet (
recordTypeName→unknown(N)), so the canonical modelmust round-trip them. The strict record-type name check stays at the
addCLI boundary where a human types a name;
isKnownRecordType/recordTypeNamesare exported for that.
addDevice/setDevicevalidate the logicalrecord before rendering to CDB bytes, so a blank target or wrong field type
fails with an actionable error for every caller (CLI, MCP, direct API, and
a future JSON/YAML import), not just the CLI's own guards.
setDevicestayslenient on the inherited
recordType/target, so editing an unusualexisting record never regresses.
cdb/invalid-recordand regenerateddocs/errors/cdb/invalid-record.md.Scope
Device-records-first, per the plan — settings and the read/decode path are
deliberately out of scope until this layer stays clean. This lays groundwork for
the deferred devices-edit TUI (JG-22) and a JSON/YAML device representation (JG-19).
Why no CHR
The device registry is the local WinBox CDB (file I/O), not a router. No
transport/RouterOS path is touched, so this is not CHR-gated (the network-free
cli-smoketier already coversdevices add/list/remove).Verification
bun run lint+bun run lint:ci+bun run test(710 pass / 0 fail / 26 skip)bun run buildall green.🤖 Generated with Claude Code