config: rework get method

This commit is contained in:
axolotle 2023-04-18 16:11:14 +02:00
parent 80dbd6dac4
commit a92e22b653
3 changed files with 62 additions and 86 deletions

View file

@ -652,22 +652,9 @@ class DomainConfigPanel(ConfigPanel):
save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml" save_path_tpl = f"{DOMAIN_SETTINGS_DIR}/{{entity}}.yml"
save_mode = "diff" save_mode = "diff"
def get(self, key="", mode="classic"): # i18n: domain_config_cert_renew_help
result = super().get(key=key, mode=mode) # i18n: domain_config_default_app_help
# i18n: domain_config_xmpp_help
if mode == "full":
for panel, section, option in self._iterate():
# This injects:
# i18n: domain_config_cert_renew_help
# i18n: domain_config_default_app_help
# i18n: domain_config_xmpp_help
if m18n.key_exists(self.config["i18n"] + "_" + option["id"] + "_help"):
option["help"] = m18n.n(
self.config["i18n"] + "_" + option["id"] + "_help"
)
return self.config
return result
def _get_raw_config(self) -> "RawConfig": def _get_raw_config(self) -> "RawConfig":
# TODO add mechanism to share some settings with other domains on the same zone # TODO add mechanism to share some settings with other domains on the same zone

View file

@ -19,7 +19,7 @@
import os import os
import subprocess import subprocess
from logging import getLogger from logging import getLogger
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Any, Union
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
@ -31,7 +31,12 @@ from yunohost.log import is_unit_operation
from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings
if TYPE_CHECKING: if TYPE_CHECKING:
from yunohost.utils.configpanel import ConfigPanelModel, RawConfig, RawSettings from yunohost.utils.configpanel import (
ConfigPanelGetMode,
ConfigPanelModel,
RawConfig,
RawSettings,
)
logger = getLogger("yunohost.settings") logger = getLogger("yunohost.settings")
@ -129,17 +134,11 @@ class SettingsConfigPanel(ConfigPanel):
def __init__(self, config_path=None, save_path=None, creation=False): def __init__(self, config_path=None, save_path=None, creation=False):
super().__init__("settings") super().__init__("settings")
def get(self, key="", mode="classic"): def get(
self, key: Union[str, None] = None, mode: "ConfigPanelGetMode" = "classic"
) -> Any:
result = super().get(key=key, mode=mode) result = super().get(key=key, mode=mode)
if mode == "full":
for panel, section, option in self._iterate():
if m18n.key_exists(self.config["i18n"] + "_" + option["id"] + "_help"):
option["help"] = m18n.n(
self.config["i18n"] + "_" + option["id"] + "_help"
)
return self.config
# Dirty hack to let settings_get() to work from a python script # Dirty hack to let settings_get() to work from a python script
if isinstance(result, str) and result in ["True", "False"]: if isinstance(result, str) and result in ["True", "False"]:
result = bool(result == "True") result = bool(result == "True")

View file

@ -31,6 +31,7 @@ from moulinette.interfaces.cli import colorize
from moulinette.utils.filesystem import mkdir, read_toml, read_yaml, write_to_yaml from moulinette.utils.filesystem import mkdir, read_toml, read_yaml, write_to_yaml
from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.utils.error import YunohostError, YunohostValidationError
from yunohost.utils.form import ( from yunohost.utils.form import (
AnyOption,
BaseInputOption, BaseInputOption,
BaseOption, BaseOption,
BaseReadonlyOption, BaseReadonlyOption,
@ -190,6 +191,13 @@ class ConfigPanelModel(BaseModel):
for option in section.options: for option in section.options:
yield option yield option
def get_option(self, option_id) -> Union[AnyOption, None]:
for option in self.options:
if option.id == option_id:
return option
# FIXME raise error?
return None
def iter_children( def iter_children(
self, self,
@ -236,6 +244,7 @@ if TYPE_CHECKING:
FilterKey = Sequence[Union[str, None]] FilterKey = Sequence[Union[str, None]]
RawConfig = OrderedDict[str, Any] RawConfig = OrderedDict[str, Any]
RawSettings = dict[str, Any] RawSettings = dict[str, Any]
ConfigPanelGetMode = Literal["classic", "full", "export"]
def parse_filter_key(key: Union[str, None] = None) -> "FilterKey": def parse_filter_key(key: Union[str, None] = None) -> "FilterKey":
@ -310,78 +319,59 @@ class ConfigPanel:
and re.match("^(validate|post_ask)__", func) and re.match("^(validate|post_ask)__", func)
} }
def get(self, key="", mode="classic"): def get(
self.filter_key = key or "" self, key: Union[str, None] = None, mode: "ConfigPanelGetMode" = "classic"
) -> Any:
self.filter_key = parse_filter_key(key)
self.config, self.form = self._get_config_panel(prevalidate=False)
# Read config panel toml panel_id, section_id, option_id = self.filter_key
self._get_config_panel()
if not self.config:
raise YunohostValidationError("config_no_panel")
# Read or get values and hydrate the config
self._get_raw_settings()
self._hydrate()
# In 'classic' mode, we display the current value if key refer to an option # In 'classic' mode, we display the current value if key refer to an option
if self.filter_key.count(".") == 2 and mode == "classic": if option_id and mode == "classic":
option = self.filter_key.split(".")[-1] option = self.config.get_option(option_id)
value = self.values.get(option, None)
option_type = None if option is None:
for _, _, option_ in self._iterate(): # FIXME i18n
if option_["id"] == option: raise YunohostValidationError(
option_type = OPTIONS[option_["type"]] f"Couldn't find any option with id {option_id}"
break )
return option_type.normalize(value) if option_type else value if isinstance(option, BaseReadonlyOption):
return None
return self.form[option_id]
# Format result in 'classic' or 'export' mode # Format result in 'classic' or 'export' mode
self.config.translate()
logger.debug(f"Formating result in '{mode}' mode") logger.debug(f"Formating result in '{mode}' mode")
result = {} result = OrderedDict()
for panel, section, option in self._iterate(): for panel in self.config.panels:
if section["is_action_section"] and mode != "full": for section in panel.sections:
continue if section.is_action_section and mode != "full":
continue
key = f"{panel['id']}.{section['id']}.{option['id']}" for option in section.options:
if mode == "export": if mode == "export":
result[option["id"]] = option.get("current_value") if isinstance(option, BaseInputOption):
continue result[option.id] = self.form[option.id]
continue
ask = None if mode == "classic":
if "ask" in option: key = f"{panel.id}.{section.id}.{option.id}"
ask = _value_for_locale(option["ask"]) result[key] = {"ask": option.ask}
elif "i18n" in self.config:
ask = m18n.n(self.config["i18n"] + "_" + option["id"])
if mode == "full": if isinstance(option, BaseInputOption):
option["ask"] = ask result[key]["value"] = option.humanize(
question_class = OPTIONS[option.get("type", OptionType.string)] self.form[option.id], option
# FIXME : maybe other properties should be taken from the question, not just choices ?. )
if issubclass(question_class, BaseChoicesOption): if option.type is OptionType.password:
option["choices"] = question_class(option).choices result[key][
if issubclass(question_class, BaseInputOption): "value"
option["default"] = question_class(option).default ] = "**************" # Prevent displaying password in `config get`
option["pattern"] = question_class(option).pattern
else:
result[key] = {"ask": ask}
if "current_value" in option:
question_class = OPTIONS[option.get("type", OptionType.string)]
if hasattr(question_class, "humanize"):
result[key]["value"] = question_class.humanize(
option["current_value"], option
)
else:
result[key]["value"] = option["current_value"]
# FIXME: semantics, technically here this is not about a prompt...
if getattr(question_class, "hide_user_input_in_prompt", None):
result[key][
"value"
] = "**************" # Prevent displaying password in `config get`
if mode == "full": if mode == "full":
return self.config return self.config.dict(exclude_none=True)
else: else:
return result return result