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