lib/jsonschema: make attrs required
Before the change, modules of the form
```nix
{ lib, ... }: {
foo.bar = lib.mkOption {
# ...
};
}
```
or
```nix
{ lib, ... }: {
foo = lib.mkOption {
type = lib.types.subModule {
bar = lib.mkOption {
# ...
};
};
};
}
```
would not render with `foo` as required, which is not faithful to the
module system's semantics.
This change also tests that fields with defaults are not marked required.
Note that submodule options cannot have their defaults rendered to JSON
schema, and are therefore always marked required.
Architecturally this change is rather unfortunate: So far the checks for
defaults happen in the rendering (using `isDefault`) and not in the parsing,
but here we're adding a field to `$exportedModuleInfo`. While strictly
speaking we probably don't want to consider requiredness as module-level
information, it seems more reasonable to me to do it that way since at
the JSON schema level we have lost the distinction between `attrs`,
`attrsOf`, `submodule`.
This commit is contained in:
committed by
Valentin Gagarin
parent
46ddc9f438
commit
e28681c4f2
@@ -123,7 +123,7 @@ rec {
|
|||||||
# parse options to jsonschema properties
|
# parse options to jsonschema properties
|
||||||
properties = lib.mapAttrs (_name: option: (parseOption' (path ++ [ _name ]) option)) options';
|
properties = lib.mapAttrs (_name: option: (parseOption' (path ++ [ _name ]) option)) options';
|
||||||
# TODO: figure out how to handle if prop.anyOf is used
|
# TODO: figure out how to handle if prop.anyOf is used
|
||||||
isRequired = prop: !(prop ? default || prop.type or null == "object");
|
isRequired = prop: !(prop ? default || prop."$exportedModuleInfo".required or false);
|
||||||
requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties;
|
requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties;
|
||||||
required = lib.optionalAttrs (requiredProps != { }) { required = lib.attrNames requiredProps; };
|
required = lib.optionalAttrs (requiredProps != { }) { required = lib.attrNames requiredProps; };
|
||||||
header' = if addHeader then header else { };
|
header' = if addHeader then header else { };
|
||||||
@@ -150,9 +150,9 @@ rec {
|
|||||||
{ };
|
{ };
|
||||||
|
|
||||||
# Metadata about the module that is made available to the schema via '$propagatedModuleInfo'
|
# Metadata about the module that is made available to the schema via '$propagatedModuleInfo'
|
||||||
exportedModuleInfo = lib.optionalAttrs true (makeModuleInfo {
|
exportedModuleInfo = makeModuleInfo {
|
||||||
inherit path;
|
inherit path;
|
||||||
});
|
};
|
||||||
in
|
in
|
||||||
# return jsonschema
|
# return jsonschema
|
||||||
header'
|
header'
|
||||||
@@ -377,7 +377,13 @@ rec {
|
|||||||
# return jsonschema property definition for attrs
|
# return jsonschema property definition for attrs
|
||||||
then
|
then
|
||||||
default
|
default
|
||||||
// exposedModuleInfo
|
// (lib.recursiveUpdate exposedModuleInfo (
|
||||||
|
lib.optionalAttrs (!default ? default) {
|
||||||
|
"$exportedModuleInfo" = {
|
||||||
|
required = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
))
|
||||||
// example
|
// example
|
||||||
// description
|
// description
|
||||||
// {
|
// {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"$exportedModuleInfo": { "path": [] },
|
"$exportedModuleInfo": { "path": [] },
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
"required": [ "services" ],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"$exportedModuleInfo": { "path": ["name"] },
|
"$exportedModuleInfo": { "path": ["name"] },
|
||||||
|
|||||||
@@ -13,7 +13,17 @@
|
|||||||
testParseNestedOptions =
|
testParseNestedOptions =
|
||||||
let
|
let
|
||||||
evaled = lib.evalModules {
|
evaled = lib.evalModules {
|
||||||
modules = [ { options.foo.bar = lib.mkOption { type = lib.types.bool; }; } ];
|
modules = [
|
||||||
|
{
|
||||||
|
options.foo.bar = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
};
|
||||||
|
options.foo.baz = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -40,12 +50,23 @@
|
|||||||
};
|
};
|
||||||
type = "boolean";
|
type = "boolean";
|
||||||
};
|
};
|
||||||
|
baz = {
|
||||||
|
"$exportedModuleInfo" = {
|
||||||
|
path = [
|
||||||
|
"foo"
|
||||||
|
"baz"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
type = "boolean";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
required = [ "bar" ];
|
required = [ "bar" ];
|
||||||
type = "object";
|
type = "object";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
type = "object";
|
type = "object";
|
||||||
|
required = [ "foo" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user