1use clap::Subcommand;
2use crate::client::ClickUpClient;
3use crate::commands::auth::resolve_token;
4use crate::commands::workspace::resolve_workspace;
5use crate::error::CliError;
6use crate::output::OutputConfig;
7use crate::Cli;
8
9#[derive(Subcommand)]
10pub enum GuestCommands {
11 Invite {
13 #[arg(long)]
15 email: String,
16 #[arg(long)]
18 can_edit_tags: bool,
19 #[arg(long)]
21 can_see_time_spent: bool,
22 #[arg(long)]
24 can_create_views: bool,
25 #[arg(long)]
27 custom_role_id: Option<u64>,
28 },
29 Get {
31 id: String,
33 },
34 Update {
36 id: String,
38 #[arg(long)]
40 can_edit_tags: Option<bool>,
41 #[arg(long)]
43 can_see_time_spent: Option<bool>,
44 #[arg(long)]
46 can_create_views: Option<bool>,
47 #[arg(long)]
49 custom_role_id: Option<u64>,
50 },
51 Remove {
53 id: String,
55 },
56 ShareTask {
58 task_id: String,
60 guest_id: String,
62 #[arg(long)]
64 permission: String,
65 },
66 UnshareTask {
68 task_id: String,
70 guest_id: String,
72 },
73 ShareList {
75 list_id: String,
77 guest_id: String,
79 #[arg(long)]
81 permission: String,
82 },
83 UnshareList {
85 list_id: String,
87 guest_id: String,
89 },
90 ShareFolder {
92 folder_id: String,
94 guest_id: String,
96 #[arg(long)]
98 permission: String,
99 },
100 UnshareFolder {
102 folder_id: String,
104 guest_id: String,
106 },
107}
108
109const GUEST_FIELDS: &[&str] = &["id", "username", "email", "role"];
110
111pub async fn execute(command: GuestCommands, cli: &Cli) -> Result<(), CliError> {
112 let token = resolve_token(cli)?;
113 let client = ClickUpClient::new(&token, cli.timeout)?;
114 let output = OutputConfig::from_cli(&cli.output, &cli.fields, cli.no_header, cli.quiet);
115
116 match command {
117 GuestCommands::Invite {
118 email,
119 can_edit_tags,
120 can_see_time_spent,
121 can_create_views,
122 custom_role_id,
123 } => {
124 let team_id = resolve_workspace(cli)?;
125 let mut body = serde_json::json!({
126 "email": email,
127 "can_edit_tags": can_edit_tags,
128 "can_see_time_spent": can_see_time_spent,
129 "can_create_views": can_create_views,
130 });
131 if let Some(role_id) = custom_role_id {
132 body["custom_role_id"] = serde_json::Value::Number(role_id.into());
133 }
134 let resp = client
135 .post(&format!("/v2/team/{}/guest", team_id), &body)
136 .await?;
137 let guest = resp.get("guest").cloned().unwrap_or(resp);
138 output.print_single(&guest, GUEST_FIELDS, "id");
139 Ok(())
140 }
141 GuestCommands::Get { id } => {
142 let team_id = resolve_workspace(cli)?;
143 let resp = client
144 .get(&format!("/v2/team/{}/guest/{}", team_id, id))
145 .await?;
146 let guest = resp.get("guest").cloned().unwrap_or(resp);
147 output.print_single(&guest, GUEST_FIELDS, "id");
148 Ok(())
149 }
150 GuestCommands::Update {
151 id,
152 can_edit_tags,
153 can_see_time_spent,
154 can_create_views,
155 custom_role_id,
156 } => {
157 let team_id = resolve_workspace(cli)?;
158 let mut body = serde_json::Map::new();
159 if let Some(v) = can_edit_tags {
160 body.insert("can_edit_tags".into(), serde_json::Value::Bool(v));
161 }
162 if let Some(v) = can_see_time_spent {
163 body.insert("can_see_time_spent".into(), serde_json::Value::Bool(v));
164 }
165 if let Some(v) = can_create_views {
166 body.insert("can_create_views".into(), serde_json::Value::Bool(v));
167 }
168 if let Some(v) = custom_role_id {
169 body.insert("custom_role_id".into(), serde_json::Value::Number(v.into()));
170 }
171 let resp = client
172 .put(
173 &format!("/v2/team/{}/guest/{}", team_id, id),
174 &serde_json::Value::Object(body),
175 )
176 .await?;
177 let guest = resp.get("guest").cloned().unwrap_or(resp);
178 output.print_single(&guest, GUEST_FIELDS, "id");
179 Ok(())
180 }
181 GuestCommands::Remove { id } => {
182 let team_id = resolve_workspace(cli)?;
183 client
184 .delete(&format!("/v2/team/{}/guest/{}", team_id, id))
185 .await?;
186 output.print_message(&format!("Guest {} removed from workspace", id));
187 Ok(())
188 }
189 GuestCommands::ShareTask {
190 task_id,
191 guest_id,
192 permission,
193 } => {
194 let body = serde_json::json!({ "permission_level": permission });
195 let resp = client
196 .post(&format!("/v2/task/{}/guest/{}", task_id, guest_id), &body)
197 .await?;
198 output.print_single(&resp, GUEST_FIELDS, "id");
199 Ok(())
200 }
201 GuestCommands::UnshareTask { task_id, guest_id } => {
202 client
203 .delete(&format!("/v2/task/{}/guest/{}", task_id, guest_id))
204 .await?;
205 output.print_message(&format!(
206 "Guest {} unshared from task {}",
207 guest_id, task_id
208 ));
209 Ok(())
210 }
211 GuestCommands::ShareList {
212 list_id,
213 guest_id,
214 permission,
215 } => {
216 let body = serde_json::json!({ "permission_level": permission });
217 let resp = client
218 .post(&format!("/v2/list/{}/guest/{}", list_id, guest_id), &body)
219 .await?;
220 output.print_single(&resp, GUEST_FIELDS, "id");
221 Ok(())
222 }
223 GuestCommands::UnshareList { list_id, guest_id } => {
224 client
225 .delete(&format!("/v2/list/{}/guest/{}", list_id, guest_id))
226 .await?;
227 output.print_message(&format!(
228 "Guest {} unshared from list {}",
229 guest_id, list_id
230 ));
231 Ok(())
232 }
233 GuestCommands::ShareFolder {
234 folder_id,
235 guest_id,
236 permission,
237 } => {
238 let body = serde_json::json!({ "permission_level": permission });
239 let resp = client
240 .post(
241 &format!("/v2/folder/{}/guest/{}", folder_id, guest_id),
242 &body,
243 )
244 .await?;
245 output.print_single(&resp, GUEST_FIELDS, "id");
246 Ok(())
247 }
248 GuestCommands::UnshareFolder {
249 folder_id,
250 guest_id,
251 } => {
252 client
253 .delete(&format!("/v2/folder/{}/guest/{}", folder_id, guest_id))
254 .await?;
255 output.print_message(&format!(
256 "Guest {} unshared from folder {}",
257 guest_id, folder_id
258 ));
259 Ok(())
260 }
261 }
262}