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 endpoints::cloud_docs::*,
11 http::Transport,
12 req_option::RequestOption,
13 SDKResult,
14 },
15 impl_executable_builder_owned,
16};
17
18#[derive(Debug, Serialize, Default, Clone)]
20pub struct ListCommentsRequest {
21 #[serde(skip)]
22 api_request: ApiRequest,
23 #[serde(skip)]
25 file_token: String,
26 #[serde(skip)]
28 file_type: String,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 is_whole: Option<bool>,
32 #[serde(skip_serializing_if = "Option::is_none")]
34 is_solved: Option<bool>,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 page_size: Option<i32>,
38 #[serde(skip_serializing_if = "Option::is_none")]
40 page_token: Option<String>,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 user_id_type: Option<String>,
44}
45
46impl ListCommentsRequest {
47 pub fn builder() -> ListCommentsRequestBuilder {
48 ListCommentsRequestBuilder::default()
49 }
50
51 pub fn new(file_token: impl ToString, file_type: impl ToString) -> Self {
52 Self {
53 file_token: file_token.to_string(),
54 file_type: file_type.to_string(),
55 ..Default::default()
56 }
57 }
58}
59
60#[derive(Default)]
61pub struct ListCommentsRequestBuilder {
62 request: ListCommentsRequest,
63}
64
65impl ListCommentsRequestBuilder {
66 pub fn file_token(mut self, file_token: impl ToString) -> Self {
68 self.request.file_token = file_token.to_string();
69 self
70 }
71
72 pub fn file_type(mut self, file_type: impl ToString) -> Self {
74 self.request.file_type = file_type.to_string();
75 self
76 }
77
78 pub fn with_doc_type(mut self) -> Self {
80 self.request.file_type = "doc".to_string();
81 self
82 }
83
84 pub fn with_docx_type(mut self) -> Self {
86 self.request.file_type = "docx".to_string();
87 self
88 }
89
90 pub fn with_sheet_type(mut self) -> Self {
92 self.request.file_type = "sheet".to_string();
93 self
94 }
95
96 pub fn with_bitable_type(mut self) -> Self {
98 self.request.file_type = "bitable".to_string();
99 self
100 }
101
102 pub fn set_whole(mut self, is_whole: bool) -> Self {
104 self.request.is_whole = Some(is_whole);
105 self
106 }
107
108 pub fn whole_comments_only(mut self) -> Self {
110 self.request.is_whole = Some(true);
111 self
112 }
113
114 pub fn all_comment_types(mut self) -> Self {
116 self.request.is_whole = None;
117 self
118 }
119
120 pub fn set_solved(mut self, is_solved: bool) -> Self {
122 self.request.is_solved = Some(is_solved);
123 self
124 }
125
126 pub fn solved_comments_only(mut self) -> Self {
128 self.request.is_solved = Some(true);
129 self
130 }
131
132 pub fn unsolved_comments_only(mut self) -> Self {
134 self.request.is_solved = Some(false);
135 self
136 }
137
138 pub fn all_comments(mut self) -> Self {
140 self.request.is_solved = None;
141 self
142 }
143
144 pub fn page_size(mut self, page_size: i32) -> Self {
146 self.request.page_size = Some(page_size);
147 self
148 }
149
150 pub fn page_token(mut self, page_token: impl ToString) -> Self {
152 self.request.page_token = Some(page_token.to_string());
153 self
154 }
155
156 pub fn user_id_type(mut self, user_id_type: impl ToString) -> Self {
158 self.request.user_id_type = Some(user_id_type.to_string());
159 self
160 }
161
162 pub fn build(mut self) -> ListCommentsRequest {
163 self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
164 self.request
165 }
166}
167
168impl_executable_builder_owned!(
170 ListCommentsRequestBuilder,
171 super::CommentsService,
172 ListCommentsRequest,
173 BaseResponse<ListCommentsResponse>,
174 list
175);
176
177#[derive(Debug, Deserialize)]
179pub struct Comment {
180 pub comment_id: String,
182 pub user_id: String,
184 pub create_time: i64,
186 pub update_time: i64,
188 pub is_solved: bool,
190 pub solved_time: Option<i64>,
192 pub solver_user_id: Option<String>,
194 pub has_more: bool,
196 pub page_token: Option<String>,
198 pub reply_list: Option<Vec<Reply>>,
200 pub is_whole: Option<bool>,
202 pub quote: Option<String>,
204}
205
206#[derive(Debug, Deserialize)]
208pub struct Reply {
209 pub reply_id: String,
211 pub user_id: String,
213 pub create_time: i64,
215 pub update_time: i64,
217 pub content: ReplyContent,
219 pub extra: Option<serde_json::Value>,
221}
222
223#[derive(Debug, Serialize, Deserialize, Default, Clone)]
225pub struct ReplyContent {
226 pub elements: Vec<ContentElement>,
228}
229
230#[derive(Debug, Serialize, Deserialize, Clone)]
232pub struct ContentElement {
233 #[serde(rename = "type")]
235 pub element_type: String,
236 pub text_run: Option<TextRun>,
238}
239
240#[derive(Debug, Serialize, Deserialize, Clone)]
242pub struct TextRun {
243 pub text: String,
245 pub style: Option<serde_json::Value>,
247}
248
249#[derive(Debug, Deserialize)]
251pub struct ListCommentsResponse {
252 pub items: Vec<Comment>,
254 pub has_more: bool,
256 pub page_token: Option<String>,
258}
259
260impl ApiResponseTrait for ListCommentsResponse {
261 fn data_format() -> ResponseFormat {
262 ResponseFormat::Data
263 }
264}
265
266pub async fn list_comments(
268 request: ListCommentsRequest,
269 config: &Config,
270 option: Option<RequestOption>,
271) -> SDKResult<BaseResponse<ListCommentsResponse>> {
272 let mut api_req = request.api_request;
273 api_req.http_method = Method::GET;
274 api_req.api_path = format!(
275 "{}?file_type={}&file_token={}",
276 COMMENT_V1_COMMENTS, request.file_type, request.file_token
277 );
278
279 let mut query_params = Vec::new();
281 if let Some(is_whole) = request.is_whole {
282 query_params.push(format!("is_whole={is_whole}"));
283 }
284 if let Some(is_solved) = request.is_solved {
285 query_params.push(format!("is_solved={is_solved}"));
286 }
287 if let Some(page_size) = request.page_size {
288 query_params.push(format!("page_size={page_size}"));
289 }
290 if let Some(page_token) = request.page_token {
291 query_params.push(format!("page_token={page_token}"));
292 }
293 if let Some(user_id_type) = request.user_id_type {
294 query_params.push(format!("user_id_type={user_id_type}"));
295 }
296
297 if !query_params.is_empty() {
298 api_req.api_path = format!("{}&{}", api_req.api_path, query_params.join("&"));
299 }
300
301 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
302
303 let api_resp = Transport::request(api_req, config, option).await?;
304 Ok(api_resp)
305}
306
307impl Comment {
308 pub fn get_text_content(&self) -> String {
310 if let Some(replies) = &self.reply_list {
311 replies
312 .iter()
313 .map(|reply| reply.get_text_content())
314 .collect::<Vec<_>>()
315 .join("\n")
316 } else {
317 String::new()
318 }
319 }
320
321 pub fn has_replies(&self) -> bool {
323 self.reply_list
324 .as_ref()
325 .is_some_and(|replies| !replies.is_empty())
326 }
327
328 pub fn reply_count(&self) -> usize {
330 self.reply_list.as_ref().map_or(0, |replies| replies.len())
331 }
332}
333
334impl Reply {
335 pub fn get_text_content(&self) -> String {
337 self.content
338 .elements
339 .iter()
340 .filter_map(|element| {
341 element
342 .text_run
343 .as_ref()
344 .map(|text_run| text_run.text.clone())
345 })
346 .collect::<Vec<_>>()
347 .join("")
348 }
349}
350
351#[cfg(test)]
352#[allow(unused_variables, unused_unsafe)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_list_comments_request_builder() {
358 let request = ListCommentsRequest::builder()
359 .file_token("doccnxxxxxx")
360 .with_doc_type()
361 .whole_comments_only()
362 .unsolved_comments_only()
363 .page_size(20)
364 .user_id_type("open_id")
365 .build();
366
367 assert_eq!(request.file_token, "doccnxxxxxx");
368 assert_eq!(request.file_type, "doc");
369 assert_eq!(request.is_whole, Some(true));
370 assert_eq!(request.is_solved, Some(false));
371 assert_eq!(request.page_size, Some(20));
372 assert_eq!(request.user_id_type, Some("open_id".to_string()));
373 }
374}