42eb0b62d5
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).
68 lines
2.8 KiB
Markdown
68 lines
2.8 KiB
Markdown
# 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"}})
|
|
```
|