From cb58b300e16b8deec05dbefd2c0ed18d696d3eb4 Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Mon, 3 Feb 2025 19:12:28 +0100 Subject: [PATCH 1/2] chore: Implement user deletion address user deletion. --- openstack_tui/.config/config.yaml | 6 ++-- .../src/components/identity/users.rs | 31 ++++++++++++++++++- openstack_tui/src/components/table_view.rs | 19 ++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/openstack_tui/.config/config.yaml b/openstack_tui/.config/config.yaml index 28b85c354..1000bbc72 100644 --- a/openstack_tui/.config/config.yaml +++ b/openstack_tui/.config/config.yaml @@ -127,12 +127,12 @@ mode_keybindings: "y": action: DescribeApiResponse description: YAML + "ctrl-d": + action: IdentityUserDelete + description: Delete "e": action: IdentityUserFlipEnable description: Enable/Disable user - "r": - action: IdentityUserDelete - description: Delete user (todo!) "a": action: IdentityUserCreate description: Create new user (todo!) diff --git a/openstack_tui/src/components/identity/users.rs b/openstack_tui/src/components/identity/users.rs index 39b55b078..1b01c07e6 100644 --- a/openstack_tui/src/components/identity/users.rs +++ b/openstack_tui/src/components/identity/users.rs @@ -23,7 +23,7 @@ use crate::{ action::Action, cloud_worker::identity::v3::{ IdentityApiRequest, IdentityUserApiRequest, IdentityUserApplicationCredentialListBuilder, - IdentityUserList, IdentityUserSetBuilder, + IdentityUserDelete, IdentityUserDeleteBuilder, IdentityUserList, IdentityUserSetBuilder, }, cloud_worker::types::ApiRequest, components::{table_view::TableViewComponentBase, Component}, @@ -118,6 +118,27 @@ impl Component for IdentityUsers<'_> { } } } + Action::IdentityUserDelete => { + // only if we are currently in the proper mode + if current_mode == Mode::IdentityUsers { + // and have command_tx + if let Some(command_tx) = self.get_command_tx() { + // and have a selected entry + if let Some(row) = self.get_selected() { + // send action to set Delete User + command_tx.send(Action::Confirm(ApiRequest::from( + IdentityUserApiRequest::Delete(Box::new( + IdentityUserDeleteBuilder::default() + .id(row.id.clone()) + .name(row.name.clone()) + .build() + .wrap_err("cannot prepare user delete request")?, + )), + )))?; + } + } + } + } Action::ShowIdentityUserApplicationCredentials => { // only if we are currently in the proper mode if current_mode == Mode::IdentityUsers { @@ -170,6 +191,14 @@ impl Component for IdentityUsers<'_> { self.sync_table_data()?; } self.set_loading(false); + } else if let IdentityUserApiRequest::Delete(del) = *req { + if let IdentityUserDelete { id, .. } = *del { + if self.delete_item_row_by_res_id_mut(&id)?.is_none() { + return Ok(Some(Action::Refresh)); + } + self.sync_table_data()?; + self.set_loading(false); + } } } _ => {} diff --git a/openstack_tui/src/components/table_view.rs b/openstack_tui/src/components/table_view.rs index 2e9f714b5..b8978ba91 100644 --- a/openstack_tui/src/components/table_view.rs +++ b/openstack_tui/src/components/table_view.rs @@ -576,6 +576,25 @@ where None } + /// delete the row with the typed data matching resource id + #[instrument(level = "debug", skip(self))] + pub fn delete_item_row_by_res_id_mut(&mut self, search_id: &String) -> Result> { + let mut item_idx: Option = None; + for (idx, raw_item) in self.raw_items.iter_mut().enumerate() { + if let Some(row_item_id) = raw_item.get("id").or(raw_item.get("uuid")) { + if row_item_id == search_id { + item_idx = Some(idx); + break; + } + } + } + if let Some(idx) = item_idx { + self.raw_items.remove(idx); + self.items.remove(idx); + } + Ok(item_idx) + } + pub fn get_selected_raw(&self) -> Option<&Value> { self.state.selected().map(|x| &self.raw_items[x]) } From ad0119d69c511908f449d7c8d315ef2b7d881f73 Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Wed, 5 Feb 2025 14:07:28 +0100 Subject: [PATCH 2/2] chore: Address some clippy warnings --- openstack_tui/src/components/describe.rs | 21 +------------------ openstack_tui/src/components/error_popup.rs | 6 ------ openstack_tui/src/components/home.rs | 15 +------------ .../src/components/resource_select_popup.rs | 14 ------------- openstack_tui/src/config.rs | 18 ++++++++-------- openstack_tui/src/lib.rs | 6 +++--- 6 files changed, 14 insertions(+), 66 deletions(-) diff --git a/openstack_tui/src/components/describe.rs b/openstack_tui/src/components/describe.rs index fe64904c0..7ba47833e 100644 --- a/openstack_tui/src/components/describe.rs +++ b/openstack_tui/src/components/describe.rs @@ -21,16 +21,13 @@ use ratatui::{ }; use serde_json::Value; use std::cmp; -use std::collections::HashMap; use crate::{action::Action, config::Config, error::TuiError, mode::Mode}; #[derive(Default)] pub struct Describe { config: Config, - pub keymap: HashMap, - pub text: Vec, - pub last_events: Vec, + text: Vec, title: Option, is_focused: bool, is_loading: bool, @@ -49,17 +46,6 @@ impl Describe { } } - pub fn keymap(mut self, keymap: HashMap) -> Self { - self.keymap = keymap; - self - } - - pub fn tick(&mut self) { - self.last_events.drain(..); - } - - pub fn render_tick(&mut self) {} - pub fn set_loading(&mut self, loading: bool) { self.is_loading = loading; } @@ -98,11 +84,6 @@ impl Describe { Ok(()) } - pub fn set_title(&mut self, title: Option) -> Result<()> { - self.title = title; - Ok(()) - } - pub fn set_focus(&mut self, focus: bool) -> Result<()> { self.is_focused = focus; Ok(()) diff --git a/openstack_tui/src/components/error_popup.rs b/openstack_tui/src/components/error_popup.rs index 11d28bad8..43fc360b0 100644 --- a/openstack_tui/src/components/error_popup.rs +++ b/openstack_tui/src/components/error_popup.rs @@ -19,7 +19,6 @@ use ratatui::{ prelude::*, widgets::{block::*, *}, }; -use std::collections::HashMap; use crate::{ action::Action, components::Component, config::Config, error::TuiError, mode::Mode, @@ -28,8 +27,6 @@ use crate::{ pub struct ErrorPopup { config: Config, - pub keymap: HashMap, - pub last_events: Vec, text: Vec, scroll: (u16, u16), } @@ -44,14 +41,11 @@ impl ErrorPopup { pub fn new() -> Self { Self { config: Config::default(), - keymap: HashMap::new(), text: Vec::new(), - last_events: Vec::new(), scroll: (0, 0), } } - pub fn render_tick(&mut self) {} pub fn scroll_right(&mut self) { self.scroll.0 = self.scroll.0.saturating_add(1); } diff --git a/openstack_tui/src/components/home.rs b/openstack_tui/src/components/home.rs index 37f187102..ecea35188 100644 --- a/openstack_tui/src/components/home.rs +++ b/openstack_tui/src/components/home.rs @@ -12,7 +12,6 @@ // // SPDX-License-Identifier: Apache-2.0 -use crossterm::event::KeyEvent; use eyre::{Result, WrapErr}; use itertools::Itertools; use ratatui::{ @@ -25,7 +24,6 @@ use ratatui::{ }; use serde::Deserialize; use serde_json::Value; -use std::collections::HashMap; use tokio::sync::mpsc::UnboundedSender; use crate::{ @@ -78,8 +76,6 @@ pub struct Home { project_id: Option, compute_quota: Option, network_quota: Option, - pub keymap: HashMap, - pub last_events: Vec, } impl Home { @@ -87,16 +83,7 @@ impl Home { Self::default() } - pub fn keymap(mut self, keymap: HashMap) -> Self { - self.keymap = keymap; - self - } - - pub fn tick(&mut self) { - self.last_events.drain(..); - } - - pub fn render_tick(&mut self) {} + pub fn tick(&mut self) {} pub fn set_loading(&mut self, loading: bool) { self.is_loading = loading; diff --git a/openstack_tui/src/components/resource_select_popup.rs b/openstack_tui/src/components/resource_select_popup.rs index 8f7329232..da0f122fe 100644 --- a/openstack_tui/src/components/resource_select_popup.rs +++ b/openstack_tui/src/components/resource_select_popup.rs @@ -19,7 +19,6 @@ use ratatui::{ prelude::*, widgets::{block::*, *}, }; -use std::collections::HashMap; use tokio::sync::mpsc::UnboundedSender; use crate::{ @@ -34,8 +33,6 @@ use crate::{ pub struct ApiRequestSelect { command_tx: Option>, config: Config, - pub keymap: HashMap, - pub last_events: Vec, fuzzy_list: FuzzySelectList, } @@ -43,17 +40,6 @@ impl ApiRequestSelect { pub fn new() -> Self { Self::default() } - - pub fn keymap(mut self, keymap: HashMap) -> Self { - self.keymap = keymap; - self - } - - pub fn tick(&mut self) { - self.last_events.drain(..); - } - - pub fn render_tick(&mut self) {} } impl Component for ApiRequestSelect { diff --git a/openstack_tui/src/config.rs b/openstack_tui/src/config.rs index 1e86da18a..1aae0e9b2 100644 --- a/openstack_tui/src/config.rs +++ b/openstack_tui/src/config.rs @@ -31,18 +31,18 @@ use crate::{action::Action, mode::Mode}; const CONFIG: &str = include_str!("../.config/config.yaml"); -#[derive(Clone, Debug, Deserialize, Default)] -pub struct AppConfig { - #[serde(default)] - pub _data_dir: PathBuf, - #[serde(default)] - pub _config_dir: PathBuf, -} +//#[derive(Clone, Debug, Deserialize, Default)] +//pub struct AppConfig { +// #[serde(default)] +// pub _data_dir: PathBuf, +// #[serde(default)] +// pub _config_dir: PathBuf, +//} #[derive(Clone, Debug, Default, Deserialize)] pub struct Config { - #[serde(default, flatten)] - pub config: AppConfig, + //#[serde(default, flatten)] + //pub config: AppConfig, #[serde(default)] pub mode_keybindings: HashMap, #[serde(default)] diff --git a/openstack_tui/src/lib.rs b/openstack_tui/src/lib.rs index cc4f782a2..7d956e865 100644 --- a/openstack_tui/src/lib.rs +++ b/openstack_tui/src/lib.rs @@ -18,10 +18,10 @@ pub(crate) mod action; pub mod app; pub mod cli; pub(crate) mod cloud_worker; -pub(crate) mod components; -pub(crate) mod config; +pub mod components; +pub mod config; pub(crate) mod error; pub(crate) mod mode; pub(crate) mod tui; pub mod utils; -pub(crate) mod widgets; +pub mod widgets;