wekan_cli/client/cards/
reads.rs1use reqwest::header::ACCEPT;
2use secrecy::{ExposeSecret, SecretString};
3use serde::{Deserialize, Serialize};
4use serde_json::Number;
5
6use super::{
7 CardDocument, ClientError, WekanClient, require_non_empty, validate_card_document,
8 validate_optional_date_time, validate_optional_id,
9};
10use crate::client::transport::decode_success;
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 SwimlaneCardSummary {
18 #[serde(rename(deserialize = "_id", serialize = "card_id"))]
19 pub card_id: String,
20 pub title: Option<String>,
21 pub description: Option<String>,
22 pub list_id: Option<String>,
23 pub received_at: Option<String>,
24 pub start_at: Option<String>,
25 pub due_at: Option<String>,
26 pub end_at: Option<String>,
27 pub assignees: Option<Vec<Option<String>>>,
28 pub sort: Option<Number>,
29}
30
31#[derive(Deserialize)]
32#[serde(deny_unknown_fields)]
33struct BoardCardCount {
34 board_cards_count: u64,
35}
36
37#[derive(Deserialize)]
38#[serde(deny_unknown_fields)]
39struct ListCardCount {
40 list_cards_count: u64,
41}
42
43impl WekanClient {
44 pub async fn board_card_count(
45 &self,
46 board_id: &str,
47 token: &SecretString,
48 ) -> Result<u64, ClientError> {
49 let body = self
50 .execute_success_request(
51 self.http
52 .get(self.board_endpoint(board_id, Some("cards_count"))?)
53 .header(ACCEPT, "application/json")
54 .bearer_auth(token.expose_secret()),
55 )
56 .await?;
57 let count: BoardCardCount = decode_success(&body, "board card count", true)?;
58 Ok(count.board_cards_count)
59 }
60
61 pub async fn list_card_count(
62 &self,
63 board_id: &str,
64 list_id: &str,
65 token: &SecretString,
66 ) -> Result<u64, ClientError> {
67 let mut endpoint = self.list_endpoint(board_id, Some(list_id))?;
68 endpoint
69 .path_segments_mut()
70 .expect("HTTP endpoint")
71 .push("cards_count");
72 let body = self
73 .execute_success_request(
74 self.http
75 .get(endpoint)
76 .header(ACCEPT, "application/json")
77 .bearer_auth(token.expose_secret()),
78 )
79 .await?;
80 let count: ListCardCount = decode_success(&body, "list card count", true)?;
81 Ok(count.list_cards_count)
82 }
83
84 pub async fn card_by_id(
85 &self,
86 card_id: &str,
87 token: &SecretString,
88 ) -> Result<CardDocument, ClientError> {
89 let mut endpoint = self
90 .server()
91 .join("api/cards")
92 .map_err(|_| super::protocol_error("could not build the card endpoint".into()))?;
93 endpoint
94 .path_segments_mut()
95 .expect("HTTP endpoint")
96 .push(card_id);
97 let body = self
98 .execute_success_request(
99 self.http
100 .get(endpoint)
101 .header(ACCEPT, "application/json")
102 .bearer_auth(token.expose_secret()),
103 )
104 .await?;
105 validate_card_document(decode_success(&body, "card lookup by ID", true)?)
106 }
107
108 pub async fn swimlane_cards(
109 &self,
110 board_id: &str,
111 swimlane_id: &str,
112 token: &SecretString,
113 ) -> Result<Vec<SwimlaneCardSummary>, ClientError> {
114 let mut endpoint = self.swimlane_endpoint(board_id, Some(swimlane_id))?;
115 endpoint
116 .path_segments_mut()
117 .expect("HTTP endpoint")
118 .push("cards");
119 let body = self
120 .execute_success_request(
121 self.http
122 .get(endpoint)
123 .header(ACCEPT, "application/json")
124 .bearer_auth(token.expose_secret()),
125 )
126 .await?;
127 let cards: Vec<SwimlaneCardSummary> =
128 decode_success(&body, "swimlane card collection", true)?;
129 for card in &cards {
130 require_non_empty(&card.card_id, "swimlane card collection", "card id")?;
131 validate_optional_id(
132 card.list_id.as_deref(),
133 "swimlane card collection",
134 "list id",
135 )?;
136 for id in card.assignees.iter().flatten().flatten() {
137 require_non_empty(id, "swimlane card collection", "assignee id")?;
138 }
139 for (field, value) in [
140 ("receivedAt", &card.received_at),
141 ("startAt", &card.start_at),
142 ("dueAt", &card.due_at),
143 ("endAt", &card.end_at),
144 ] {
145 validate_optional_date_time(value.as_deref(), field, "swimlane card collection")?;
146 }
147 }
148 Ok(cards)
149 }
150}