Files
gmconfig/README.md
2026-05-14 11:32:17 +02:00

144 lines
4.2 KiB
Markdown

# Gmconfig - A JSON-SCHEMA-based configuration management support library
## Introduction
This library offers the basic support functions for the Gajumaru configuration
management subsystem. It is based on JSON-Schema, and includes, among other things:
* A reasonably complete JSON-Schema validator
* Validating user configurations against the schema
* In-service update of the user config
* Caching of the user config (and schema) as persistent terms
* Fast config lookups using key paths
* Lookups can handle both schema defaults and user-provided defaults
* Optional type coercion during validation
* Optional conversion of enums to atoms
* Optional extensions with custom validator funs
## JSON-Schema validator
The main thing the validator currently doesn't support is proper
management of complex sets of schemas and subSchemas. There are
some preparations made for supporting `"$id"` and uris, but this
is still incomplete. `"definitions"` and `"$ref"` properties are recognized.
As almost anything is theoretically possible with JSON-Schema,
there are surely other things that are unsupported.
### All standard data types
* `"null"`
* `"boolean"`
* `"integer"`
* `"number"`
* `"boolean"`
* `"string"`
* `"array"`
* `"object"`
### Dynamic properties
* `"if"`
* `"not"`
* `"oneOf"`
* `"anyOf"`
* `"allOf"`
### Static Properties
#### Array
* `"maxItems"`
* `"minItems"`
* `"uniqueItems"`
* `"prefixItems"`
* `"contains"` (`"minContains"`, `"maxContains"`)
#### Object
* `"properties"` (`"minProperties"`, `"maxProperties"`)
* `"patternProperties"`
* `"additionalProperties"`
* `"required"`
#### String
* `"pattern"`
* `"minLength"`
* `"maxLength"`
#### Number, Integer
* `"minimum"`
* `"maximum"`
* `"exclusiveMinimum"`
* `"exclusiveMinimum"`
* `"multipleOf"` (only if object is an integer)
### `"$updateSemantics"`
* `"replace"` (fully replaces any old data)
* `"merge"` (for objects, keeps and/or updates existing values)
* `"suggest"` (adds value if not already present)
### Custom validation options
Using `gmconfig_schema_utils:validate(Json, Schema, Opts)`, a few options are supported
to further enhance the validation (`Opts` is of type `map()`):
`coerce => boolean()` converts strings to integers, null and booleans when needed.
`enums_to_atoms => boolean()` converts enum strings to atoms
`extensions => map()` supports mapping `x-...` properties to custom validators.
#### Validator extensions
See the following test case:
```erlang
t_nested_refs() ->
S = read("data/nested_refs_schema.json"),
F = fun(Str, #{<<"tags">> := Tags}) ->
true = lists:any(
fun(T) ->
nomatch =/= string:prefix(Str, T)
end, Tags)
end,
Opts = #{extensions => #{<<"x-serialization">> => F}},
Vs = #{<<"tx">> => #{<<"from">> => <<"ak_good">>}},
Vf = #{<<"tx">> => #{<<"from">> => <<"ac_bad">>}},
validate(Vs, S, Opts),
fails(Vf, S, Opts, #{e => failing_schemas}),
ok.
```
This simulates an encoding extension, where the example fun here simply checks
if tags specified under the `x-serialization` property are prefixes of the
given string value.
In the test schema, we can see the following definition:
```json
"properties": {
"from": {
"allOf": [
{ "$ref": "#/components/schemas/Pubkey" },
{ "x-serialization": {
"tags": ["ak"]
}}
]
}
}
...
"Pubkey": {
"type": "string",
"x-serialization": {
"tags": ["ak", "ct"]
}
```
Whenever the validator encounters an `x-...` property mapped to a validator fun,
this fun is called with the value and the schema part of the property. The return
value of the fun is ignored, and any normal return is treated as a validation success.
The example illustrates a common pattern in OpenAPI specs, where entity references are
used extensively. The `Pubkey` data type can have a more general `x-serialization`
definition, where multiple key types are accepted, whereas a specialized use of the
type can narrow the scope by accepting only a subset of the possible types.