Schema and types
The pydantic models in reactor.ui.wire are the definition of the wire. The
other two forms follow from them, and a test on each tier fails when one moves
without the others.
The JSON Schema
reactor ui schema # print it
reactor ui schema --out schema/reactor-ui.v1.schema.json # write the committed copy
The schema is JSON Schema 2020-12, with the $id
https://reactor.datalayer.tech/schema/reactor-ui.v1.schema.json. Its root
is UiOp — one op of the envelope, a oneOf with op as the discriminator —
and its $defs hold every op, every patch operation, DisplayWire,
ActionOffer, FileRef and UiAction, the message going the other way.
From Python:
from reactor.ui.schema import render_schema, ui_wire_schema, write_schema
ui_wire_schema()["$defs"]["UiAction"]["required"] # ['action', 'display_id', 'token']
tests/test_ui_schema.py regenerates the schema and compares it with the
committed file: a model changed without regenerating fails there.
The TypeScript types
@datalayer/reactor/ui declares the same types by hand (Display, UiOp,
PatchOperation, UiAction, …), and UI_WIRE_FIELDS lists every property of
every type by its schema name — the compiler checks each name is a key of its
type. src/ui/__tests__/wire.test.ts reads the committed schema and checks:
UI_WIRE_FIELDSnames every object definition of the schema, and only those;- each definition's properties are exactly the schema's;
UI_OP_NAMESis exactly what the discriminator maps.
So a field added in Python is refused twice until it is in the schema and in the TypeScript types.
A change to the wire
- Change the models in
reactor/ui/wire.py. reactor ui schema --out schema/reactor-ui.v1.schema.json.- Follow it in
src/ui/wire.ts— the type,UI_WIRE_FIELDS, and the guards (isDisplay,isPatchOperation,isUiOp) when the shape of an op changes.
A change a page of the current version cannot read is a new wire version:
UI_WIRE_VERSION and the schema's file name move together, and the reducer
refuses an op of another version.
Display types
Each display type has a schema of its own, from its pydantic class:
platform.display_types().schemas()
# {'application/vnd.example.approval+json@1': {'properties': {...}, 'title': 'Approval', ...}}
A host serves it for whatever needs to know a type's data — a renderer's
author, a form generator, a model told what a display may contain. The
renderers on the page read display.data as their plugin knows it; nothing
generates TypeScript per display type.