1use reqwest::{Method, header::ACCEPT};
2use secrecy::{ExposeSecret, SecretString};
3use serde::Deserialize;
4
5use super::{ClientError, WekanClient, require_non_empty, validate_ids};
6use crate::client::{boards::members::validate_id, transport::decode_success};
7
8#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
9#[serde(deny_unknown_fields)]
10pub struct CardMembershipResponse {
11 #[serde(rename = "_id")]
12 pub card_id: String,
13 pub members: Vec<String>,
14 pub assignees: Vec<String>,
15}
16
17impl WekanClient {
18 pub async fn add_card_member(
19 &self,
20 board: &str,
21 list: &str,
22 card: &str,
23 user: &str,
24 token: &SecretString,
25 ) -> Result<CardMembershipResponse, ClientError> {
26 self.change_card_membership((board, list, card), user, "members", Method::POST, token)
27 .await
28 }
29 pub async fn remove_card_member(
30 &self,
31 board: &str,
32 list: &str,
33 card: &str,
34 user: &str,
35 token: &SecretString,
36 ) -> Result<CardMembershipResponse, ClientError> {
37 self.change_card_membership((board, list, card), user, "members", Method::DELETE, token)
38 .await
39 }
40 pub async fn add_card_assignee(
41 &self,
42 board: &str,
43 list: &str,
44 card: &str,
45 user: &str,
46 token: &SecretString,
47 ) -> Result<CardMembershipResponse, ClientError> {
48 self.change_card_membership((board, list, card), user, "assignees", Method::POST, token)
49 .await
50 }
51 pub async fn remove_card_assignee(
52 &self,
53 board: &str,
54 list: &str,
55 card: &str,
56 user: &str,
57 token: &SecretString,
58 ) -> Result<CardMembershipResponse, ClientError> {
59 self.change_card_membership(
60 (board, list, card),
61 user,
62 "assignees",
63 Method::DELETE,
64 token,
65 )
66 .await
67 }
68
69 async fn change_card_membership(
70 &self,
71 (board, list, card): (&str, &str, &str),
72 user: &str,
73 field: &str,
74 method: Method,
75 token: &SecretString,
76 ) -> Result<CardMembershipResponse, ClientError> {
77 for id in [board, list, card, user] {
78 validate_id(id)?;
79 }
80 let mut endpoint = self.card_endpoint(board, list, Some(card))?;
81 endpoint
82 .path_segments_mut()
83 .expect("the server URL supports path segments")
84 .extend([field, user]);
85 let body = self
86 .execute_success_request(
87 self.http
88 .request(method, endpoint)
89 .header(ACCEPT, "application/json")
90 .bearer_auth(token.expose_secret()),
91 )
92 .await?;
93 let response: CardMembershipResponse = decode_success(&body, "card membership", true)?;
94 require_non_empty(&response.card_id, "card membership", "card id")?;
95 validate_ids(&response.members, "card membership", "member id")?;
96 validate_ids(&response.assignees, "card membership", "assignee id")?;
97 if response.card_id != card {
98 return Err(ClientError::Protocol {
99 diagnostic: None,
100 message: "Wekan acknowledged a different membership card ID than requested".into(),
101 success_status_received: true,
102 });
103 }
104 Ok(response)
105 }
106}