mrllama123 / dyno-viewer Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
currently we only list the first 100 tables in aws account this needs to support more than that
add the ability to view the table in different ways i.e:
note: when selecting an entire row make sure to only include the attributes that are in the item
right now whenever there is an error it crashes and as a end user you can't see the logs (i really hope in future they fix this) so it would be good to add some helpful messages in the app for errors like:
right now when you do a query in the app it always assumes that its a string type but you can have the primary key as any DynamoDB type. So the app needs to support that
right now it just shows the raw object/value in the dataTable which is no readable when it comes to maps or decimals. Look into the rich Text object maybe
testing this doesn't look like its going to be easy but would be nice if we could have some good basic unit tests
before releasing it to the wild, Want to refine and add some more docs.
Some doc changes i can think off the top of my head are:
when trying to do a scan with filters it wont run the query
right now the ui styling and css is very hacky and all over the place as i didn't know what i was doing when i originally wrote it. Also it doesn't display the table data very well for one row items
add a menu to select or deselect cols similar to how the dynamodb console does it
like in the console support removing/adding back cols being displayed in the table viewer
the current way we display data from tables is hacky and over complicated
the different ui components are in one file this needs to be split out into separate files and folders for code readability. Might also change repo name to be a bit more clearer on what it is
textual does support animations: https://textual.textualize.io/guide/animation/ and loading screens: https://textual.textualize.io/widget_gallery/#loadingindicator
so should be easy enough to add
for some regions it will throw an exception when you switch to it once you have loaded up a table for the first time
add a help menu. Which shows things like keyboard shortcuts etc
e.g for test_pk_sk_gsi_data test case 1 snapshot rows are:
[
[
"customer#12345",
"CUSTOMER",
null,
null,
null,
null,
null,
null,
null,
null
],
[
"customer#12345",
"CUSTOMER",
"account#123",
"ACCOUNT",
"account#123",
"ACCOUNT",
"account2#123",
"ACCOUNT2",
"account3#123",
"ACCOUNT3"
]
]
which it should be:
[
[
"customer#12345",
"CUSTOMER",
null,
null,
null,
null,
null,
null,
],
[
"customer#12345",
"CUSTOMER",
"account#123",
"ACCOUNT",
"account2#123",
"ACCOUNT2",
"account3#123",
"ACCOUNT3"
]
]
with this fix i will also remove snapshot support as it hid this issue way to easily and is easy enough to just use standard assert. I did some of this in here: #50 will move that into a separate branch and make that pr just support concurrency
also snapshots are not supported for running tests concurrently which is needed to make sure the tests are fast and don't chew into github action minutes
right now when it paginates a query or a scanned result of a table it displays all of it in the app. Which after something like 100 or so items in the app data table, It lags the app
stack trace:
╭──────────────────────────────────────────────────────────── Traceback (most recent call last) ────────────────────────────────────────────────────────────╮
│ /home/bob/dev/dyna-cli/dyna_cli/components/screens/table_select.py:76 in on_input_changed │
│ │
│ 73 │ │ list_view = self.query_one(ListView) │
│ 74 │ │ list_view.clear() │
│ 75 │ │ for matched_table in match_tables: │
│ ❱ 76 │ │ │ list_view.append(ListItem(Label(matched_table), id=matched_table)) │
│ 77 │ │
│ 78 │ def on_input_submitted(self, submitted: Input.Submitted) -> None: │
│ 79 │ │ if submitted.value in self.tables: │
│ │
│ ╭─────────────────────────────────── locals ────────────────────────────────────╮ │
│ │ changed = Changed() │ │
│ │ list_view = ListView(pseudo_classes={'enabled'}) │ │
│ │ match_tables = ['test-table', 'test-table'] │ │
│ │ matched_table = 'test-table' │ │
│ │ self = TableSelectScreen(pseudo_classes={'enabled', 'focus-within'}) │ │
│ ╰───────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ /home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_list_view.py:185 in append │
│ │
│ 182 │ │ │ An awaitable that yields control to the event loop │
│ 183 │ │ │ │ until the DOM has been updated with the new child item. │
│ 184 │ │ """ │
│ ❱ 185 │ │ await_mount = self.mount(item) │
│ 186 │ │ if len(self) == 1: │
│ 187 │ │ │ self.index = 0 │
│ 188 │ │ return await_mount │
│ │
│ ╭─────────────────────────── locals ───────────────────────────╮ │
│ │ item = ListItem(id='test-table', pseudo_classes={'enabled'}) │ │
│ │ self = ListView(pseudo_classes={'enabled'}) │ │
│ ╰──────────────────────────────────────────────────────────────╯ │
│ │
│ /home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widget.py:749 in mount │
│ │
│ 746 │ │ else: │
│ 747 │ │ │ parent = self │
│ 748 │ │ │
│ ❱ 749 │ │ mounted = self.app._register( │
│ 750 │ │ │ parent, *widgets, before=insert_before, after=insert_after │
│ 751 │ │ ) │
│ 752 │
│ │
│ ╭──────────────────────────────────── locals ─────────────────────────────────────╮ │
│ │ after = None │ │
│ │ before = None │ │
│ │ ids_to_mount = ['test-table'] │ │
│ │ insert_after = None │ │
│ │ insert_before = None │ │
│ │ num_unique_ids = 1 │ │
│ │ num_widgets_with_ids = 1 │ │
│ │ parent = ListView(pseudo_classes={'enabled'}) │ │
│ │ self = ListView(pseudo_classes={'enabled'}) │ │
│ │ unique_ids = {'test-table'} │ │
│ │ widgets = (ListItem(id='test-table', pseudo_classes={'enabled'}),) │ │
│ ╰─────────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ /home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/app.py:1880 in _register │
│ │
│ 1877 │ │ │ if not isinstance(widget, Widget): │
│ 1878 │ │ │ │ raise AppError(f"Can't register {widget!r}; expected a Widget instance") │
│ 1879 │ │ │ if widget not in self._registry: │
│ ❱ 1880 │ │ │ │ self._register_child(parent, widget, before, after) │
│ 1881 │ │ │ │ if widget._nodes: │
│ 1882 │ │ │ │ │ self._register(widget, *widget._nodes) │
│ 1883 │ │ │ │ apply_stylesheet(widget) │
│ │
│ ╭─────────────────────────────────────────────────────────────────────── locals ────────────────────────────────────────────────────────────────────────╮ │
│ │ after = None │ │
│ │ apply_stylesheet = <bound method Stylesheet.apply of <Stylesheet ['/home/bob/dev/dyna-cli/dyna_cli/components/css/query.css', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/app.py:App', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/screen.py:Screen', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widget.py:Widget', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_data_table.py:DataT… │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/scroll_view.py:ScrollView', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/containers.py:ScrollableCont… │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_footer.py:Footer', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/containers.py:VerticalScroll… │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_list_item.py:ListIt… │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_label.py:Label', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_static.py:Static', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/containers.py:Container', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_button.py:Button', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/containers.py:Horizontal', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_switch.py:Switch', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_option_list.py:Opti… │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_input.py:Input', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_select.py:Select', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/containers.py:Vertical', │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_select.py:SelectCur… │ │
│ │ '/home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/widgets/_select.py:SelectOve… │ │
│ │ before = None │ │
│ │ parent = ListView(pseudo_classes={'enabled'}) │ │
│ │ self = DynCli(title='DynCli', classes={'-dark-mode'}) │ │
│ │ widget = ListItem(id='test-table', pseudo_classes={'enabled'}) │ │
│ │ widget_list = (ListItem(id='test-table', pseudo_classes={'enabled'}),) │ │
│ │ widgets = (ListItem(id='test-table', pseudo_classes={'enabled'}),) │ │
│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ /home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/app.py:1835 in _register_child │
│ │
│ 1832 │ │ │ │ # At this point we appear to not be adding before or after, │
│ 1833 │ │ │ │ # or we've got a before/after value that really means │
│ 1834 │ │ │ │ # "please append". So... │
│ ❱ 1835 │ │ │ │ parent._nodes._append(child) │
│ 1836 │ │ │ │
│ 1837 │ │ │ # Now that the widget is in the NodeList of its parent, sort out │
│ 1838 │ │ │ # the rest of the admin. │
│ │
│ ╭──────────────────────────── locals ────────────────────────────╮ │
│ │ after = None │ │
│ │ before = None │ │
│ │ child = ListItem(id='test-table', pseudo_classes={'enabled'}) │ │
│ │ parent = ListView(pseudo_classes={'enabled'}) │ │
│ │ self = DynCli(title='DynCli', classes={'-dark-mode'}) │ │
│ ╰────────────────────────────────────────────────────────────────╯ │
│ │
│ /home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/_node_list.py:81 in _append │
│ │
│ 78 │ │ │ self._nodes_set.add(widget) │
│ 79 │ │ │ widget_id = widget.id │
│ 80 │ │ │ if widget_id is not None: │
│ ❱ 81 │ │ │ │ self._ensure_unique_id(widget_id) │
│ 82 │ │ │ │ self._nodes_by_id[widget_id] = widget │
│ 83 │ │ │ self._updates += 1 │
│ 84 │
│ │
│ ╭────────────────────────────────────────────── locals ───────────────────────────────────────────────╮ │
│ │ self = <NodeList │ │
│ │ │ [ │ │
│ │ │ │ ListItem( │ │
│ │ │ │ │ id='test-table', │ │
│ │ │ │ │ classes={'--highlight'}, │ │
│ │ │ │ │ pseudo_classes={'enabled'} │ │
│ │ │ │ ), │ │
│ │ │ │ ListItem(id='test-table', pseudo_classes={'enabled'}) │ │
│ │ │ ] │ │
│ │ > │ │
│ │ widget = ListItem(id='test-table', pseudo_classes={'enabled'}) │ │
│ │ widget_id = 'test-table' │ │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│ │
│ /home/bob/.cache/pypoetry/virtualenvs/dyna-cli-iGbQMlYs-py3.10/lib/python3.10/site-packages/textual/_node_list.py:102 in _ensure_unique_id │
│ │
│ 99 │ │
│ 100 │ def _ensure_unique_id(self, widget_id: str) -> None: │
│ 101 │ │ if widget_id in self._nodes_by_id: │
│ ❱ 102 │ │ │ raise DuplicateIds( │
│ 103 │ │ │ │ f"Tried to insert a widget with ID {widget_id!r}, but a widget {self._no │
│ 104 │ │ │ │ "already exists with that ID in this list of children. " │
│ 105 │ │ │ │ "The children of a widget must have unique IDs." │
│ │
│ ╭────────────────────────────────────────────── locals ───────────────────────────────────────────────╮ │
│ │ self = <NodeList │ │
│ │ │ [ │ │
│ │ │ │ ListItem( │ │
│ │ │ │ │ id='test-table', │ │
│ │ │ │ │ classes={'--highlight'}, │ │
│ │ │ │ │ pseudo_classes={'enabled'} │ │
│ │ │ │ ), │ │
│ │ │ │ ListItem(id='test-table', pseudo_classes={'enabled'}) │ │
│ │ │ ] │ │
│ │ > │ │
│ │ widget_id = 'test-table' │ │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
support paginating though a table data
flatpak doesn't work well for cli apps and adds more complexity than needed
add the ability to output data to a csv or json
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.