|
| 1 | +# Writing JSON Schemas |
| 2 | + |
| 3 | +A JSON Schema is a JSON file which defines what a structure should |
| 4 | +look like; in our case we use it in our testsuite to check that they |
| 5 | +match command responses, and also use it to generate our |
| 6 | +documentation. |
| 7 | + |
| 8 | +Yes, schemas are horrible to write, but they're damn useful. We can |
| 9 | +only use a subset of the full [https://json-schema.org/](JSON Schema |
| 10 | +Specification), but if you find that limiting it's probably a sign |
| 11 | +that you should simplify your JSON output. |
| 12 | + |
| 13 | +## How to Write a Schema |
| 14 | + |
| 15 | +Name the schema doc/schemas/`command`.schema.json: the testsuite should |
| 16 | +pick it up and check all invocations of that command against it. |
| 17 | + |
| 18 | +I recommend copying an existing one to start. |
| 19 | + |
| 20 | +You will need to put the magic lines in the manual page so `make doc-all` |
| 21 | +will fill it in for you: |
| 22 | + |
| 23 | +``` |
| 24 | +[comment]: # (GENERATE-FROM-SCHEMA-START) |
| 25 | +[comment]: # (GENERATE-FROM-SCHEMA-END) |
| 26 | +``` |
| 27 | + |
| 28 | +If something goes wrong, try tools/fromscheme.py |
| 29 | +doc/schemas/`command`.schema.json to see how far it got before it died. |
| 30 | + |
| 31 | +You should always use `"additionalProperties": false`, otherwise |
| 32 | +your schema might not be covering everything. Deprecated fields |
| 33 | +simply have `"deprecated": true` in their properties, so they |
| 34 | +are allowed by omitted from the documentation. |
| 35 | + |
| 36 | +You should always list all fields which are *always* present in |
| 37 | +`"required"`. |
| 38 | + |
| 39 | +We extend the basic types; see |
| 40 | +[contrib/pyln-testing/pyln/testing/fixtures.py](fixtures.py). |
| 41 | + |
| 42 | + |
| 43 | +### Using Conditional Fields |
| 44 | + |
| 45 | +Sometimes one field is only sometimes present; if you can, you should make |
| 46 | +the schema know when it should (and should not!) be there. |
| 47 | + |
| 48 | +There are two kinds of conditional fields expressable: fields which |
| 49 | +are only present if another field is present, or fields only present |
| 50 | +if another field has certain values. |
| 51 | + |
| 52 | +To add conditional fields: |
| 53 | + |
| 54 | +1. Do *not* mention them in the main "properties" section. |
| 55 | +2. Set `"additionalProperties": true` for the main "properties" section. |
| 56 | +3. Add an `"allOf": [` array at the same height as `"properties"'`. Inside |
| 57 | + this place one `if`/`then` for each conditional field. |
| 58 | +4. If a field simply requires another field to be present, use the pattern |
| 59 | + `"required": [ "field" ]` inside the "if". |
| 60 | +5. If a field requires another field value, use the pattern |
| 61 | + `"properties": { "field": { "enum": [ "val1", "val2" ] } }` inside |
| 62 | + the "if". |
| 63 | +6. Inside the "then", use `"additionalProperties": false` and place |
| 64 | + empty `{}` for all the other possible properties. |
| 65 | +7. If you haven't covered all the possibilties with `if` statements, |
| 66 | + add an `else` with `"additionalProperties": false` which simply |
| 67 | + mentions every allowable property. This ensures that the fields |
| 68 | + can *only* be present when conditions are met. |
| 69 | + |
| 70 | +### JSON Drinking Game! |
| 71 | + |
| 72 | +1. Sip whenever you have an additional comma at the end of a sequence. |
| 73 | +2. Sip whenever you omit a comma in a sequence because you cut & paste. |
| 74 | +3. Skull whenever you wish JSON had comments. |
| 75 | + |
| 76 | +Good luck! |
| 77 | +Rusty. |
0 commit comments