open_lark/service/cloud_docs/comments/
list.rs1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 core::{
6 api_req::ApiRequest,
7 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
8 config::Config,
9 constants::AccessTokenType,
10 http::Transport,
11 req_option::RequestOption,
12 SDKResult,
13 },
14 impl_executable_builder_owned,
15};
16
17#[derive(Debug, Serialize, Default, Clone)]
19pub struct ListCommentsRequest {
20 #[serde(skip)]
21 api_request: ApiRequest,
22 #[serde(skip)]
24 file_token: String,
25 #[serde(skip)]
27 file_type: String,
28 #[serde(skip_serializing_if = "Option::is_none")]
30 is_whole: Option<bool>,
31 #[serde(skip_serializing_if = "Option::is_none")]
33 is_solved: Option<bool>,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 page_size: Option<i32>,
37 #[serde(skip_serializing_if = "Option::is_none")]
39 page_token: Option<String>,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 user_id_type: Option<String>,
43}
44
45impl ListCommentsRequest {
46 pub fn builder() -> ListCommentsRequestBuilder {
47 ListCommentsRequestBuilder::default()
48 }
49
50 pub fn new(file_token: impl ToString, file_type: impl ToString) -> Self {
51 Self {
52 file_token: file_token.to_string(),
53 file_type: file_type.to_string(),
54 ..Default::default()
55 }
56 }
57}
58
59#[derive(Default)]
60pub struct ListCommentsRequestBuilder {
61 request: ListCommentsRequest,
62}
63
64impl ListCommentsRequestBuilder {
65 pub fn file_token(mut self, file_token: impl ToString) -> Self {
67 self.request.file_token = file_token.to_string();
68 self
69 }
70
71 pub fn file_type(mut self, file_type: impl ToString) -> Self {
73 self.request.file_type = file_type.to_string();
74 self
75 }
76
77 pub fn with_doc_type(mut self) -> Self {
79 self.request.file_type = "doc".to_string();
80 self
81 }
82
83 pub fn with_docx_type(mut self) -> Self {
85 self.request.file_type = "docx".to_string();
86 self
87 }
88
89 pub fn with_sheet_type(mut self) -> Self {
91 self.request.file_type = "sheet".to_string();
92 self
93 }
94
95 pub fn with_bitable_type(mut self) -> Self {
97 self.request.file_type = "bitable".to_string();
98 self
99 }
100
101 pub fn set_whole(mut self, is_whole: bool) -> Self {
103 self.request.is_whole = Some(is_whole);
104 self
105 }
106
107 pub fn whole_comments_only(mut self) -> Self {
109 self.request.is_whole = Some(true);
110 self
111 }
112
113 pub fn all_comment_types(mut self) -> Self {
115 self.request.is_whole = None;
116 self
117 }
118
119 pub fn set_solved(mut self, is_solved: bool) -> Self {
121 self.request.is_solved = Some(is_solved);
122 self
123 }
124
125 pub fn solved_comments_only(mut self) -> Self {
127 self.request.is_solved = Some(true);
128 self
129 }
130
131 pub fn unsolved_comments_only(mut self) -> Self {
133 self.request.is_solved = Some(false);
134 self
135 }
136
137 pub fn all_comments(mut self) -> Self {
139 self.request.is_solved = None;
140 self
141 }
142
143 pub fn page_size(mut self, page_size: i32) -> Self {
145 self.request.page_size = Some(page_size);
146 self
147 }
148
149 pub fn page_token(mut self, page_token: impl ToString) -> Self {
151 self.request.page_token = Some(page_token.to_string());
152 self
153 }
154
155 pub fn user_id_type(mut self, user_id_type: impl ToString) -> Self {
157 self.request.user_id_type = Some(user_id_type.to_string());
158 self
159 }
160
161 pub fn build(mut self) -> ListCommentsRequest {
162 self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
163 self.request
164 }
165}
166
167impl_executable_builder_owned!(
169 ListCommentsRequestBuilder,
170 super::CommentsService,
171 ListCommentsRequest,
172 BaseResponse<ListCommentsResponse>,
173 list
174);
175
176#[derive(Debug, Deserialize)]
178pub struct Comment {
179 pub comment_id: String,
181 pub user_id: String,
183 pub create_time: i64,
185 pub update_time: i64,
187 pub is_solved: bool,
189 pub solved_time: Option<i64>,
191 pub solver_user_id: Option<String>,
193 pub has_more: bool,
195 pub page_token: Option<String>,
197 pub reply_list: Option<Vec<Reply>>,
199 pub is_whole: Option<bool>,
201 pub quote: Option<String>,
203}
204
205#[derive(Debug, Deserialize)]
207pub struct Reply {
208 pub reply_id: String,
210 pub user_id: String,
212 pub create_time: i64,
214 pub update_time: i64,
216 pub content: ReplyContent,
218 pub extra: Option<serde_json::Value>,
220}
221
222#[derive(Debug, Serialize, Deserialize, Default, Clone)]
224pub struct ReplyContent {
225 pub elements: Vec<ContentElement>,
227}
228
229#[derive(Debug, Serialize, Deserialize, Clone)]
231pub struct ContentElement {
232 #[serde(rename = "type")]
234 pub element_type: String,
235 pub text_run: Option<TextRun>,
237}
238
239#[derive(Debug, Serialize, Deserialize, Clone)]
241pub struct TextRun {
242 pub text: String,
244 pub style: Option<serde_json::Value>,
246}
247
248#[derive(Debug, Deserialize)]
250pub struct ListCommentsResponse {
251 pub items: Vec<Comment>,
253 pub has_more: bool,
255 pub page_token: Option<String>,
257}
258
259impl ApiResponseTrait for ListCommentsResponse {
260 fn data_format() -> ResponseFormat {
261 ResponseFormat::Data
262 }
263}
264
265pub async fn list_comments(
267 request: ListCommentsRequest,
268 config: &Config,
269 option: Option<RequestOption>,
270) -> SDKResult<BaseResponse<ListCommentsResponse>> {
271 let mut api_req = request.api_request;
272 api_req.http_method = Method::GET;
273 api_req.api_path = format!(
274 "/open-apis/comment/v1/comments?file_type={}&file_token={}",
275 request.file_type, request.file_token
276 );
277
278 let mut query_params = Vec::new();
280 if let Some(is_whole) = request.is_whole {
281 query_params.push(format!("is_whole={is_whole}"));
282 }
283 if let Some(is_solved) = request.is_solved {
284 query_params.push(format!("is_solved={is_solved}"));
285 }
286 if let Some(page_size) = request.page_size {
287 query_params.push(format!("page_size={page_size}"));
288 }
289 if let Some(page_token) = request.page_token {
290 query_params.push(format!("page_token={page_token}"));
291 }
292 if let Some(user_id_type) = request.user_id_type {
293 query_params.push(format!("user_id_type={user_id_type}"));
294 }
295
296 if !query_params.is_empty() {
297 api_req.api_path = format!("{}&{}", api_req.api_path, query_params.join("&"));
298 }
299
300 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
301
302 let api_resp = Transport::request(api_req, config, option).await?;
303 Ok(api_resp)
304}
305
306impl Comment {
307 pub fn get_text_content(&self) -> String {
309 if let Some(replies) = &self.reply_list {
310 replies
311 .iter()
312 .map(|reply| reply.get_text_content())
313 .collect::<Vec<_>>()
314 .join("\n")
315 } else {
316 String::new()
317 }
318 }
319
320 pub fn has_replies(&self) -> bool {
322 self.reply_list
323 .as_ref()
324 .is_some_and(|replies| !replies.is_empty())
325 }
326
327 pub fn reply_count(&self) -> usize {
329 self.reply_list.as_ref().map_or(0, |replies| replies.len())
330 }
331}
332
333impl Reply {
334 pub fn get_text_content(&self) -> String {
336 self.content
337 .elements
338 .iter()
339 .filter_map(|element| {
340 element
341 .text_run
342 .as_ref()
343 .map(|text_run| text_run.text.clone())
344 })
345 .collect::<Vec<_>>()
346 .join("")
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::*;
353
354 #[test]
355 fn test_list_comments_request_builder() {
356 let request = ListCommentsRequest::builder()
357 .file_token("doccnxxxxxx")
358 .with_doc_type()
359 .whole_comments_only()
360 .unsolved_comments_only()
361 .page_size(20)
362 .user_id_type("open_id")
363 .build();
364
365 assert_eq!(request.file_token, "doccnxxxxxx");
366 assert_eq!(request.file_type, "doc");
367 assert_eq!(request.is_whole, Some(true));
368 assert_eq!(request.is_solved, Some(false));
369 assert_eq!(request.page_size, Some(20));
370 assert_eq!(request.user_id_type, Some("open_id".to_string()));
371 }
372}