From 9a2b6c902be22adf3cfc735119ccf99d3a4792f3 Mon Sep 17 00:00:00 2001 From: Devrim Date: Mon, 25 Dec 2023 15:48:28 +0300 Subject: [PATCH] feat(jans-cli-tui): message configuration (#7198) Signed-off-by: Mustafa Baser Signed-off-by: Yuriy Movchan --- jans-cli-tui/cli_tui/cli/config_cli.py | 3 + .../cli_tui/plugins/010_auth_server/main.py | 16 ++- .../plugins/010_auth_server/message.py | 128 ++++++++++++++++++ jans-cli-tui/cli_tui/utils/utils.py | 89 +++++++++++- 4 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 jans-cli-tui/cli_tui/plugins/010_auth_server/message.py diff --git a/jans-cli-tui/cli_tui/cli/config_cli.py b/jans-cli-tui/cli_tui/cli/config_cli.py index 76581443d7a..ad5a14d2098 100755 --- a/jans-cli-tui/cli_tui/cli/config_cli.py +++ b/jans-cli-tui/cli_tui/cli/config_cli.py @@ -991,6 +991,9 @@ def patch_requests(self, endpoint, url_param_dict, data): response = requests.patch(**patch_params) self.log_response(response) + if self.wrapped: + return response + try: return response.json() except: diff --git a/jans-cli-tui/cli_tui/plugins/010_auth_server/main.py b/jans-cli-tui/cli_tui/plugins/010_auth_server/main.py index 2fd50739015..70785c02b0e 100755 --- a/jans-cli-tui/cli_tui/plugins/010_auth_server/main.py +++ b/jans-cli-tui/cli_tui/plugins/010_auth_server/main.py @@ -42,6 +42,7 @@ from ssa import SSA from agama import Agama from authn import Authn +from message import Message from attributes import Attributes from prompt_toolkit.widgets import ( @@ -70,6 +71,7 @@ def __init__( self.ssa = SSA(app) self.agama = Agama(app) self.authn = Authn(app) + self.message = Message() self.attributes = Attributes(app) self.oauth_containers = {} @@ -245,6 +247,7 @@ def oauth_prepare_containers(self) -> None: self.oauth_containers['authn'] = self.authn.main_container self.oauth_containers['attributes'] = self.attributes.main_container self.oauth_containers['logging'] = DynamicContainer(lambda: self.oauth_data_container['logging']) + self.oauth_containers['message'] = self.message.main_container self.oauth_main_container = HSplit([ Box(self.nav_bar.nav_window, style='class:sub-navbar', height=1), @@ -259,7 +262,18 @@ def oauth_prepare_navbar(self) -> None: """ self.nav_bar = JansNavBar( self.app, - entries=[('clients', 'C[l]ients'), ('scopes', 'Sc[o]pes'), ('keys', '[K]eys'), ('authn', 'Au[t]hn'), ('properties', 'Properti[e]s'), ('logging', 'Lo[g]ging'), ('ssa', '[S]SA'), ('agama', 'Aga[m]a'), ('attributes', 'Attri[b]utes')], + entries=[ + ('clients', 'C[l]ients'), + ('scopes', 'Sc[o]pes'), + ('keys', '[K]eys'), + ('authn', 'Au[t]hn'), + ('properties', 'Properti[e]s'), + ('logging', 'Lo[g]ging'), + ('ssa', '[S]SA'), + ('agama', 'Aga[m]a'), + ('attributes', 'Attri[b]utes'), + ('message', 'Message') + ], selection_changed=self.oauth_nav_selection_changed, select=0, jans_name='oauth:nav_bar' diff --git a/jans-cli-tui/cli_tui/plugins/010_auth_server/message.py b/jans-cli-tui/cli_tui/plugins/010_auth_server/message.py new file mode 100644 index 00000000000..73f45c6e30d --- /dev/null +++ b/jans-cli-tui/cli_tui/plugins/010_auth_server/message.py @@ -0,0 +1,128 @@ +import asyncio + +from prompt_toolkit.layout.dimension import D +from prompt_toolkit.eventloop import get_event_loop +from prompt_toolkit.layout.containers import HSplit, VSplit, DynamicContainer, HorizontalAlign +from prompt_toolkit.widgets import Button, Frame, RadioList + +from utils.multi_lang import _ +from utils.utils import DialogUtils +from utils.static import cli_style, common_strings +from utils.utils import common_data + +class Message(DialogUtils): + def __init__(self) -> None: + + self.first_enter = False + self.data = {} + self.message_provider_container = VSplit([]) + self.working_container = VSplit([]) + self.main_container = DynamicContainer(lambda: self.working_container) + + self.main_container.on_page_enter = self.on_page_enter + + def on_page_enter(self) -> None: + + if not self.first_enter: + self.get_message_configuration() + self.schema = common_data.app.cli_object.get_schema_from_reference('', '#/components/schemas/MessageConfiguration') + + self.message_provider_type_widget = common_data.app.getTitledRadioButton( + _("Message Provider Type"), + name='messageProviderType', + values=[(mtype, mtype) for mtype in self.schema['properties']['messageProviderType']['enum']], + current_value='NULL', + jans_help="Message provider Type", + on_selection_changed=self.display_message_provider_type, + style=cli_style.radio_button) + self.working_container = HSplit([ + Frame(self.message_provider_type_widget), + DynamicContainer(lambda: self.message_provider_container) + ]) + + self.first_enter = True + + def display_message_provider_type(self, rlwidget: RadioList): + provider_type = rlwidget.current_value + + save_button = Button(text=_("Save"), handler=self.save) + + if provider_type == 'NULL': + widgets = [] + self.provider_widgets = [] + else: + styles = {'widget_style':cli_style.black_bg_widget, 'string': cli_style.edit_text, 'boolean': cli_style.check_box} + schema_prop_name = f'{provider_type.lower()}Configuration' + properties = self.schema['properties'][schema_prop_name]['properties'] + self.provider_widgets = self.get_widgets( + properties=properties, + values=self.data.get(schema_prop_name), + styles=styles + ) + widgets = [Frame(body=HSplit(self.provider_widgets),title=f'{provider_type} Configuration')] + + widgets.append(VSplit([save_button], align=HorizontalAlign.CENTER)) + self.message_provider_container = HSplit(widgets, width=D()) + + def save(self): + provider_type = self.message_provider_type_widget.me.current_value + + # save provider type + + async def provider_type_coroutine(): + cli_args = {'operation_id': 'patch-config-message', 'data':[{'op':'replace', 'path': 'messageProviderType', 'value': provider_type}]} + common_data.app.start_progressing(_("Saving provider type")) + response = await get_event_loop().run_in_executor(common_data.app.executor, common_data.app.cli_requests, cli_args) + common_data.app.stop_progressing() + if response.status_code not in (200, 201): + common_data.app.show_message(common_strings.error, str(response.text), tobefocused=self.main_container) + else: + self.data = response.json() + + asyncio.ensure_future(provider_type_coroutine()) + + + # save prpvider data + + async def provider_coroutine(pcli_args): + common_data.app.start_progressing(_("Saving message configuration")) + response = await get_event_loop().run_in_executor(common_data.app.executor, common_data.app.cli_requests, pcli_args) + common_data.app.stop_progressing() + if response.status_code not in (200, 201): + common_data.app.show_message(common_strings.error, str(response.text), tobefocused=self.main_container) + else: + self.data = response.json() + + if provider_type != 'NULL': + data = {} + for widget in self.provider_widgets: + item_data = self.get_item_data(widget) + if item_data: + data[item_data['key']] = item_data['value'] + cli_args = {'operation_id': 'put-config-message-' + provider_type.lower(), 'data': data} + + + asyncio.ensure_future(provider_coroutine(cli_args)) + + + def get_message_configuration(self): + async def coroutine(): + cli_args = {'operation_id': 'get-config-message'} + common_data.app.start_progressing(_("Retreiving message configuration")) + response = await get_event_loop().run_in_executor(common_data.app.executor, common_data.app.cli_requests, cli_args) + common_data.app.stop_progressing() + + if response.status_code not in (200, 201): + common_data.app.show_message(common_strings.error, str(response.text), tobefocused=self.main_container) + + try: + self.data = response.json() + except Exception as e: + common_data.app.show_message(common_strings.error, str(e), tobefocused=self.main_container) + return + + self.message_provider_type_widget.me.current_value = self.data.get('messageProviderType', 'NULL') + self.display_message_provider_type(self.message_provider_type_widget.me) + + asyncio.ensure_future(coroutine()) + diff --git a/jans-cli-tui/cli_tui/utils/utils.py b/jans-cli-tui/cli_tui/utils/utils.py index ca995e50ecb..cb2854fce58 100755 --- a/jans-cli-tui/cli_tui/utils/utils.py +++ b/jans-cli-tui/cli_tui/utils/utils.py @@ -11,7 +11,7 @@ from wui_components.jans_drop_down import DropDownWidget from wui_components.jans_spinner import Spinner from wui_components.jans_vetrical_nav import JansVerticalNav - +from utils.static import cli_style from wui_components.jans_date_picker import DateSelectWidget from utils.multi_lang import _ @@ -90,12 +90,95 @@ def check_required_fields(self, container=None, data=None, tobefocused=None): missing_fields.append(item.children[1].jans_name) if missing_fields: - app = self.app if hasattr(self, 'app') else self.myparent - app.show_message(_("Please fill required fields"), _("The following fields are required:\n") + ', '.join(missing_fields), tobefocused=tobefocused) + common_data.app.show_message(_("Please fill required fields"), _("The following fields are required:\n") + ', '.join(missing_fields), tobefocused=tobefocused) return False return True + def get_widgets( + self, + properties:dict, + values: dict=None, + styles: dict=None + ) -> list: + """Returns list of widgets for properties + + Args: + properties (dict): properties to get widget + values (dict): values of properties + styes (dict): styles for widgets + """ + + if not values: + values = {} + if not styles: + styles = {'widget_style':'', 'string': cli_style.edit_text, 'boolean': cli_style.check_box} + + widgets = [] + for item_name in properties: + item = properties[item_name] + if item['type'] in ('integer', 'string'): + if item.get('enum'): + widgets.append( + common_data.app.getTitledWidget( + item_name, + name=item_name, + widget=DropDownWidget( + values=[(str(enum), str(enum)) for enum in item['enum']], + value=str(values.get(item_name) or '') + ), + style=styles['string'], + ) + ) + else: + widgets.append( + common_data.app.getTitledText( + item_name, + name=item_name, + value=str(values.get(item_name) or ''), + text_type=item['type'], + style=styles['string'], + widget_style=styles['widget_style'] + ) + ) + + elif item['type'] == 'boolean': + widgets.append( + common_data.app.getTitledCheckBox( + item_name, + name=item_name, + checked=values.get(item_name, False), + style=styles['boolean'], + widget_style=styles['widget_style'] + ) + ) + + elif item['type'] == 'array' and item['items'].get('enum'): + widgets.append( + common_data.app.getTitledCheckBoxList( + item_name, + name=item_name, + values=item['items']['enum'], + current_values=values.get(item_name, []), + style=styles['boolean'], + widget_style=styles['widget_style'] + ) + ) + + elif item['type'] == 'array' and item['items'].get('type') in ('string', 'integer'): + titled_text = common_data.app.getTitledText( + item_name, + name=item_name, + height=4, + text_type = item['items']['type'], + value='\n'.join(values.get(item_name, [])), + style=styles['string'], + widget_style=styles['widget_style'] + ) + titled_text.jans_list_type = True + widgets.append(titled_text) + + return widgets def fromisoformat(dt_str): dt, _, us = dt_str.partition(".")