From 650c0136f2c31d71be4a51f6780639773e6216ba Mon Sep 17 00:00:00 2001 From: axolotle Date: Sun, 26 Nov 2023 18:29:43 +0100 Subject: [PATCH] form:UserOption: merge choices and default into root validator with first admin user as default --- src/tests/test_questions.py | 43 +++++++++---------------------------- src/utils/form.py | 38 +++++++++++++------------------- 2 files changed, 25 insertions(+), 56 deletions(-) diff --git a/src/tests/test_questions.py b/src/tests/test_questions.py index afd73c861..152046ec9 100644 --- a/src/tests/test_questions.py +++ b/src/tests/test_questions.py @@ -1449,6 +1449,7 @@ class TestSelect(BaseTest): # │ TAGS │ # ╰───────────────────────────────────────────────────────╯ + # [], ["one"], {} class TestTags(BaseTest): raw_option = {"type": "tags", "id": "tags_id"} @@ -1678,47 +1679,29 @@ class TestApp(BaseTest): admin_username = "admin_user" admin_user = { - "ssh_allowed": False, "username": admin_username, - "mailbox-quota": "0", "mail": "a@ynh.local", - "mail-aliases": [f"root@{main_domain}"], # Faking "admin" "fullname": "john doe", - "group": [], + "groups": ["admins"], } regular_username = "normal_user" regular_user = { - "ssh_allowed": False, "username": regular_username, - "mailbox-quota": "0", "mail": "z@ynh.local", "fullname": "john doe", - "group": [], + "groups": [], } @contextmanager -def patch_users( - *, - users, - admin_username, - main_domain, -): +def patch_users(*, users): """ Data mocking for UserOption: - yunohost.user.user_list - yunohost.user.user_info - yunohost.domain._get_maindomain """ - admin_info = next( - (user for user in users.values() if user["username"] == admin_username), - {"mail-aliases": []}, - ) - with patch.object(user, "user_list", return_value={"users": users}), patch.object( - user, - "user_info", - return_value=admin_info, # Faking admin user - ), patch.object(domain, "_get_maindomain", return_value=main_domain): + with patch.object(user, "user_list", return_value={"users": users}): yield @@ -1729,8 +1712,8 @@ class TestUser(BaseTest): # No tests for empty users since it should not happens { "data": [ - {"users": {admin_username: admin_user}, "admin_username": admin_username, "main_domain": main_domain}, - {"users": {admin_username: admin_user, regular_username: regular_user}, "admin_username": admin_username, "main_domain": main_domain}, + {"users": {admin_username: admin_user}}, + {"users": {admin_username: admin_user, regular_username: regular_user}}, ], "scenarios": [ # FIXME User option is not really nullable, even if optional @@ -1741,7 +1724,7 @@ class TestUser(BaseTest): }, { "data": [ - {"users": {admin_username: admin_user, regular_username: regular_user}, "admin_username": admin_username, "main_domain": main_domain}, + {"users": {admin_username: admin_user, regular_username: regular_user}}, ], "scenarios": [ *xpass(scenarios=[ @@ -1756,18 +1739,12 @@ class TestUser(BaseTest): @pytest.mark.usefixtures("patch_no_tty") def test_basic_attrs(self): - with patch_users( - users={admin_username: admin_user}, - admin_username=admin_username, - main_domain=main_domain, - ): + with patch_users(users={admin_username: admin_user}): self._test_basic_attrs() def test_options_prompted_with_ask_help(self, prefill_data=None): with patch_users( - users={admin_username: admin_user, regular_username: regular_user}, - admin_username=admin_username, - main_domain=main_domain, + users={admin_username: admin_user, regular_username: regular_user} ): super().test_options_prompted_with_ask_help( prefill_data={"raw_option": {}, "prefill": admin_username} diff --git a/src/utils/form.py b/src/utils/form.py index 97ae93de7..76349c07f 100644 --- a/src/utils/form.py +++ b/src/utils/form.py @@ -46,6 +46,7 @@ from pydantic import ( ValidationError, create_model, validator, + root_validator, ) from pydantic.color import Color from pydantic.fields import Field @@ -1705,43 +1706,34 @@ class UserOption(BaseChoicesOption): filter: Literal[None] = None choices: Union[dict[str, str], None] - @validator("choices", pre=True, always=True) - def inject_users_choices( - cls, value: Union[dict[str, str], None], values: Values - ) -> dict[str, str]: + @root_validator(pre=True) + def inject_users_choices_and_default(cls, values: Values) -> Values: # TODO remove calls to resources in validators (pydantic V2 should adress this) from yunohost.user import user_list - value = { + users = user_list(fields=["username", "fullname", "mail", "groups"])["users"] + + values["choices"] = { username: f"{infos['fullname']} ({infos['mail']})" - for username, infos in user_list()["users"].items() + for username, infos in users.items() } # FIXME keep this to test if any user, do not raise error if no admin? - if not value: + if not values["choices"]: raise YunohostValidationError( "app_argument_invalid", name=values["id"], error="You should create a YunoHost user first.", ) - return value + if not values.get("default"): + values["default"] = next( + username + for username, infos in users.items() + if "admins" in infos["groups"] + ) - @validator("default", pre=True, always=True) - 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_list, user_info - - if value is None: - # FIXME : in the past we looked for the user holding the "root@" alias - # but it's now obsolete... - # Should be replaced by something like "any first user we find in the admin group" - pass - - return value + return values class GroupOption(BaseChoicesOption):