mod boards;
mod cards;
mod create;
mod current;
mod delete;
mod disable_login;
mod enable_login;
mod get;
mod list;
mod take_ownership;
mod token;
mod update;
#[cfg(test)]
mod tests;
use clap::{Args, Subcommand};
use crate::{
client::{UserRecord, WekanClientFactory},
command_result::{
CommandSuccess, UserBoardMembership, UserDetail, UserDialogOptionsDetail, UserEmail,
UserOrganization, UserProfileDetail, UserProfileNotificationDetail,
UserProfileStarredPageDetail, UserSessionData, UserTeam, UserWorkspaceNodeDetail,
},
credentials::{CredentialStore, SecretInputProvider},
error::AppError,
input::ConfirmationProvider,
};
use super::client_error::map_client_error;
#[derive(Debug, Args)]
pub struct UserArgs {
#[command(subcommand)]
pub command: UserCommand,
}
#[derive(Debug, Subcommand)]
pub enum UserCommand {
Token(token::TokenArgs),
Current(current::CurrentArgs),
Cards(cards::CardsArgs),
List(list::ListArgs),
Get(get::GetArgs),
Update(update::UpdateArgs),
Create(create::CreateArgs),
Boards(boards::BoardsArgs),
TakeOwnership(take_ownership::TakeOwnershipArgs),
DisableLogin(disable_login::DisableLoginArgs),
EnableLogin(enable_login::EnableLoginArgs),
Delete(delete::DeleteArgs),
}
pub(crate) async fn dispatch(
command: UserCommand,
client_factory: &WekanClientFactory,
credential_store: &dyn CredentialStore,
secret_input: &dyn SecretInputProvider,
confirmation: &dyn ConfirmationProvider,
) -> Result<CommandSuccess, AppError> {
match command {
UserCommand::Token(args) => {
token::dispatch(
args.command,
client_factory,
credential_store,
secret_input,
confirmation,
)
.await
}
UserCommand::Current(args) => {
current::execute(args, client_factory, credential_store).await
}
UserCommand::Cards(args) => cards::execute(args, client_factory, credential_store).await,
UserCommand::List(args) => list::execute(args, client_factory, credential_store).await,
UserCommand::Get(args) => get::execute(args, client_factory, credential_store).await,
UserCommand::Update(args) => update::execute(args, client_factory, credential_store).await,
UserCommand::Create(args) => {
create::execute(args, client_factory, credential_store, secret_input).await
}
UserCommand::Boards(args) => boards::execute(args, client_factory, credential_store).await,
UserCommand::TakeOwnership(args) => {
take_ownership::execute(args, client_factory, credential_store, confirmation).await
}
UserCommand::DisableLogin(args) => {
disable_login::execute(args, client_factory, credential_store, confirmation).await
}
UserCommand::EnableLogin(args) => {
enable_login::execute(args, client_factory, credential_store).await
}
UserCommand::Delete(args) => {
delete::execute(args, client_factory, credential_store, confirmation).await
}
}
}
fn user_detail(user: UserRecord) -> UserDetail {
UserDetail {
passkeys: user.passkeys,
sqrl: user.sqrl,
web_push_subscriptions: user.web_push_subscriptions,
ntfy: user.ntfy,
notification_delivery_pending: user.notification_delivery_pending,
notification_delivery_revision: user.notification_delivery_revision,
feature_preview: user.feature_preview,
inactivity_clock_start: user.inactivity_clock_start,
login_disabled_reason: user.login_disabled_reason,
anonymized: user.anonymized,
anonymized_at: user.anonymized_at,
user_id: user.user_id,
username: user.username,
full_name: user.profile.fullname.clone(),
profile: Box::new(user_profile(*user.profile)),
created_through_api: user.created_through_api,
heartbeat: user.heartbeat,
session_data: user.session_data.map(|data| UserSessionData {
total_hits: data.total_hits,
}),
import_usernames: user.import_usernames,
emails: user
.emails
.into_iter()
.map(|email| UserEmail {
address: email.address,
verified: email.verified,
})
.collect(),
is_admin: user.is_admin,
login_disabled: user.login_disabled,
authentication_method: user.authentication_method,
created_at: user.created_at,
modified_at: user.modified_at,
last_connection_date: user.last_connection_date,
organizations: user
.orgs
.into_iter()
.map(|organization| UserOrganization {
organization_id: organization.org_id,
display_name: organization.org_display_name,
is_admin: organization.is_admin,
})
.collect(),
teams: user
.teams
.into_iter()
.map(|team| UserTeam {
team_id: team.team_id,
display_name: team.team_display_name,
})
.collect(),
boards: user
.boards
.into_iter()
.map(|board| UserBoardMembership {
granted_by: board.granted_by,
custom_role_id: board.custom_role_id,
board_id: board.board_id,
is_active: board.is_active,
is_admin: board.is_admin,
is_no_comments: board.is_no_comments,
is_comment_only: board.is_comment_only,
is_worker: board.is_worker,
is_normal_assigned_only: board.is_normal_assigned_only,
is_comment_assigned_only: board.is_comment_assigned_only,
is_read_only: board.is_read_only,
is_read_assigned_only: board.is_read_assigned_only,
})
.collect(),
}
}
fn non_empty(value: &str) -> Result<String, String> {
if value.is_empty() {
Err("value must not be empty".to_owned())
} else {
Ok(value.to_owned())
}
}
fn user_workspace_node(node: crate::client::UserWorkspaceNode) -> UserWorkspaceNodeDetail {
UserWorkspaceNodeDetail {
id: node.id,
name: node.name,
icon: node.icon,
children: node
.children
.map(|nodes| nodes.into_iter().map(user_workspace_node).collect()),
}
}
fn user_dialog_options(
dialogs: std::collections::BTreeMap<String, crate::client::UserDialogOptions>,
) -> std::collections::BTreeMap<String, UserDialogOptionsDetail> {
dialogs
.into_iter()
.map(|(board, options)| {
(
board,
UserDialogOptionsDetail {
board_id: options.board_id,
swimlane_id: options.swimlane_id,
list_id: options.list_id,
card_id: options.card_id,
},
)
})
.collect()
}
fn user_profile(profile: crate::client::UserProfile) -> UserProfileDetail {
UserProfileDetail {
chat_view_layout: profile.chat_view_layout,
email_to_card: profile.email_to_card,
notification_delivery: profile.notification_delivery,
my_dependencies: profile.my_dependencies,
notify_muted_activities: profile.notify_muted_activities,
notify_override_tray: profile.notify_override_tray,
notify_override_email: profile.notify_override_email,
webhook_hide_identity: profile.webhook_hide_identity,
unread_changes_since: profile.unread_changes_since,
keep_title_tags: profile.keep_title_tags,
checklist_ding_sound: profile.checklist_ding_sound,
dismissed_board_announcements: profile.dismissed_board_announcements,
card_label_text_override: profile.card_label_text_override,
show_label_text_override: profile.show_label_text_override,
board_labels: profile.board_labels,
hidden_boards: profile.hidden_boards,
starred_swimlanes: profile.starred_swimlanes,
starred_lists: profile.starred_lists,
starred_cards: profile.starred_cards,
default_board_template_id: profile.default_board_template_id,
default_board_template_board_id: profile.default_board_template_board_id,
board_views: profile.board_views,
show_board_dependencies: profile.show_board_dependencies,
show_my_dependencies: profile.show_my_dependencies,
board_visits: profile.board_visits,
dismissed_rule_suggestions: profile.dismissed_rule_suggestions,
date_format_override: profile.date_format_override,
full_name: profile.fullname,
avatar_url: profile.avatar_url,
email_buffer: profile.email_buffer,
show_desktop_drag_handles: profile.show_desktop_drag_handles,
left_menu_collapsed: profile.left_menu_collapsed,
collapsed_workspaces: profile.collapsed_workspaces,
left_menu_width: profile.left_menu_width,
submit_on_enter: profile.submit_on_enter,
open_many_cards_at_once: profile.open_many_cards_at_once,
all_boards_theme_tiles: profile.all_boards_theme_tiles,
global_theme_color: profile.global_theme_color,
global_theme_custom_colors: profile.global_theme_custom_colors,
ui_font: profile.ui_font,
ui_font_size: profile.ui_font_size,
ui_text_color: profile.ui_text_color,
ui_text_bg_color: profile.ui_text_bg_color,
dismissed_announcement_version: profile.dismissed_announcement_version,
card_maximized: profile.card_maximized,
card_collapsed: profile.card_collapsed,
show_activities: profile.show_activities,
custom_fields_grid: profile.custom_fields_grid,
trello_api_saved: profile.trello_api_saved,
hidden_minicard_label_text: profile.hidden_minicard_label_text,
initials: profile.initials,
board_workspaces_tree: profile
.board_workspaces_tree
.map(|nodes| nodes.into_iter().map(user_workspace_node).collect()),
board_workspace_assignments: profile.board_workspace_assignments,
invited_boards: profile.invited_boards,
language: profile.language,
map_provider: profile.map_provider,
move_and_copy_dialog: profile.move_and_copy_dialog.map(user_dialog_options),
move_checklist_dialog: profile.move_checklist_dialog.map(user_dialog_options),
copy_checklist_dialog: profile.copy_checklist_dialog.map(user_dialog_options),
notifications: profile.notifications.map(|items| {
items
.into_iter()
.map(|item| UserProfileNotificationDetail {
activity: item.activity,
read: item.read,
show_at: item.show_at,
})
.collect()
}),
rescue_card_description: profile.rescue_card_description,
show_cards_count_at: profile.show_cards_count_at,
start_day_of_week: profile.start_day_of_week,
starred_boards: profile.starred_boards,
starred_pages: profile.starred_pages.map(|items| {
items
.into_iter()
.map(|item| UserProfileStarredPageDetail {
url: item.url,
title: item.title,
})
.collect()
}),
default_board_id: profile.default_board_id,
icode: profile.icode,
board_view: profile.board_view,
list_sort_by: profile.list_sort_by,
all_boards_sort_by: profile.all_boards_sort_by,
templates_board_id: profile.templates_board_id,
card_templates_swimlane_id: profile.card_templates_swimlane_id,
list_templates_swimlane_id: profile.list_templates_swimlane_id,
board_templates_swimlane_id: profile.board_templates_swimlane_id,
sidebar_width: profile.sidebar_width,
list_widths: profile.list_widths,
list_constraints: profile.list_constraints,
auto_width_boards: profile.auto_width_boards,
board_sort_index: profile.board_sort_index,
fixed_list_width_boards: profile.fixed_list_width_boards,
fixed_list_widths: profile.fixed_list_widths,
swimlane_heights: profile.swimlane_heights,
collapsed_lists: profile.collapsed_lists,
collapsed_swimlanes: profile.collapsed_swimlanes,
collapsed_card_sections: profile.collapsed_card_sections,
keyboard_shortcuts: profile.keyboard_shortcuts,
vertical_scrollbars: profile.vertical_scrollbars,
show_week_of_year: profile.show_week_of_year,
date_format: profile.date_format,
mobile_mode: profile.mobile_mode,
card_zoom: profile.card_zoom,
calendar_system: profile.calendar_system,
collapsed_cards: profile.collapsed_cards,
card_last_views: profile.card_last_views,
}
}