1mod boards;
2mod cards;
3mod create;
4mod current;
5mod delete;
6mod disable_login;
7mod enable_login;
8mod get;
9mod list;
10mod take_ownership;
11mod update;
12
13#[cfg(test)]
14mod tests;
15
16use clap::{Args, Subcommand};
17
18use crate::{
19 client::{UserRecord, WekanClientFactory},
20 command_result::{
21 CommandSuccess, UserBoardMembership, UserDetail, UserEmail, UserOrganization,
22 UserProfileDetail, UserProfileNotificationDetail, UserProfileStarredPageDetail,
23 UserSessionData, UserTeam, UserWorkspaceNodeDetail,
24 },
25 credentials::{CredentialStore, SecretInputProvider},
26 error::AppError,
27 input::ConfirmationProvider,
28};
29
30use super::client_error::map_client_error;
31
32#[derive(Debug, Args)]
33pub struct UserArgs {
34 #[command(subcommand)]
35 pub command: UserCommand,
36}
37
38#[derive(Debug, Subcommand)]
39pub enum UserCommand {
40 Current(current::CurrentArgs),
42
43 Cards(cards::CardsArgs),
45
46 List(list::ListArgs),
48
49 Get(get::GetArgs),
51
52 Update(update::UpdateArgs),
54
55 Create(create::CreateArgs),
57
58 Boards(boards::BoardsArgs),
60
61 TakeOwnership(take_ownership::TakeOwnershipArgs),
63
64 DisableLogin(disable_login::DisableLoginArgs),
66
67 EnableLogin(enable_login::EnableLoginArgs),
69
70 Delete(delete::DeleteArgs),
72}
73
74pub(crate) async fn dispatch(
75 command: UserCommand,
76 client_factory: &WekanClientFactory,
77 credential_store: &dyn CredentialStore,
78 secret_input: &dyn SecretInputProvider,
79 confirmation: &dyn ConfirmationProvider,
80) -> Result<CommandSuccess, AppError> {
81 match command {
82 UserCommand::Current(args) => {
83 current::execute(args, client_factory, credential_store).await
84 }
85 UserCommand::Cards(args) => cards::execute(args, client_factory, credential_store).await,
86 UserCommand::List(args) => list::execute(args, client_factory, credential_store).await,
87 UserCommand::Get(args) => get::execute(args, client_factory, credential_store).await,
88 UserCommand::Update(args) => update::execute(args, client_factory, credential_store).await,
89 UserCommand::Create(args) => {
90 create::execute(args, client_factory, credential_store, secret_input).await
91 }
92 UserCommand::Boards(args) => boards::execute(args, client_factory, credential_store).await,
93 UserCommand::TakeOwnership(args) => {
94 take_ownership::execute(args, client_factory, credential_store, confirmation).await
95 }
96 UserCommand::DisableLogin(args) => {
97 disable_login::execute(args, client_factory, credential_store, confirmation).await
98 }
99 UserCommand::EnableLogin(args) => {
100 enable_login::execute(args, client_factory, credential_store).await
101 }
102 UserCommand::Delete(args) => {
103 delete::execute(args, client_factory, credential_store, confirmation).await
104 }
105 }
106}
107
108fn user_detail(user: UserRecord) -> UserDetail {
109 UserDetail {
110 user_id: user.user_id,
111 username: user.username,
112 full_name: user.profile.fullname.clone(),
113 profile: Box::new(user_profile(user.profile)),
114 created_through_api: user.created_through_api,
115 heartbeat: user.heartbeat,
116 session_data: user.session_data.map(|data| UserSessionData {
117 total_hits: data.total_hits,
118 }),
119 import_usernames: user.import_usernames,
120 emails: user
121 .emails
122 .into_iter()
123 .map(|email| UserEmail {
124 address: email.address,
125 verified: email.verified,
126 })
127 .collect(),
128 is_admin: user.is_admin,
129 login_disabled: user.login_disabled,
130 authentication_method: user.authentication_method,
131 created_at: user.created_at,
132 modified_at: user.modified_at,
133 last_connection_date: user.last_connection_date,
134 organizations: user
135 .orgs
136 .into_iter()
137 .map(|organization| UserOrganization {
138 organization_id: organization.org_id,
139 display_name: organization.org_display_name,
140 is_admin: organization.is_admin,
141 })
142 .collect(),
143 teams: user
144 .teams
145 .into_iter()
146 .map(|team| UserTeam {
147 team_id: team.team_id,
148 display_name: team.team_display_name,
149 })
150 .collect(),
151 boards: user
152 .boards
153 .into_iter()
154 .map(|board| UserBoardMembership {
155 board_id: board.board_id,
156 is_active: board.is_active,
157 is_admin: board.is_admin,
158 is_no_comments: board.is_no_comments,
159 is_comment_only: board.is_comment_only,
160 is_worker: board.is_worker,
161 is_normal_assigned_only: board.is_normal_assigned_only,
162 is_comment_assigned_only: board.is_comment_assigned_only,
163 is_read_only: board.is_read_only,
164 is_read_assigned_only: board.is_read_assigned_only,
165 })
166 .collect(),
167 }
168}
169
170fn non_empty(value: &str) -> Result<String, String> {
171 if value.is_empty() {
172 Err("value must not be empty".to_owned())
173 } else {
174 Ok(value.to_owned())
175 }
176}
177
178fn user_workspace_node(node: crate::client::UserWorkspaceNode) -> UserWorkspaceNodeDetail {
179 UserWorkspaceNodeDetail {
180 id: node.id,
181 name: node.name,
182 icon: node.icon,
183 children: node
184 .children
185 .map(|nodes| nodes.into_iter().map(user_workspace_node).collect()),
186 }
187}
188
189fn user_profile(profile: crate::client::UserProfile) -> UserProfileDetail {
190 UserProfileDetail {
191 full_name: profile.fullname,
192 avatar_url: profile.avatar_url,
193 email_buffer: profile.email_buffer,
194 show_desktop_drag_handles: profile.show_desktop_drag_handles,
195 left_menu_collapsed: profile.left_menu_collapsed,
196 collapsed_workspaces: profile.collapsed_workspaces,
197 left_menu_width: profile.left_menu_width,
198 submit_on_enter: profile.submit_on_enter,
199 open_many_cards_at_once: profile.open_many_cards_at_once,
200 all_boards_theme_tiles: profile.all_boards_theme_tiles,
201 global_theme_color: profile.global_theme_color,
202 global_theme_custom_colors: profile.global_theme_custom_colors,
203 ui_font: profile.ui_font,
204 ui_font_size: profile.ui_font_size,
205 ui_text_color: profile.ui_text_color,
206 ui_text_bg_color: profile.ui_text_bg_color,
207 dismissed_announcement_version: profile.dismissed_announcement_version,
208 card_maximized: profile.card_maximized,
209 card_collapsed: profile.card_collapsed,
210 show_activities: profile.show_activities,
211 custom_fields_grid: profile.custom_fields_grid,
212 trello_api_saved: profile.trello_api_saved,
213 hidden_minicard_label_text: profile.hidden_minicard_label_text,
214 initials: profile.initials,
215 board_workspaces_tree: profile
216 .board_workspaces_tree
217 .map(|nodes| nodes.into_iter().map(user_workspace_node).collect()),
218 board_workspace_assignments: profile.board_workspace_assignments,
219 invited_boards: profile.invited_boards,
220 language: profile.language,
221 map_provider: profile.map_provider,
222 move_and_copy_dialog: profile.move_and_copy_dialog,
223 move_checklist_dialog: profile.move_checklist_dialog,
224 copy_checklist_dialog: profile.copy_checklist_dialog,
225 notifications: profile.notifications.map(|items| {
226 items
227 .into_iter()
228 .map(|item| UserProfileNotificationDetail {
229 activity: item.activity,
230 read: item.read,
231 })
232 .collect()
233 }),
234 rescue_card_description: profile.rescue_card_description,
235 show_cards_count_at: profile.show_cards_count_at,
236 start_day_of_week: profile.start_day_of_week,
237 starred_boards: profile.starred_boards,
238 starred_pages: profile.starred_pages.map(|items| {
239 items
240 .into_iter()
241 .map(|item| UserProfileStarredPageDetail {
242 url: item.url,
243 title: item.title,
244 })
245 .collect()
246 }),
247 default_board_id: profile.default_board_id,
248 icode: profile.icode,
249 board_view: profile.board_view,
250 list_sort_by: profile.list_sort_by,
251 all_boards_sort_by: profile.all_boards_sort_by,
252 templates_board_id: profile.templates_board_id,
253 card_templates_swimlane_id: profile.card_templates_swimlane_id,
254 list_templates_swimlane_id: profile.list_templates_swimlane_id,
255 board_templates_swimlane_id: profile.board_templates_swimlane_id,
256 sidebar_width: profile.sidebar_width,
257 list_widths: profile.list_widths,
258 list_constraints: profile.list_constraints,
259 auto_width_boards: profile.auto_width_boards,
260 board_sort_index: profile.board_sort_index,
261 fixed_list_width_boards: profile.fixed_list_width_boards,
262 fixed_list_widths: profile.fixed_list_widths,
263 swimlane_heights: profile.swimlane_heights,
264 collapsed_lists: profile.collapsed_lists,
265 collapsed_swimlanes: profile.collapsed_swimlanes,
266 collapsed_card_sections: profile.collapsed_card_sections,
267 keyboard_shortcuts: profile.keyboard_shortcuts,
268 vertical_scrollbars: profile.vertical_scrollbars,
269 show_week_of_year: profile.show_week_of_year,
270 date_format: profile.date_format,
271 mobile_mode: profile.mobile_mode,
272 card_zoom: profile.card_zoom,
273 }
274}