Skip to content

Commit

Permalink
fix: jans-cli-tui working branch 7 (#3824)
Browse files Browse the repository at this point in the history
* feat: jans-cli-tui path browser (ref: #3823)

* feat: jans-cli-tui implement path browser for save data

* feat: jans-cli-tui agama project deployment

* fix: jans-cli-tui remove unused path finder

* fix: jans-cli-tui code smells

* fix: jans-cli-tui code smells
  • Loading branch information
devrimyatar authored Feb 10, 2023
1 parent 8a49559 commit 776bab3
Show file tree
Hide file tree
Showing 7 changed files with 426 additions and 519 deletions.
81 changes: 19 additions & 62 deletions jans-cli-tui/cli_tui/jans_cli_tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,47 +42,6 @@
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
from prompt_toolkit.layout.containers import Float, HSplit, VSplit
from prompt_toolkit.formatted_text import HTML, merge_formatted_text
from prompt_toolkit.completion import PathCompleter


class TextInputDialog:
def __init__(self, title="", label_text="", completer=None):
self.future = Future()

def accept_text(buf):
get_app().layout.focus(ok_button)
buf.complete_state = None
return True

def accept():
self.future.set_result(self.text_area.text)

def cancel():
self.future.set_result(None)

self.text_area = TextArea(
completer=completer,
multiline=False,
width=D(preferred=40),
accept_handler=accept_text,
)

ok_button = Button(text="OK", handler=accept)
cancel_button = Button(text="Cancel", handler=cancel)

self.dialog = Dialog(
title=title,
body=HSplit([Label(text=label_text), self.text_area]),
buttons=[ok_button, cancel_button],
width=D(preferred=80),
modal=True,
)

def __pt_container__(self):
return self.dialog



from prompt_toolkit.layout.containers import (
Float,
HSplit,
Expand Down Expand Up @@ -123,6 +82,7 @@ def __pt_container__(self):
from wui_components.jans_cli_dialog import JansGDialog
from wui_components.jans_nav_bar import JansNavBar
from wui_components.jans_message_dialog import JansMessageDialog
from wui_components.jans_path_browser import jans_file_browser_dialog, BrowseType

home_dir = Path.home()
config_dir = home_dir.joinpath('.config')
Expand Down Expand Up @@ -158,6 +118,7 @@ def __init__(self):
self.pbar_text = ""
self.progressing_text = ""
self.mouse_float=True
self.browse_path = '/'

self.not_implemented = Frame(
body=HSplit([Label(text=_("Not imlemented yet")), Button(text=_("MyButton"))], width=D()),
Expand Down Expand Up @@ -814,9 +775,11 @@ async def show_dialog_as_float(self, dialog:Dialog, focus=None) -> None:
'Coroutine.'
float_ = Float(content=dialog)
self.root_layout.floats.append(float_)
self.layout.focus(dialog)

if focus:
self.layout.focus(focus)
else:
self.layout.focus(dialog)

self.invalidate()

Expand All @@ -837,10 +800,12 @@ def show_jans_dialog(self, dialog:Dialog, focus=None) -> None:
async def coroutine():
focused_before = self.layout.current_window
result = await self.show_dialog_as_float(dialog, focus)
try:
self.layout.focus(focused_before)
except Exception:
self.layout.focus(self.center_frame)

if not self.root_layout.floats:
try:
self.layout.focus(focused_before)
except Exception:
self.layout.focus(self.center_frame)

return result

Expand All @@ -865,30 +830,22 @@ def data_display_dialog(self, **params: Any) -> None:
body = HSplit(data_display_widgets, style='class:jans-main-datadisplay')
title = params.get('title') or params['selected'][0]

def do_save(dialog):
def do_save(path):
try:
with open(dialog.body.text, 'w') as w:
with open(path, 'w') as w:
w.write(text_area.text)
self.pbar_text = _("File {} was saved".format(dialog.body.text))
self.show_message(_("Info"), _("File {} was successfully saved").format(dialog.body.text), tobefocused=self.center_container)
self.pbar_text = _("File {} was saved".format(text_area.text))
self.show_message(_("Info"), _("File {} was successfully saved").format(path), tobefocused=self.center_container)
except Exception as e:
self.show_message(_("Error!"), _("An error ocurred while saving") + ":\n{}".format(str(e)), tobefocused=self.center_container)

def save(dialog):
dialog.future.set_result('Save')
path_textarea = TextArea(style=cli_style.edit_text)
do_save_button = Button(_("OK"), handler=do_save)
buttons = [Button('Cancel'), do_save_button]
save_dialog = JansGDialog(self, title=_("Enter path of file to save"), body=path_textarea, buttons=buttons)
self.show_jans_dialog(save_dialog)


save_button = Button(_("Save"), handler=save)

def save(dialog):
file_browser_dialog = jans_file_browser_dialog(self, path=self.browse_path, browse_type=BrowseType.save_as, ok_handler=do_save)
self.show_jans_dialog(file_browser_dialog)

save_button = Button(_("Export"), handler=save)
buttons = [Button('Close'), save_button]
dialog = JansGDialog(self, title=title, body=body, buttons=buttons)

self.show_jans_dialog(dialog)

def save_creds(self, dialog:Dialog) -> None:
Expand Down
170 changes: 170 additions & 0 deletions jans-cli-tui/cli_tui/plugins/010_auth_server/agama.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import os
import json
import asyncio
import zipfile

from datetime import datetime
from typing import Any

from prompt_toolkit.application import Application
from prompt_toolkit.eventloop import get_event_loop
from prompt_toolkit.layout.dimension import D
from prompt_toolkit.layout.containers import HSplit, VSplit, DynamicContainer
from prompt_toolkit.buffer import Buffer
from prompt_toolkit.formatted_text import HTML

from utils.multi_lang import _
from utils.utils import DialogUtils
from utils.static import cli_style, common_strings
from wui_components.jans_vetrical_nav import JansVerticalNav
from wui_components.jans_path_browser import jans_file_browser_dialog, BrowseType

class Agama(DialogUtils):
def __init__(
self,
app: Application
) -> None:

self.app = self.myparent = app
self.data = []
self.working_container = JansVerticalNav(
myparent=app,
headers=[_("Project Name"), _("Type"), _("Author"), _("Updated")],
preferred_size= self.app.get_column_sizes(.25, .25 , .3, .1, .1),
on_display=self.app.data_display_dialog,
on_delete=self.delete_agama_project,
selectes=0,
headerColor=cli_style.navbar_headcolor,
entriesColor=cli_style.navbar_entriescolor,
hide_headers = True
)

self.main_container = HSplit([
VSplit([
self.app.getButton(text=_("Get Projects"), name='oauth:agama:get', jans_help=_("Retreive all Agama Projects"), handler=self.get_agama_projects),
self.app.getTitledText(_("Search"), name='oauth:agama:search', jans_help=_(common_strings.enter_to_search), accept_handler=self.search_agama_project, style=cli_style.edit_text),
self.app.getButton(text=_("Upload Project"), name='oauth:agama:add', jans_help=_("To add a new Agama project press this button"), handler=self.upload_project),
],
padding=3,
width=D(),
),
DynamicContainer(lambda: self.working_container)
], style=cli_style.container)


def update_agama_container(self, start_index=0, search_str=''):

self.working_container.clear()
data_display = []

for agama in self.data.get('entries', []):
if search_str.lower():
project_str = ' '.join((
agama['details']['projectMetadata'].get('projectName'),
agama['details']['projectMetadata'].get('author', ''),
agama['details']['projectMetadata'].get('type', ''),
agama['details']['projectMetadata'].get('description', '')
)).lower()
if search_str not in project_str:
continue

dt_object = datetime.fromisoformat(agama['createdAt'])

data_display.append((
agama['details']['projectMetadata'].get('projectName'),
agama['details']['projectMetadata'].get('type', '??'),
agama['details']['projectMetadata'].get('author', '??'),
'{:02d}/{:02d}/{}'.format(dt_object.day, dt_object.month, str(dt_object.year)[2:])
))

if not data_display:
self.app.show_message(_("Oops"), _(common_strings.no_matching_result), tobefocused = self.main_container)
return

self.working_container.hide_headers = False
for datum in data_display[start_index:start_index+self.app.entries_per_page]:
self.working_container.add_item(datum)

self.app.layout.focus(self.working_container)

def get_agama_projects(self, search_str=''):
async def coroutine():
cli_args = {'operation_id': 'get-agama-dev-prj'}
self.app.start_progressing(_("Retreiving agama projects..."))
response = await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
try:
self.data = response.json()
except Exception:
self.app.show_message(_(common_strings.error), HTML(_("Server reterned non json data <i>{}</i>").format(response.text)), tobefocused=self.app.center_container)
return

if not 'entriesCount' in self.data:
self.app.show_message(_(common_strings.error), HTML(_("Server reterned unexpected data <i>{}</i>").format(self.data)), tobefocused=self.app.center_container)
return

self.working_container.all_data = self.data.get('entries', [])
self.update_agama_container(search_str=search_str)

asyncio.ensure_future(coroutine())

def upload_project(self):

def do_upload_project(path):
try:
project_zip = zipfile.ZipFile(path)
except Exception:
self.app.show_message(_(common_strings.error), HTML(_("Can't open <b>{}</b> as zip file.").format(path)), tobefocused=self.app.center_container)
return

try:
project_json = json.loads(project_zip.read('project.json'))
except Exception as e:
self.app.show_message(_(common_strings.error), HTML(_("Can't read <b>project.json</b> from zip file:\n {}").format(str(e))), tobefocused=self.app.center_container)
return

if 'projectName' not in project_json:
self.app.show_message(_(common_strings.error), HTML(_("Property <b>projectName</b> does not exist in <b>project.json</b>.")), tobefocused=self.app.center_container)
return

async def coroutine():
cli_args = {'operation_id': 'post-agama-dev-studio-prj', 'data_fn': path, 'url_suffix':'name:{}'.format(project_json['projectName'])}
self.app.start_progressing(_("Uploading agama project..."))
await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
self.get_agama_projects()

asyncio.ensure_future(coroutine())


file_browser_dialog = jans_file_browser_dialog(self.app, path=self.app.browse_path, browse_type=BrowseType.file, ok_handler=do_upload_project)
self.app.show_jans_dialog(file_browser_dialog)

def search_agama_project(self, tbuffer:Buffer) -> None:
if 'entries' in self.data:
self.update_agama_container(search_str=tbuffer.text)
else:
self.get_agama_projects(search_str=tbuffer.text)

def delete_agama_project(self, **kwargs: Any) -> None:
agama = self.data['entries'][kwargs['selected_idx']]
project_name = agama['details']['projectMetadata']['projectName']

def do_delete_agama_project(result):
async def coroutine():
cli_args = {'operation_id': 'delete-agama-dev-studio-prj', 'url_suffix': 'name:{}'.format(agama['details']['projectMetadata']['projectName'])}
self.app.start_progressing(_("Deleting agama project {}".format(project_name)))
await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args)
self.app.stop_progressing()
self.get_agama_projects()

asyncio.ensure_future(coroutine())

dialog = self.app.get_confirm_dialog(
message = HTML(_("Are you sure want to delete Agama Project <b>{}</b>?").format(project_name)),
confirm_handler=do_delete_agama_project
)

self.app.show_jans_dialog(dialog)


5 changes: 4 additions & 1 deletion jans-cli-tui/cli_tui/plugins/010_auth_server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from edit_client_dialog import EditClientDialog
from edit_scope_dialog import EditScopeDialog
from ssa import SSA
from agama import Agama

from prompt_toolkit.widgets import (
HorizontalLine,
Expand All @@ -62,6 +63,7 @@ def __init__(
self.search_text= None
self.oauth_update_properties_start_index = 0
self.ssa = SSA(app)
self.agama = Agama(app)
self.app_configuration = {}
self.oauth_containers = {}

Expand Down Expand Up @@ -185,6 +187,7 @@ def oauth_prepare_containers(self) -> None:
],style='class:outh_containers_scopes')

self.oauth_containers['ssa'] = self.ssa.main_container
self.oauth_containers['agama'] = self.agama.main_container
self.oauth_containers['logging'] = DynamicContainer(lambda: self.oauth_data_container['logging'])

self.oauth_main_container = HSplit([
Expand All @@ -200,7 +203,7 @@ def oauth_prepare_navbar(self) -> None:
"""
self.nav_bar = JansNavBar(
self.app,
entries=[('clients', 'C[l]ients'), ('scopes', 'Sc[o]pes'), ('keys', '[K]eys'), ('defaults', '[D]efaults'), ('properties', 'Properti[e]s'), ('logging', 'Lo[g]ging'), ('ssa', '[S]SA')],
entries=[('clients', 'C[l]ients'), ('scopes', 'Sc[o]pes'), ('keys', '[K]eys'), ('defaults', '[D]efaults'), ('properties', 'Properti[e]s'), ('logging', 'Lo[g]ging'), ('ssa', '[S]SA'), ('agama', 'Aga[m]a')],
selection_changed=self.oauth_nav_selection_changed,
select=0,
jans_name='oauth:nav_bar'
Expand Down
1 change: 1 addition & 0 deletions jans-cli-tui/cli_tui/utils/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ class cli_style:
class common_strings:
enter_to_search = "Press enter to perform search"
no_matching_result = "No matching result"
error = "Error!"
10 changes: 5 additions & 5 deletions jans-cli-tui/cli_tui/wui_components/jans_cli_dialog.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import json
from functools import partial
from asyncio import Future
from prompt_toolkit.widgets import Button, Dialog
Expand All @@ -14,10 +13,10 @@ class JansGDialog:
def __init__(
self,
parent,
body: Optional[AnyContainer]=None,
title: Optional[str]= '',
buttons: Optional[Sequence[Button]]=[],
width: AnyDimension=None
body: Optional[AnyContainer] = None,
title: Optional[str] = '',
buttons: Optional[Sequence[Button]] = None,
width: AnyDimension = None
)-> Dialog:

"""init for JansGDialog
Expand All @@ -35,6 +34,7 @@ def __init__(
self.future = Future()
self.body = body
self.myparent = parent
self.title = title

if not width:
width = parent.dialog_width
Expand Down
Loading

0 comments on commit 776bab3

Please sign in to comment.