Skip to main content

wekan_cli/client/
checklist_items.rs

1use reqwest::header::ACCEPT;
2use secrecy::{ExposeSecret, SecretString};
3use serde::{Deserialize, Serialize};
4use serde_json::Number;
5
6use super::{
7    ClientError, WekanClient,
8    checklists::{protocol_error, require_date, require_id},
9    transport::decode_success,
10};
11
12#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
13#[serde(
14    deny_unknown_fields,
15    rename_all(deserialize = "camelCase", serialize = "snake_case")
16)]
17pub struct ChecklistItemDocument {
18    #[serde(rename(deserialize = "_id", serialize = "item_id"))]
19    pub item_id: String,
20    pub title: String,
21    pub sort: Number,
22    pub is_finished: bool,
23    pub checklist_id: String,
24    pub card_id: String,
25    /// Denormalized metadata; v11.06 REST card moves can leave the source board ID.
26    pub board_id: Option<String>,
27    pub user_id: Option<String>,
28    pub created_at: Option<String>,
29    pub modified_at: String,
30}
31
32#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
33#[serde(untagged)]
34pub enum ChecklistItemSortInput {
35    Number(Number),
36    String(String),
37    Null,
38}
39
40#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
41#[serde(untagged)]
42pub enum ChecklistItemFinishedInput {
43    Boolean(bool),
44    String(String),
45}
46
47#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
48pub struct CreateChecklistItemRequest {
49    pub title: String,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub sort: Option<ChecklistItemSortInput>,
52}
53
54#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
55#[serde(rename_all = "camelCase")]
56pub struct UpdateChecklistItemRequest {
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub title: Option<String>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub is_finished: Option<ChecklistItemFinishedInput>,
61}
62
63#[derive(Clone, Debug, Eq, PartialEq)]
64pub struct CreateChecklistItemResult {
65    pub item_id: String,
66}
67
68#[derive(Clone, Debug, Eq, PartialEq)]
69pub struct UpdateChecklistItemResult {
70    pub item_id: String,
71}
72
73#[derive(Clone, Debug, Eq, PartialEq)]
74pub struct DeleteChecklistItemResult {
75    pub item_id: String,
76}
77
78#[derive(Deserialize)]
79#[serde(deny_unknown_fields)]
80struct IdResponse {
81    #[serde(rename = "_id")]
82    id: String,
83}
84
85impl WekanClient {
86    pub async fn checklist_item(
87        &self,
88        board_id: &str,
89        card_id: &str,
90        checklist_id: &str,
91        item_id: &str,
92        token: &SecretString,
93    ) -> Result<ChecklistItemDocument, ClientError> {
94        let body = self
95            .execute_success_request(
96                self.http
97                    .get(self.checklist_item_endpoint(
98                        board_id,
99                        card_id,
100                        checklist_id,
101                        Some(item_id),
102                    )?)
103                    .header(ACCEPT, "application/json")
104                    .bearer_auth(token.expose_secret()),
105            )
106            .await?;
107        let item: ChecklistItemDocument = decode_success(&body, "checklist item", true)?;
108        for (value, field) in [
109            (&item.item_id, "item id"),
110            (&item.checklist_id, "checklist id"),
111            (&item.card_id, "card id"),
112        ] {
113            require_id(value, field)?;
114        }
115        if let Some(id) = &item.board_id {
116            require_id(id, "board id")?;
117        }
118        if let Some(id) = &item.user_id {
119            require_id(id, "creator user id")?;
120        }
121        if item.item_id != item_id || item.checklist_id != checklist_id || item.card_id != card_id {
122            return Err(protocol_error(
123                "the checklist item response identifiers did not match the requested scope",
124            ));
125        }
126        require_date(&item.modified_at)?;
127        if let Some(date) = &item.created_at {
128            require_date(date)?;
129        }
130        Ok(item)
131    }
132
133    pub async fn create_checklist_item(
134        &self,
135        board_id: &str,
136        card_id: &str,
137        checklist_id: &str,
138        request: &CreateChecklistItemRequest,
139        token: &SecretString,
140    ) -> Result<CreateChecklistItemResult, ClientError> {
141        let body = self
142            .execute_success_request(
143                self.http
144                    .post(self.checklist_item_endpoint(board_id, card_id, checklist_id, None)?)
145                    .header(ACCEPT, "application/json")
146                    .bearer_auth(token.expose_secret())
147                    .json(request),
148            )
149            .await?;
150        let response: IdResponse = decode_success(&body, "checklist item creation", true)?;
151        require_id(&response.id, "created item id")?;
152        Ok(CreateChecklistItemResult {
153            item_id: response.id,
154        })
155    }
156
157    pub async fn update_checklist_item(
158        &self,
159        board_id: &str,
160        card_id: &str,
161        checklist_id: &str,
162        item_id: &str,
163        request: &UpdateChecklistItemRequest,
164        token: &SecretString,
165    ) -> Result<UpdateChecklistItemResult, ClientError> {
166        let body = self
167            .execute_success_request(
168                self.http
169                    .put(self.checklist_item_endpoint(
170                        board_id,
171                        card_id,
172                        checklist_id,
173                        Some(item_id),
174                    )?)
175                    .header(ACCEPT, "application/json")
176                    .bearer_auth(token.expose_secret())
177                    .json(request),
178            )
179            .await?;
180        let response: IdResponse = decode_success(&body, "checklist item update", true)?;
181        require_id(&response.id, "updated item id")?;
182        if response.id != item_id {
183            return Err(protocol_error(
184                "Wekan reported a different updated checklist item ID than requested",
185            ));
186        }
187        Ok(UpdateChecklistItemResult {
188            item_id: response.id,
189        })
190    }
191
192    pub async fn delete_checklist_item(
193        &self,
194        board_id: &str,
195        card_id: &str,
196        checklist_id: &str,
197        item_id: &str,
198        token: &SecretString,
199    ) -> Result<DeleteChecklistItemResult, ClientError> {
200        let body = self
201            .execute_success_request(
202                self.http
203                    .delete(self.checklist_item_endpoint(
204                        board_id,
205                        card_id,
206                        checklist_id,
207                        Some(item_id),
208                    )?)
209                    .header(ACCEPT, "application/json")
210                    .bearer_auth(token.expose_secret()),
211            )
212            .await?;
213        let response: IdResponse = decode_success(&body, "checklist item deletion", true)?;
214        require_id(&response.id, "deleted item id")?;
215        if response.id != item_id {
216            return Err(protocol_error(
217                "Wekan reported a different deleted checklist item ID than requested",
218            ));
219        }
220        Ok(DeleteChecklistItemResult {
221            item_id: response.id,
222        })
223    }
224
225    fn checklist_item_endpoint(
226        &self,
227        board_id: &str,
228        card_id: &str,
229        checklist_id: &str,
230        item_id: Option<&str>,
231    ) -> Result<reqwest::Url, ClientError> {
232        let mut endpoint = self.checklist_endpoint(board_id, card_id, Some(checklist_id))?;
233        let mut segments = endpoint
234            .path_segments_mut()
235            .map_err(|()| ClientError::Protocol {
236                diagnostic: None,
237                message: "could not add identifiers to the checklist item endpoint".to_owned(),
238                success_status_received: false,
239            })?;
240        segments.push("items");
241        if let Some(id) = item_id {
242            segments.push(id);
243        }
244        drop(segments);
245        Ok(endpoint)
246    }
247}