mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge pull request #989 from YunoHost/test-for-args-parsing
Test for args parsing
This commit is contained in:
commit
6a75716fb5
2 changed files with 184 additions and 67 deletions
|
@ -2385,126 +2385,134 @@ def _parse_args_for_action(action, args={}):
|
||||||
return _parse_args_in_yunohost_format(args, action_args)
|
return _parse_args_in_yunohost_format(args, action_args)
|
||||||
|
|
||||||
|
|
||||||
def _parse_args_in_yunohost_format(args, action_args):
|
def _parse_args_in_yunohost_format(user_answers, argument_questions):
|
||||||
"""Parse arguments store in either manifest.json or actions.json
|
"""Parse arguments store in either manifest.json or actions.json or from a
|
||||||
|
config panel against the user answers when they are present.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
user_answers -- a dictionnary of arguments from the user (generally
|
||||||
|
empty in CLI, filed from the admin interface)
|
||||||
|
argument_questions -- the arguments description store in yunohost
|
||||||
|
format from actions.json/toml, manifest.json/toml
|
||||||
|
or config_panel.json/toml
|
||||||
"""
|
"""
|
||||||
from yunohost.domain import domain_list, _get_maindomain
|
from yunohost.domain import domain_list, _get_maindomain
|
||||||
from yunohost.user import user_list
|
from yunohost.user import user_list
|
||||||
|
|
||||||
args_dict = OrderedDict()
|
parsed_answers_dict = OrderedDict()
|
||||||
|
|
||||||
for arg in action_args:
|
for question in argument_questions:
|
||||||
arg_name = arg['name']
|
question_name = question['name']
|
||||||
arg_type = arg.get('type', 'string')
|
question_type = question.get('type', 'string')
|
||||||
arg_default = arg.get('default', None)
|
question_default = question.get('default', None)
|
||||||
arg_choices = arg.get('choices', [])
|
question_choices = question.get('choices', [])
|
||||||
arg_value = None
|
question_value = None
|
||||||
|
|
||||||
# Transpose default value for boolean type and set it to
|
# Transpose default value for boolean type and set it to
|
||||||
# false if not defined.
|
# false if not defined.
|
||||||
if arg_type == 'boolean':
|
if question_type == 'boolean':
|
||||||
arg_default = 1 if arg_default else 0
|
question_default = 1 if question_default else 0
|
||||||
|
|
||||||
# do not print for webadmin
|
# do not print for webadmin
|
||||||
if arg_type == 'display_text' and msettings.get('interface') != 'api':
|
if question_type == 'display_text' and msettings.get('interface') != 'api':
|
||||||
print(_value_for_locale(arg['ask']))
|
print(_value_for_locale(question['ask']))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Attempt to retrieve argument value
|
# Attempt to retrieve argument value
|
||||||
if arg_name in args:
|
if question_name in user_answers:
|
||||||
arg_value = args[arg_name]
|
question_value = user_answers[question_name]
|
||||||
else:
|
else:
|
||||||
if 'ask' in arg:
|
if 'ask' in question:
|
||||||
# Retrieve proper ask string
|
# Retrieve proper ask string
|
||||||
ask_string = _value_for_locale(arg['ask'])
|
text_for_user_input_in_cli = _value_for_locale(question['ask'])
|
||||||
|
|
||||||
# Append extra strings
|
# Append extra strings
|
||||||
if arg_type == 'boolean':
|
if question_type == 'boolean':
|
||||||
ask_string += ' [yes | no]'
|
text_for_user_input_in_cli += ' [yes | no]'
|
||||||
elif arg_choices:
|
elif question_choices:
|
||||||
ask_string += ' [{0}]'.format(' | '.join(arg_choices))
|
text_for_user_input_in_cli += ' [{0}]'.format(' | '.join(question_choices))
|
||||||
|
|
||||||
if arg_default is not None:
|
if question_default is not None:
|
||||||
if arg_type == 'boolean':
|
if question_type == 'boolean':
|
||||||
ask_string += ' (default: {0})'.format("yes" if arg_default == 1 else "no")
|
text_for_user_input_in_cli += ' (default: {0})'.format("yes" if question_default == 1 else "no")
|
||||||
else:
|
else:
|
||||||
ask_string += ' (default: {0})'.format(arg_default)
|
text_for_user_input_in_cli += ' (default: {0})'.format(question_default)
|
||||||
|
|
||||||
# Check for a password argument
|
# Check for a password argument
|
||||||
is_password = True if arg_type == 'password' else False
|
is_password = True if question_type == 'password' else False
|
||||||
|
|
||||||
if arg_type == 'domain':
|
if question_type == 'domain':
|
||||||
arg_default = _get_maindomain()
|
question_default = _get_maindomain()
|
||||||
ask_string += ' (default: {0})'.format(arg_default)
|
text_for_user_input_in_cli += ' (default: {0})'.format(question_default)
|
||||||
msignals.display(m18n.n('domains_available'))
|
msignals.display(m18n.n('domains_available'))
|
||||||
for domain in domain_list()['domains']:
|
for domain in domain_list()['domains']:
|
||||||
msignals.display("- {}".format(domain))
|
msignals.display("- {}".format(domain))
|
||||||
|
|
||||||
elif arg_type == 'user':
|
elif question_type == 'user':
|
||||||
msignals.display(m18n.n('users_available'))
|
msignals.display(m18n.n('users_available'))
|
||||||
for user in user_list()['users'].keys():
|
for user in user_list()['users'].keys():
|
||||||
msignals.display("- {}".format(user))
|
msignals.display("- {}".format(user))
|
||||||
|
|
||||||
elif arg_type == 'password':
|
elif question_type == 'password':
|
||||||
msignals.display(m18n.n('good_practices_about_user_password'))
|
msignals.display(m18n.n('good_practices_about_user_password'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
input_string = msignals.prompt(ask_string, is_password)
|
input_string = msignals.prompt(text_for_user_input_in_cli, is_password)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
input_string = None
|
input_string = None
|
||||||
if (input_string == '' or input_string is None) \
|
if (input_string == '' or input_string is None) \
|
||||||
and arg_default is not None:
|
and question_default is not None:
|
||||||
arg_value = arg_default
|
question_value = question_default
|
||||||
else:
|
else:
|
||||||
arg_value = input_string
|
question_value = input_string
|
||||||
elif arg_default is not None:
|
elif question_default is not None:
|
||||||
arg_value = arg_default
|
question_value = question_default
|
||||||
|
|
||||||
# If the value is empty (none or '')
|
# If the value is empty (none or '')
|
||||||
# then check if arg is optional or not
|
# then check if question is optional or not
|
||||||
if arg_value is None or arg_value == '':
|
if question_value is None or question_value == '':
|
||||||
if arg.get("optional", False):
|
if question.get("optional", False):
|
||||||
# Argument is optional, keep an empty value
|
# Argument is optional, keep an empty value
|
||||||
# and that's all for this arg !
|
# and that's all for this question!
|
||||||
args_dict[arg_name] = ('', arg_type)
|
parsed_answers_dict[question_name] = ('', question_type)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# The argument is required !
|
# The argument is required !
|
||||||
raise YunohostError('app_argument_required', name=arg_name)
|
raise YunohostError('app_argument_required', name=question_name)
|
||||||
|
|
||||||
# Validate argument choice
|
# Validate argument choice
|
||||||
if arg_choices and arg_value not in arg_choices:
|
if question_choices and question_value not in question_choices:
|
||||||
raise YunohostError('app_argument_choice_invalid', name=arg_name, choices=', '.join(arg_choices))
|
raise YunohostError('app_argument_choice_invalid', name=question_name, choices=', '.join(question_choices))
|
||||||
|
|
||||||
# Validate argument type
|
# Validate argument type
|
||||||
if arg_type == 'domain':
|
if question_type == 'domain':
|
||||||
if arg_value not in domain_list()['domains']:
|
if question_value not in domain_list()['domains']:
|
||||||
raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown'))
|
raise YunohostError('app_argument_invalid', name=question_name, error=m18n.n('domain_unknown'))
|
||||||
elif arg_type == 'user':
|
elif question_type == 'user':
|
||||||
if not arg_value in user_list()["users"].keys():
|
if question_value not in user_list()["users"].keys():
|
||||||
raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('user_unknown', user=arg_value))
|
raise YunohostError('app_argument_invalid', name=question_name, error=m18n.n('user_unknown', user=question_value))
|
||||||
elif arg_type == 'app':
|
elif question_type == 'app':
|
||||||
if not _is_installed(arg_value):
|
if not _is_installed(question_value):
|
||||||
raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('app_unknown'))
|
raise YunohostError('app_argument_invalid', name=question_name, error=m18n.n('app_unknown'))
|
||||||
elif arg_type == 'boolean':
|
elif question_type == 'boolean':
|
||||||
if isinstance(arg_value, bool):
|
if isinstance(question_value, bool):
|
||||||
arg_value = 1 if arg_value else 0
|
question_value = 1 if question_value else 0
|
||||||
else:
|
else:
|
||||||
if str(arg_value).lower() in ["1", "yes", "y"]:
|
if str(question_value).lower() in ["1", "yes", "y"]:
|
||||||
arg_value = 1
|
question_value = 1
|
||||||
elif str(arg_value).lower() in ["0", "no", "n"]:
|
elif str(question_value).lower() in ["0", "no", "n"]:
|
||||||
arg_value = 0
|
question_value = 0
|
||||||
else:
|
else:
|
||||||
raise YunohostError('app_argument_choice_invalid', name=arg_name, choices='yes, no, y, n, 1, 0')
|
raise YunohostError('app_argument_choice_invalid', name=question_name, choices='yes, no, y, n, 1, 0')
|
||||||
elif arg_type == 'password':
|
elif question_type == 'password':
|
||||||
forbidden_chars = "{}"
|
forbidden_chars = "{}"
|
||||||
if any(char in arg_value for char in forbidden_chars):
|
if any(char in question_value for char in forbidden_chars):
|
||||||
raise YunohostError('pattern_password_app', forbidden_chars=forbidden_chars)
|
raise YunohostError('pattern_password_app', forbidden_chars=forbidden_chars)
|
||||||
from yunohost.utils.password import assert_password_is_strong_enough
|
from yunohost.utils.password import assert_password_is_strong_enough
|
||||||
assert_password_is_strong_enough('user', arg_value)
|
assert_password_is_strong_enough('user', question_value)
|
||||||
args_dict[arg_name] = (arg_value, arg_type)
|
parsed_answers_dict[question_name] = (question_value, question_type)
|
||||||
|
|
||||||
return args_dict
|
return parsed_answers_dict
|
||||||
|
|
||||||
|
|
||||||
def _validate_and_normalize_webpath(manifest, args_dict, app_folder):
|
def _validate_and_normalize_webpath(manifest, args_dict, app_folder):
|
||||||
|
|
109
src/yunohost/tests/test_apps_arguments_parsing.py
Normal file
109
src/yunohost/tests/test_apps_arguments_parsing.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import pytest
|
||||||
|
from collections import OrderedDict
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
|
from moulinette import msignals
|
||||||
|
|
||||||
|
from yunohost.app import _parse_args_in_yunohost_format
|
||||||
|
from yunohost.utils.error import YunohostError
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Argument default format:
|
||||||
|
{
|
||||||
|
"name": "the_name",
|
||||||
|
"type": "one_of_the_available_type", // "sting" is not specified
|
||||||
|
"ask": {
|
||||||
|
"en": "the question in english",
|
||||||
|
"fr": "the question in french"
|
||||||
|
},
|
||||||
|
"help": {
|
||||||
|
"en": "some help text in english",
|
||||||
|
"fr": "some help text in french"
|
||||||
|
},
|
||||||
|
"example": "an example value", // optional
|
||||||
|
"default", "some stuff", // optional, not available for all types
|
||||||
|
"optional": true // optional, will skip if not answered
|
||||||
|
}
|
||||||
|
|
||||||
|
User answers:
|
||||||
|
{"name": "value", ...}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_in_yunohost_format_empty():
|
||||||
|
assert _parse_args_in_yunohost_format({}, []) == {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_in_yunohost_format_string():
|
||||||
|
questions = [{
|
||||||
|
"name": "some_string",
|
||||||
|
"type": "string",
|
||||||
|
}]
|
||||||
|
answers = {"some_string": "some_value"}
|
||||||
|
expected_result = OrderedDict({"some_string": ("some_value", "string")})
|
||||||
|
assert _parse_args_in_yunohost_format(answers, questions) == expected_result
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_in_yunohost_format_string_default_type():
|
||||||
|
questions = [{
|
||||||
|
"name": "some_string",
|
||||||
|
}]
|
||||||
|
answers = {"some_string": "some_value"}
|
||||||
|
expected_result = OrderedDict({"some_string": ("some_value", "string")})
|
||||||
|
assert _parse_args_in_yunohost_format(answers, questions) == expected_result
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_in_yunohost_format_string_no_input():
|
||||||
|
questions = [{
|
||||||
|
"name": "some_string",
|
||||||
|
}]
|
||||||
|
answers = {}
|
||||||
|
|
||||||
|
with pytest.raises(YunohostError):
|
||||||
|
_parse_args_in_yunohost_format(answers, questions)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_in_yunohost_format_string_input():
|
||||||
|
questions = [{
|
||||||
|
"name": "some_string",
|
||||||
|
"ask": "some question",
|
||||||
|
}]
|
||||||
|
answers = {}
|
||||||
|
expected_result = OrderedDict({"some_string": ("some_value", "string")})
|
||||||
|
|
||||||
|
with patch.object(msignals, "prompt", return_value="some_value"):
|
||||||
|
assert _parse_args_in_yunohost_format(answers, questions) == expected_result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip # that shit should work x(
|
||||||
|
def test_parse_args_in_yunohost_format_string_input_no_ask():
|
||||||
|
questions = [{
|
||||||
|
"name": "some_string",
|
||||||
|
}]
|
||||||
|
answers = {}
|
||||||
|
expected_result = OrderedDict({"some_string": ("some_value", "string")})
|
||||||
|
|
||||||
|
with patch.object(msignals, "prompt", return_value="some_value"):
|
||||||
|
assert _parse_args_in_yunohost_format(answers, questions) == expected_result
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_in_yunohost_format_string_no_input_optional():
|
||||||
|
questions = [{
|
||||||
|
"name": "some_string",
|
||||||
|
"optional": True,
|
||||||
|
}]
|
||||||
|
answers = {}
|
||||||
|
expected_result = OrderedDict({"some_string": ("", "string")})
|
||||||
|
assert _parse_args_in_yunohost_format(answers, questions) == expected_result
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_in_yunohost_format_string_no_input_default():
|
||||||
|
questions = [{
|
||||||
|
"name": "some_string",
|
||||||
|
"ask": "some question",
|
||||||
|
"default": "some_value",
|
||||||
|
}]
|
||||||
|
answers = {}
|
||||||
|
expected_result = OrderedDict({"some_string": ("some_value", "string")})
|
||||||
|
assert _parse_args_in_yunohost_format(answers, questions) == expected_result
|
Loading…
Add table
Reference in a new issue