fix(web): plugin settings form shows schema defaults for unsaved keys (#597)

The server-rendered plugin settings partial rendered straight from the
saved config, so an option added in a plugin update (geochron 1.2.0's
show_date / show_date_line, default true) drew as an unchecked box, and
the save route's missing-checkbox handling then stored it as false.
Enum dropdowns likewise showed their first option instead of the default.

- _load_plugin_config_partial runs the stored section through
  prepare_plugin_config (as GET /plugins/config does) before masking
  secrets, so a secret's schema default is masked too.
- render_field falls back to the field's own default, covering children
  of objects that declare a default of their own (where the defaults
  extraction stops).
- The legacy-boolean parity test now compares against the config the
  plugin actually runs with (defaults included).

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-18 11:09:25 -04:00
committed by GitHub
co-authored by Claude Opus 5
parent 92f1960d00
commit 19686ab761
5 changed files with 244 additions and 8 deletions
+16 -7
View File
@@ -30,6 +30,8 @@ from src.plugin_system.plugin_manager import PluginManager
from src.plugin_system.schema_manager import (
legacy_bool_as_object,
normalize_legacy_booleans,
plugin_config_defaults,
prepare_plugin_config,
)
@@ -361,26 +363,33 @@ def _form_checkboxes(stored):
return parser.checked
def _loader_enabled(normalized, *path):
node = normalized
def _loader_enabled(prepared, *path):
node = prepared
for key in path:
node = node.get(key) if isinstance(node, dict) else None
return isinstance(node, dict) and node.get("enabled") is True
if not isinstance(node, dict):
# A value the loader cannot read as the object (1, "true") fails
# validation; the form treats it as missing and draws the schema
# default, as it does for every other missing field.
return True
return node.get("enabled") is True
@pytest.mark.parametrize("value", [True, False, 1, "true", None])
def test_form_and_loader_agree_on_what_a_stored_value_means(value):
"""Where the loader reads a stored value as ``{"enabled": true}``, the form
draws the object's ``enabled`` checkbox ticked, and nowhere else."""
"""The form draws the object's ``enabled`` checkbox ticked exactly when the
plugin runs with it on -- legacy booleans read as objects, then schema
defaults filled in (prepare_plugin_config)."""
stored = {"enabled": True,
"global": {"dynamic_duration": value, "outer": {"inner": value}}}
normalized = normalize_legacy_booleans(stored, PARITY_SCHEMA)
prepared = prepare_plugin_config(stored, PARITY_SCHEMA,
plugin_config_defaults(PARITY_SCHEMA))
boxes = _form_checkboxes(stored)
for path in (("global", "dynamic_duration"), ("global", "outer", "inner")):
name = ".".join(path) + ".enabled"
assert name in boxes, f"form drew no {name} checkbox"
assert boxes[name] is _loader_enabled(normalized, *path), name
assert boxes[name] is _loader_enabled(prepared, *path), name
@pytest.mark.parametrize("value", [True, False])