form: rework entities validators to avoid multiple calls to them

This commit is contained in:
axolotle 2023-04-18 15:25:40 +02:00
parent 02948ad49c
commit 80dbd6dac4

View file

@ -44,7 +44,6 @@ from pydantic import (
Extra, Extra,
ValidationError, ValidationError,
create_model, create_model,
root_validator,
validator, validator,
) )
from pydantic.color import Color from pydantic.color import Color
@ -324,7 +323,7 @@ class BaseOption(BaseModel):
return value return value
# FIXME Legacy, is `name` still needed? # FIXME Legacy, is `name` still needed?
@validator("name", pre=True, always=True) @validator("name")
def apply_legacy_name(cls, value: Union[str, None], values: Values) -> str: def apply_legacy_name(cls, value: Union[str, None], values: Values) -> str:
if value is None: if value is None:
return values["id"] return values["id"]
@ -1096,21 +1095,30 @@ class DomainOption(BaseChoicesOption):
type: Literal[OptionType.domain] = OptionType.domain type: Literal[OptionType.domain] = OptionType.domain
choices: Union[dict[str, str], None] choices: Union[dict[str, str], None]
@root_validator() @validator("choices", pre=True, always=True)
def inject_domains_choices_and_default(cls, values: Values) -> Values: def inject_domains_choices(
cls, value: Union[dict[str, str], None], values: Values
) -> dict[str, str]:
# TODO remove calls to resources in validators (pydantic V2 should adress this) # TODO remove calls to resources in validators (pydantic V2 should adress this)
from yunohost.domain import domain_list from yunohost.domain import domain_list
data = domain_list() data = domain_list()
values["choices"] = { return {
domain: domain + "" if domain == data["main"] else domain domain: domain + "" if domain == data["main"] else domain
for domain in data["domains"] for domain in data["domains"]
} }
if values["default"] is None: @validator("default")
values["default"] = data["main"] def inject_default(
cls, value: Union[str, None], values: Values
) -> Union[str, None]:
# TODO remove calls to resources in validators (pydantic V2 should adress this)
from yunohost.domain import _get_maindomain
return values if value is None:
return _get_maindomain()
return value
@staticmethod @staticmethod
def normalize(value, option={}): def normalize(value, option={}):
@ -1131,8 +1139,11 @@ class AppOption(BaseChoicesOption):
add_yunohost_portal_to_choices: bool = False add_yunohost_portal_to_choices: bool = False
filter: Union[str, None] = None filter: Union[str, None] = None
@root_validator() @validator("choices", pre=True, always=True)
def inject_apps_choices(cls, values: Values) -> Values: def inject_apps_choices(
cls, value: Union[dict[str, str], None], values: Values
) -> dict[str, str]:
# TODO remove calls to resources in validators (pydantic V2 should adress this)
from yunohost.app import app_list from yunohost.app import app_list
apps = app_list(full=True)["apps"] apps = app_list(full=True)["apps"]
@ -1143,61 +1154,77 @@ class AppOption(BaseChoicesOption):
for app in apps for app in apps
if evaluate_simple_js_expression(values["filter"], context=app) if evaluate_simple_js_expression(values["filter"], context=app)
] ]
values["choices"] = {"_none": "---"}
value = {"_none": "---"}
if values.get("add_yunohost_portal_to_choices", False): if values.get("add_yunohost_portal_to_choices", False):
values["choices"]["_yunohost_portal_with_public_apps"] = "YunoHost's portal with public apps" value["_yunohost_portal_with_public_apps"] = "YunoHost's portal with public apps"
values["choices"].update( value.update(
{ {
app["id"]: f"{app['label']} ({app.get('domain_path', app['id'])})" app["id"]: f"{app['label']} ({app.get('domain_path', app['id'])})"
for app in apps for app in apps
} }
) )
return values return value
class UserOption(BaseChoicesOption): class UserOption(BaseChoicesOption):
type: Literal[OptionType.user] = OptionType.user type: Literal[OptionType.user] = OptionType.user
choices: Union[dict[str, str], None] choices: Union[dict[str, str], None]
@root_validator() @validator("choices", pre=True, always=True)
def inject_users_choices_and_default(cls, values: dict[str, Any]) -> dict[str, Any]: def inject_users_choices(
from yunohost.domain import _get_maindomain cls, value: Union[dict[str, str], None], values: Values
from yunohost.user import user_info, user_list ) -> dict[str, str]:
# TODO remove calls to resources in validators (pydantic V2 should adress this)
from yunohost.user import user_list
values["choices"] = { value = {
username: f"{infos['fullname']} ({infos['mail']})" username: f"{infos['fullname']} ({infos['mail']})"
for username, infos in user_list()["users"].items() for username, infos in user_list()["users"].items()
} }
# FIXME keep this to test if any user, do not raise error if no admin? # FIXME keep this to test if any user, do not raise error if no admin?
if not values["choices"]: if not value:
raise YunohostValidationError( raise YunohostValidationError(
"app_argument_invalid", "app_argument_invalid",
name=values["id"], name=values["id"],
error="You should create a YunoHost user first.", error="You should create a YunoHost user first.",
) )
if values["default"] is None: return value
@validator("default")
def inject_default(
cls, value: Union[str, None], values: Values
) -> Union[str, None]:
# TODO remove calls to resources in validators (pydantic V2 should adress this)
from yunohost.domain import _get_maindomain
from yunohost.user import user_info
if value is None:
# FIXME: this code is obsolete with the new admins group # FIXME: this code is obsolete with the new admins group
# Should be replaced by something like "any first user we find in the admin group" # Should be replaced by something like "any first user we find in the admin group"
root_mail = "root@%s" % _get_maindomain() root_mail = "root@%s" % _get_maindomain()
for user in values["choices"].keys(): for user in values["choices"].keys():
if root_mail in user_info(user).get("mail-aliases", []): if root_mail in user_info(user).get("mail-aliases", []):
values["default"] = user return user
break
return values return value
class GroupOption(BaseChoicesOption): class GroupOption(BaseChoicesOption):
type: Literal[OptionType.group] = OptionType.group type: Literal[OptionType.group] = OptionType.group
choices: Union[dict[str, str], None] choices: Union[dict[str, str], None]
default: Union[Literal["visitors", "all_users", "admins"], None] = "all_users"
@root_validator() @validator("choices", pre=True, always=True)
def inject_groups_choices_and_default(cls, values: Values) -> Values: def inject_groups_choices(
cls, value: Union[dict[str, str], None], values: Values
) -> dict[str, str]:
# TODO remove calls to resources in validators (pydantic V2 should adress this)
from yunohost.user import user_group_list from yunohost.user import user_group_list
groups = user_group_list(short=True, include_primary_groups=False)["groups"] groups = user_group_list(short=True, include_primary_groups=False)["groups"]
@ -1212,14 +1239,7 @@ class GroupOption(BaseChoicesOption):
else groupname else groupname
) )
values["choices"] = { return {groupname: _human_readable_group(groupname) for groupname in groups}
groupname: _human_readable_group(groupname) for groupname in groups
}
if values["default"] is None:
values["default"] = "all_users"
return values
OPTIONS = { OPTIONS = {
@ -1293,7 +1313,7 @@ class OptionsModel(BaseModel):
"id": id_, "id": id_,
"type": option.get("type", "string"), "type": option.get("type", "string"),
# ConfigPanel options needs to be set as optional by default # ConfigPanel options needs to be set as optional by default
"optional": option.get("optional", optional) "optional": option.get("optional", optional),
} }
for id_, option in options.items() for id_, option in options.items()
] ]