Close the printer command variants
Found by a test of mine that asserted the union was closed and then failed: TypeBox objects permit unknown properties by default, so a `start_print` carrying an `ams_mapping` validated and the field was silently dropped. Every command variant is now `additionalProperties: false`, and `printer_telemetry` deliberately stays open. The asymmetry follows from who writes each message. Telemetry comes from gateways in the field and is read liberally: an unknown field must not cost a whole farm's batch, and a gateway newer than the backend should still be understood. A command is written by the backend, is four fields, and moves a physical machine - there an unrecognised field is a bug on the sending side, and dropping it silently is the worst outcome available, because a caller that misspells a field watches its command succeed while the machine does something else. `ams_mapping` is the case that matters: it is deliberately absent from `start_print` because the gateway reads the mapping out of the project archive, and a caller passing one has misunderstood something that previously cost a heated nozzle and a self-paused print. It is now told so.
This commit is contained in:
@@ -74,6 +74,23 @@ export type PrinterTelemetry = Static<typeof PrinterTelemetrySchema>;
|
||||
* JSON Schema for why that variant is absent until the gateway's
|
||||
* start-an-uploaded-file path has been run against real hardware by hand.
|
||||
*/
|
||||
/**
|
||||
* Every variant is closed (`additionalProperties: false`), unlike
|
||||
* `PrinterTelemetrySchema` which is deliberately open. The asymmetry is the
|
||||
* point, and it follows from who writes each message.
|
||||
*
|
||||
* Telemetry is written by gateways in the field, so it is read liberally: an
|
||||
* unknown field must not cost a whole farm's batch, and a gateway that is
|
||||
* newer than the backend should still be understood.
|
||||
*
|
||||
* Commands are written by this backend, are a handful of fields, and move
|
||||
* physical machines. Here an unrecognised field is a bug on the sending side,
|
||||
* and silently dropping it is the worst available outcome - a caller that
|
||||
* misspells a field would watch its command succeed and do something other
|
||||
* than what it asked for. `ams_mapping` is the concrete case: it is
|
||||
* deliberately not part of `start_print`, and a caller that passes one must
|
||||
* be told, not ignored.
|
||||
*/
|
||||
export const PrinterCommandSchema = Type.Union([
|
||||
Type.Object({
|
||||
action: Type.Literal("start_print"),
|
||||
@@ -98,7 +115,7 @@ export const PrinterCommandSchema = Type.Union([
|
||||
* may wait days and the URL would be long expired.
|
||||
*/
|
||||
url: Type.String({ format: "uri" }),
|
||||
}),
|
||||
}, { additionalProperties: false }),
|
||||
Type.Object({
|
||||
action: Type.Literal("set_fan_speed"),
|
||||
/**
|
||||
@@ -107,7 +124,7 @@ export const PrinterCommandSchema = Type.Union([
|
||||
* is set here does not read back identically.
|
||||
*/
|
||||
percent: Type.Integer({ minimum: 0, maximum: 100 }),
|
||||
}),
|
||||
}, { additionalProperties: false }),
|
||||
Type.Object({
|
||||
action: Type.Literal("control_print"),
|
||||
/**
|
||||
@@ -124,12 +141,12 @@ export const PrinterCommandSchema = Type.Union([
|
||||
Type.Literal("resume"),
|
||||
Type.Literal("stop"),
|
||||
]),
|
||||
}),
|
||||
}, { additionalProperties: false }),
|
||||
Type.Object({
|
||||
action: Type.Literal("send_gcode_line"),
|
||||
/** The most powerful variant by far; be careful about exposing it to a UI. */
|
||||
line: Type.String(),
|
||||
}),
|
||||
}, { additionalProperties: false }),
|
||||
]);
|
||||
export type PrinterCommand = Static<typeof PrinterCommandSchema>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user