Add JSON Schema documentation for bambulab/v1, with real inheritance
Mirrors src/printer/bambu_commands/ as language-agnostic schema docs (not used for codegen — the Rust structs stay hand-written). envelope.schema.json is the base; get_version/pushall request+response schemas extend it via allOf + $ref, which is genuinely closer to real inheritance than anything else in this project's schema stack: proto and Rust only ever gave us composition (embed a field holding the base struct), never a mechanism that reuses a *referenced* schema's constraints automatically. Verified with the jsonschema Python library, not just written by hand: all three real examples from the conversation validate correctly, and — importantly — negative tests confirm the base schema's required fields and the command consts are actually enforced through the allOf chain, not just documented. README covers the one real gotcha (additionalProperties: false on a base schema silently breaks allOf composition, since allOf validates each sub-schema against the whole instance independently rather than merging them).
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# bambulab/v1 JSON Schemas
|
||||
|
||||
Documentation/validation schemas mirroring `src/printer/bambu_commands/` —
|
||||
not used for codegen (the Rust structs are hand-written, see that module's
|
||||
doc comment for why), but useful on their own: as language-agnostic
|
||||
documentation of the wire format, or for validating a captured MQTT
|
||||
payload without writing Rust to do it.
|
||||
|
||||
## The "inheritance" pattern
|
||||
|
||||
`envelope.schema.json` is the base — `sequence_id` + `command`, the two
|
||||
fields every command/response shares. Every other schema here *extends*
|
||||
it with `allOf` + `$ref`:
|
||||
|
||||
```json
|
||||
{
|
||||
"allOf": [
|
||||
{ "$ref": "envelope.schema.json" },
|
||||
{ "type": "object", "properties": { "command": { "const": "get_version" } } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This is genuinely closer to real inheritance than anything else in this
|
||||
project's schema stack (proto, Rust): the instance must satisfy the base
|
||||
schema *and* the extension schema simultaneously, and — verified, not just
|
||||
asserted — the base's `required: ["sequence_id", "command"]` is actually
|
||||
enforced on every schema that extends it, not just copy-pasted as
|
||||
documentation. Proto and Rust only ever gave us composition (embed a field
|
||||
holding the base), never something that reuses a *named, referenced*
|
||||
schema's constraints automatically like this.
|
||||
|
||||
**One gotcha if you add a new base-style schema**: don't set
|
||||
`"additionalProperties": false` on something meant to be `allOf`-extended.
|
||||
`allOf` validates every sub-schema against the *whole* instance
|
||||
independently — it doesn't merge object schemas — so a closed base schema
|
||||
would reject every field the extending schema adds. Leave
|
||||
`additionalProperties` unset (defaults to allowed) on anything used as a
|
||||
base; enforce closedness only on the top-level schemas
|
||||
(`request.schema.json`/`report.schema.json` do this correctly — check
|
||||
their `additionalProperties: false`).
|
||||
|
||||
## Files
|
||||
|
||||
- `envelope.schema.json` — the base
|
||||
- `module_info.schema.json` — nested object used inside `get_version`'s response
|
||||
- `get_version.request.schema.json` / `get_version.response.schema.json`
|
||||
- `pushall.request.schema.json` / `pushall.response.schema.json`
|
||||
- `request.schema.json` / `report.schema.json` — the top-level `{"info": ...}` / `{"pushing": ...}` dispatch, matching `BambuRequest`/`BambuReport`
|
||||
|
||||
## Validating something
|
||||
|
||||
```python
|
||||
from referencing import Registry, Resource
|
||||
from jsonschema import Draft202012Validator
|
||||
import json, glob, os
|
||||
|
||||
resources = []
|
||||
for path in glob.glob("*.schema.json"):
|
||||
contents = json.load(open(path))
|
||||
resources.append((contents["$id"], Resource.from_contents(contents)))
|
||||
resources.append((os.path.basename(path), Resource.from_contents(contents)))
|
||||
registry = Registry().with_resources(resources)
|
||||
|
||||
schema = json.load(open("request.schema.json"))
|
||||
Draft202012Validator(schema, registry=registry).validate({"info": {"sequence_id": "0", "command": "get_version"}})
|
||||
```
|
||||
Reference in New Issue
Block a user