doc: document how to try to write JSON schemas.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2021-06-16 10:34:17 +09:30
parent fe161ff962
commit a9f49b5326
2 changed files with 83 additions and 0 deletions

View File

@ -194,6 +194,12 @@ All `warning` fields should have unique names which start with
`warning_`, the value of which should be an explanation. This allows
for programs to deal with them sanely, and also perform translations.
### Documenting JSON APIs
We use JSON schemas to validate that JSON-RPC returns are in the
correct form, and also to generate documentation. See
[doc/schemas/WRITING_SCHEMAS.md](WRITING_SCHEMAS.md).
## Changing JSON APIs
All JSON API changes need a Changelog line (see below).

View File

@ -0,0 +1,77 @@
# Writing JSON Schemas
A JSON Schema is a JSON file which defines what a structure should
look like; in our case we use it in our testsuite to check that they
match command responses, and also use it to generate our
documentation.
Yes, schemas are horrible to write, but they're damn useful. We can
only use a subset of the full [https://json-schema.org/](JSON Schema
Specification), but if you find that limiting it's probably a sign
that you should simplify your JSON output.
## How to Write a Schema
Name the schema doc/schemas/`command`.schema.json: the testsuite should
pick it up and check all invocations of that command against it.
I recommend copying an existing one to start.
You will need to put the magic lines in the manual page so `make doc-all`
will fill it in for you:
```
[comment]: # (GENERATE-FROM-SCHEMA-START)
[comment]: # (GENERATE-FROM-SCHEMA-END)
```
If something goes wrong, try tools/fromscheme.py
doc/schemas/`command`.schema.json to see how far it got before it died.
You should always use `"additionalProperties": false`, otherwise
your schema might not be covering everything. Deprecated fields
simply have `"deprecated": true` in their properties, so they
are allowed by omitted from the documentation.
You should always list all fields which are *always* present in
`"required"`.
We extend the basic types; see
[contrib/pyln-testing/pyln/testing/fixtures.py](fixtures.py).
### Using Conditional Fields
Sometimes one field is only sometimes present; if you can, you should make
the schema know when it should (and should not!) be there.
There are two kinds of conditional fields expressable: fields which
are only present if another field is present, or fields only present
if another field has certain values.
To add conditional fields:
1. Do *not* mention them in the main "properties" section.
2. Set `"additionalProperties": true` for the main "properties" section.
3. Add an `"allOf": [` array at the same height as `"properties"'`. Inside
this place one `if`/`then` for each conditional field.
4. If a field simply requires another field to be present, use the pattern
`"required": [ "field" ]` inside the "if".
5. If a field requires another field value, use the pattern
`"properties": { "field": { "enum": [ "val1", "val2" ] } }` inside
the "if".
6. Inside the "then", use `"additionalProperties": false` and place
empty `{}` for all the other possible properties.
7. If you haven't covered all the possibilties with `if` statements,
add an `else` with `"additionalProperties": false` which simply
mentions every allowable property. This ensures that the fields
can *only* be present when conditions are met.
### JSON Drinking Game!
1. Sip whenever you have an additional comma at the end of a sequence.
2. Sip whenever you omit a comma in a sequence because you cut & paste.
3. Skull whenever you wish JSON had comments.
Good luck!
Rusty.