1use log::error;
2use reqwest::Method;
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 core::{
7 api_req::ApiRequest,
8 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
9 config::Config,
10 constants::AccessTokenType,
11 endpoints::cloud_docs::*,
12 http::Transport,
13 req_option::RequestOption,
14 standard_response::StandardResponse,
15 trait_system::Service,
16 SDKResult,
17 },
18 impl_executable_builder_owned,
19};
20
21pub struct FileService {
23 config: Config,
24}
25
26impl FileService {
27 pub fn new(config: Config) -> Self {
28 Self { config }
29 }
30
31 pub async fn get_file_meta(
37 &self,
38 request: GetFileMetaRequest,
39 option: Option<RequestOption>,
40 ) -> SDKResult<GetFileMetaRespData> {
41 let api_req = ApiRequest {
42 http_method: Method::POST,
43 api_path: DRIVE_V1_METAS_BATCH_QUERY.to_string(),
44 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
45 body: serde_json::to_vec(&request)?,
46 ..Default::default()
47 };
48
49 let api_resp: BaseResponse<GetFileMetaRespData> =
50 Transport::request(api_req, &self.config, option).await?;
51 api_resp.into_result()
52 }
53
54 pub async fn get_file_statistics(
60 &self,
61 request: GetFileStatisticsRequest,
62 option: Option<RequestOption>,
63 ) -> SDKResult<GetFileStatisticsRespData> {
64 let api_req = ApiRequest {
65 http_method: Method::GET,
66 api_path: DRIVE_V1_FILE_STATISTICS.replace("{}", &request.file_token),
67 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
68 ..Default::default()
69 };
70
71 let api_resp: BaseResponse<GetFileStatisticsRespData> =
72 Transport::request(api_req, &self.config, option).await?;
73 api_resp.into_result()
74 }
75
76 pub async fn list_file_view_records(
82 &self,
83 request: ListFileViewRecordsRequest,
84 option: Option<RequestOption>,
85 ) -> SDKResult<ListFileViewRecordsRespData> {
86 let mut api_req = ApiRequest {
87 http_method: Method::GET,
88 api_path: DRIVE_V1_FILE_VIEW_RECORDS.replace("{}", &request.file_token),
89 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
90 ..Default::default()
91 };
92
93 if let Some(page_token) = request.page_token {
95 api_req.query_params.insert("page_token", page_token);
96 }
97 if let Some(page_size) = request.page_size {
98 api_req
99 .query_params
100 .insert("page_size", page_size.to_string());
101 }
102
103 let api_resp: BaseResponse<ListFileViewRecordsRespData> =
104 Transport::request(api_req, &self.config, option).await?;
105 api_resp.into_result()
106 }
107
108 pub async fn create_file(
114 &self,
115 request: CreateFileRequest,
116 option: Option<RequestOption>,
117 ) -> SDKResult<CreateFileRespData> {
118 let api_req = ApiRequest {
119 http_method: Method::POST,
120 api_path: DRIVE_V1_FILES.to_string(),
121 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
122 body: serde_json::to_vec(&request)?,
123 ..Default::default()
124 };
125
126 let api_resp: BaseResponse<CreateFileRespData> =
127 Transport::request(api_req, &self.config, option).await?;
128 api_resp.into_result()
129 }
130
131 pub async fn copy_file(
137 &self,
138 request: CopyFileRequest,
139 option: Option<RequestOption>,
140 ) -> SDKResult<CopyFileRespData> {
141 let body = serde_json::json!({
143 "name": request.name,
144 "type": request.copy_type,
145 "parent_token": request.parent_token
146 });
147
148 let api_req = ApiRequest {
149 http_method: Method::POST,
150 api_path: DRIVE_V1_FILE_COPY.replace("{}", &request.file_token),
151 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
152 body: serde_json::to_vec(&body)?,
153 ..Default::default()
154 };
155
156 let api_resp: BaseResponse<CopyFileRespData> =
157 Transport::request(api_req, &self.config, option).await?;
158 api_resp.into_result()
159 }
160
161 pub async fn delete_file(
167 &self,
168 request: DeleteFileRequest,
169 option: Option<RequestOption>,
170 ) -> SDKResult<DeleteFileRespData> {
171 let api_req = ApiRequest {
172 http_method: Method::DELETE,
173 api_path: DRIVE_V1_FILE_GET.replace("{}", &request.file_token),
174 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
175 ..Default::default()
176 };
177
178 let api_resp: BaseResponse<DeleteFileRespData> =
179 Transport::request(api_req, &self.config, option).await?;
180 api_resp.into_result()
181 }
182
183 pub async fn create_file_shortcut(
189 &self,
190 request: CreateFileShortcutRequest,
191 option: Option<RequestOption>,
192 ) -> SDKResult<CreateFileShortcutRespData> {
193 let api_req = ApiRequest {
194 http_method: Method::POST,
195 api_path: DRIVE_V1_FILES_CREATE_SHORTCUT.to_string(),
196 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
197 body: serde_json::to_vec(&request)?,
198 ..Default::default()
199 };
200
201 let api_resp: BaseResponse<CreateFileShortcutRespData> =
202 Transport::request(api_req, &self.config, option).await?;
203 api_resp.into_result()
204 }
205
206 pub async fn search_files(
212 &self,
213 request: SearchFilesRequest,
214 option: Option<RequestOption>,
215 ) -> SDKResult<SearchFilesRespData> {
216 let mut api_req = ApiRequest {
217 http_method: Method::GET,
218 api_path: DRIVE_V1_FILES_SEARCH.to_string(),
219 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
220 ..Default::default()
221 };
222
223 api_req
225 .query_params
226 .insert("search_key", request.search_key);
227 if let Some(count) = request.count {
228 api_req.query_params.insert("count", count.to_string());
229 }
230 if let Some(offset) = request.offset {
231 api_req.query_params.insert("offset", offset.to_string());
232 }
233 if let Some(owner_ids) = request.owner_ids {
234 api_req
235 .query_params
236 .insert("owner_ids", owner_ids.join(","));
237 }
238
239 let api_resp: BaseResponse<SearchFilesRespData> =
240 Transport::request(api_req, &self.config, option).await?;
241 api_resp.into_result()
242 }
243
244 pub async fn upload_prepare(
250 &self,
251 request: FileUploadPrepareRequest,
252 option: Option<RequestOption>,
253 ) -> SDKResult<FileUploadPrepareRespData> {
254 let api_req = ApiRequest {
255 http_method: Method::POST,
256 api_path: DRIVE_V1_FILES_UPLOAD_PREPARE.to_string(),
257 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
258 body: serde_json::to_vec(&request)?,
259 ..Default::default()
260 };
261
262 let api_resp: BaseResponse<FileUploadPrepareRespData> =
263 Transport::request(api_req, &self.config, option).await?;
264 api_resp.into_result()
265 }
266
267 pub async fn upload_part(
273 &self,
274 request: FileUploadPartRequest,
275 option: Option<RequestOption>,
276 ) -> SDKResult<FileUploadPartRespData> {
277 let mut api_req = request.api_req;
278 api_req.http_method = Method::POST;
279 api_req.api_path = DRIVE_V1_FILES_UPLOAD_PART.to_string();
280 api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];
281
282 let api_resp: BaseResponse<FileUploadPartRespData> =
283 Transport::request(api_req, &self.config, option).await?;
284 api_resp.into_result()
285 }
286
287 pub async fn upload_finish(
293 &self,
294 request: FileUploadFinishRequest,
295 option: Option<RequestOption>,
296 ) -> SDKResult<FileUploadFinishRespData> {
297 let api_req = ApiRequest {
298 http_method: Method::POST,
299 api_path: DRIVE_V1_FILES_UPLOAD_FINISH.to_string(),
300 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
301 body: serde_json::to_vec(&request)?,
302 ..Default::default()
303 };
304
305 let api_resp: BaseResponse<FileUploadFinishRespData> =
306 Transport::request(api_req, &self.config, option).await?;
307 api_resp.into_result()
308 }
309
310 pub async fn create_import_task(
316 &self,
317 request: CreateImportTaskRequest,
318 option: Option<RequestOption>,
319 ) -> SDKResult<CreateImportTaskRespData> {
320 let api_req = ApiRequest {
321 http_method: Method::POST,
322 api_path: DRIVE_V1_IMPORT_TASKS.to_string(),
323 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
324 body: serde_json::to_vec(&request)?,
325 ..Default::default()
326 };
327
328 let api_resp: BaseResponse<CreateImportTaskRespData> =
329 Transport::request(api_req, &self.config, option).await?;
330 api_resp.into_result()
331 }
332
333 pub async fn get_import_task(
339 &self,
340 request: GetImportTaskRequest,
341 option: Option<RequestOption>,
342 ) -> SDKResult<GetImportTaskRespData> {
343 let api_req = ApiRequest {
344 http_method: Method::GET,
345 api_path: DRIVE_V1_IMPORT_TASK_GET.replace("{}", &request.ticket),
346 supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
347 ..Default::default()
348 };
349
350 let api_resp: BaseResponse<GetImportTaskRespData> =
351 Transport::request(api_req, &self.config, option).await?;
352 api_resp.into_result()
353 }
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct GetFileMetaRequest {
361 pub request_docs: Vec<RequestDoc>,
363 pub with_url: Option<bool>,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct RequestDoc {
369 pub doc_token: String,
371 pub doc_type: String,
373}
374
375impl GetFileMetaRequest {
376 pub fn new(docs: Vec<(String, String)>) -> Self {
377 Self {
378 request_docs: docs
379 .into_iter()
380 .map(|(token, doc_type)| RequestDoc {
381 doc_token: token,
382 doc_type,
383 })
384 .collect(),
385 with_url: Some(true),
386 }
387 }
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
392pub struct GetFileMetaRespData {
393 pub metas: Vec<FileMeta>,
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
398pub struct FileMeta {
399 pub doc_token: String,
401 pub doc_type: String,
403 pub title: String,
405 pub owner_id: String,
407 pub create_time: String,
409 pub update_time: String,
411 pub url: Option<String>,
413}
414
415impl ApiResponseTrait for GetFileMetaRespData {
416 fn data_format() -> ResponseFormat {
417 ResponseFormat::Data
418 }
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
423pub struct GetFileStatisticsRequest {
424 pub file_token: String,
426}
427
428impl GetFileStatisticsRequest {
429 pub fn new(file_token: impl Into<String>) -> Self {
430 Self {
431 file_token: file_token.into(),
432 }
433 }
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
438pub struct GetFileStatisticsRespData {
439 pub uv: i64,
441 pub pv: i64,
443 pub like_count: i64,
445 pub comment_count: i64,
447}
448
449impl ApiResponseTrait for GetFileStatisticsRespData {
450 fn data_format() -> ResponseFormat {
451 ResponseFormat::Data
452 }
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
457pub struct ListFileViewRecordsRequest {
458 pub file_token: String,
460 pub page_token: Option<String>,
462 pub page_size: Option<i32>,
464}
465
466impl ListFileViewRecordsRequest {
467 pub fn new(file_token: impl Into<String>) -> Self {
468 Self {
469 file_token: file_token.into(),
470 page_token: None,
471 page_size: None,
472 }
473 }
474
475 pub fn with_page_token(mut self, page_token: impl Into<String>) -> Self {
476 self.page_token = Some(page_token.into());
477 self
478 }
479
480 pub fn with_page_size(mut self, page_size: i32) -> Self {
481 self.page_size = Some(page_size);
482 self
483 }
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
488pub struct ListFileViewRecordsRespData {
489 pub has_more: bool,
491 pub page_token: Option<String>,
493 pub items: Vec<FileViewRecord>,
495}
496
497#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
498pub struct FileViewRecord {
499 pub viewer_id: String,
501 pub viewer_name: String,
503 pub view_time: String,
505 pub view_device: String,
507}
508
509impl ApiResponseTrait for ListFileViewRecordsRespData {
510 fn data_format() -> ResponseFormat {
511 ResponseFormat::Data
512 }
513}
514
515#[derive(Debug, Clone, Serialize, Deserialize)]
517pub struct CreateFileRequest {
518 pub title: String,
520 #[serde(rename = "type")]
522 pub file_type: String,
523 pub parent_token: String,
525}
526
527impl CreateFileRequest {
528 pub fn new(
529 title: impl Into<String>,
530 file_type: impl Into<String>,
531 parent_token: impl Into<String>,
532 ) -> Self {
533 Self {
534 title: title.into(),
535 file_type: file_type.into(),
536 parent_token: parent_token.into(),
537 }
538 }
539}
540
541#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
543pub struct CreateFileRespData {
544 pub token: String,
546 pub url: String,
548}
549
550impl ApiResponseTrait for CreateFileRespData {
551 fn data_format() -> ResponseFormat {
552 ResponseFormat::Data
553 }
554}
555
556#[derive(Debug, Clone, Serialize, Deserialize)]
558pub struct CopyFileRequest {
559 pub file_token: String,
561 pub name: String,
563 #[serde(rename = "type")]
565 pub copy_type: String,
566 pub parent_token: String,
568}
569
570impl CopyFileRequest {
571 pub fn new(
572 file_token: impl Into<String>,
573 name: impl Into<String>,
574 parent_token: impl Into<String>,
575 ) -> Self {
576 Self {
577 file_token: file_token.into(),
578 name: name.into(),
579 copy_type: "copy".to_string(),
580 parent_token: parent_token.into(),
581 }
582 }
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct CopyFileRespData {
588 pub token: String,
590 pub url: String,
592}
593
594impl ApiResponseTrait for CopyFileRespData {
595 fn data_format() -> ResponseFormat {
596 ResponseFormat::Data
597 }
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize)]
602pub struct DeleteFileRequest {
603 pub file_token: String,
605}
606
607impl DeleteFileRequest {
608 pub fn new(file_token: impl Into<String>) -> Self {
609 Self {
610 file_token: file_token.into(),
611 }
612 }
613}
614
615#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct DeleteFileRespData {
618 pub task_id: Option<String>,
620}
621
622impl ApiResponseTrait for DeleteFileRespData {
623 fn data_format() -> ResponseFormat {
624 ResponseFormat::Data
625 }
626}
627
628#[derive(Debug, Clone, Serialize, Deserialize)]
630pub struct CreateFileShortcutRequest {
631 pub refer_entity: ReferEntity,
633 pub name: String,
635 pub parent_token: String,
637}
638
639#[derive(Debug, Clone, Serialize, Deserialize)]
640pub struct ReferEntity {
641 #[serde(rename = "type")]
643 pub entity_type: String,
644 pub token: String,
646}
647
648impl CreateFileShortcutRequest {
649 pub fn new(
650 file_type: impl Into<String>,
651 file_token: impl Into<String>,
652 name: impl Into<String>,
653 parent_token: impl Into<String>,
654 ) -> Self {
655 Self {
656 refer_entity: ReferEntity {
657 entity_type: file_type.into(),
658 token: file_token.into(),
659 },
660 name: name.into(),
661 parent_token: parent_token.into(),
662 }
663 }
664}
665
666#[derive(Debug, Clone, Serialize, Deserialize)]
668pub struct CreateFileShortcutRespData {
669 pub token: String,
671 pub url: String,
673}
674
675impl ApiResponseTrait for CreateFileShortcutRespData {
676 fn data_format() -> ResponseFormat {
677 ResponseFormat::Data
678 }
679}
680
681#[derive(Debug, Clone, Serialize, Deserialize)]
683pub struct SearchFilesRequest {
684 pub search_key: String,
686 pub count: Option<i32>,
688 pub offset: Option<i32>,
690 pub owner_ids: Option<Vec<String>>,
692}
693
694impl SearchFilesRequest {
695 pub fn new(search_key: impl Into<String>) -> Self {
696 Self {
697 search_key: search_key.into(),
698 count: None,
699 offset: None,
700 owner_ids: None,
701 }
702 }
703
704 pub fn with_count(mut self, count: i32) -> Self {
705 self.count = Some(count);
706 self
707 }
708
709 pub fn with_offset(mut self, offset: i32) -> Self {
710 self.offset = Some(offset);
711 self
712 }
713
714 pub fn with_owner_ids(mut self, owner_ids: Vec<String>) -> Self {
715 self.owner_ids = Some(owner_ids);
716 self
717 }
718}
719
720#[derive(Debug, Clone, Serialize, Deserialize)]
722pub struct SearchFilesRespData {
723 pub files: Vec<SearchFileItem>,
725}
726
727#[derive(Debug, Clone, Serialize, Deserialize)]
728pub struct SearchFileItem {
729 pub token: String,
731 pub name: String,
733 #[serde(rename = "type")]
735 pub file_type: String,
736 pub url: String,
738 pub owner_id: String,
740}
741
742impl ApiResponseTrait for SearchFilesRespData {
743 fn data_format() -> ResponseFormat {
744 ResponseFormat::Data
745 }
746}
747
748#[derive(Debug, Clone, Serialize, Deserialize)]
750pub struct FileUploadPrepareRequest {
751 pub file_name: String,
753 pub parent_token: String,
755 pub size: i64,
757 pub block_size: Option<i32>,
759 pub checksum: Option<String>,
761}
762
763impl FileUploadPrepareRequest {
764 pub fn new(file_name: impl Into<String>, parent_token: impl Into<String>, size: i64) -> Self {
765 Self {
766 file_name: file_name.into(),
767 parent_token: parent_token.into(),
768 size,
769 block_size: None,
770 checksum: None,
771 }
772 }
773
774 pub fn with_block_size(mut self, block_size: i32) -> Self {
775 self.block_size = Some(block_size);
776 self
777 }
778
779 pub fn with_checksum(mut self, checksum: impl Into<String>) -> Self {
780 self.checksum = Some(checksum.into());
781 self
782 }
783}
784
785#[derive(Debug, Clone, Serialize, Deserialize)]
787pub struct FileUploadPrepareRespData {
788 pub upload_id: String,
790 pub block_size: i32,
792 pub block_num: i32,
794}
795
796impl ApiResponseTrait for FileUploadPrepareRespData {
797 fn data_format() -> ResponseFormat {
798 ResponseFormat::Data
799 }
800}
801
802#[derive(Debug, Clone, Default, Serialize, Deserialize)]
804pub struct FileUploadPartRequest {
805 #[serde(skip)]
807 pub api_req: ApiRequest,
808 upload_id: String,
810 seq: i32,
812 size: i32,
814 checksum: Option<String>,
816}
817
818impl FileUploadPartRequest {
819 pub fn builder() -> FileUploadPartRequestBuilder {
820 FileUploadPartRequestBuilder::default()
821 }
822}
823
824#[derive(Default)]
826pub struct FileUploadPartRequestBuilder {
827 request: FileUploadPartRequest,
828}
829
830impl FileUploadPartRequestBuilder {
831 pub fn upload_id(mut self, upload_id: impl Into<String>) -> Self {
832 self.request.upload_id = upload_id.into();
833 self
834 }
835
836 pub fn seq(mut self, seq: i32) -> Self {
837 self.request.seq = seq;
838 self
839 }
840
841 pub fn size(mut self, size: i32) -> Self {
842 self.request.size = size;
843 self
844 }
845
846 pub fn checksum(mut self, checksum: impl Into<String>) -> Self {
847 self.request.checksum = Some(checksum.into());
848 self
849 }
850
851 pub fn file_chunk(mut self, chunk: Vec<u8>) -> Self {
852 self.request.api_req.file = chunk;
853 self
854 }
855
856 pub fn build(mut self) -> FileUploadPartRequest {
857 match serde_json::to_vec(&self.request) {
858 Ok(bytes) => {
859 self.request.api_req.body = bytes;
860 }
861 Err(e) => {
862 error!("Failed to serialize file upload part request: {}", e);
863 self.request.api_req.body = Vec::new();
864 }
865 }
866 self.request
867 }
868}
869
870impl_executable_builder_owned!(
871 FileUploadPartRequestBuilder,
872 FileService,
873 FileUploadPartRequest,
874 FileUploadPartRespData,
875 upload_part
876);
877
878#[derive(Debug, Clone, Serialize, Deserialize)]
880pub struct FileUploadPartRespData {
881 pub etag: String,
883}
884
885impl ApiResponseTrait for FileUploadPartRespData {
886 fn data_format() -> ResponseFormat {
887 ResponseFormat::Data
888 }
889}
890
891#[derive(Debug, Clone, Serialize, Deserialize)]
893pub struct FileUploadFinishRequest {
894 pub upload_id: String,
896 pub block_infos: Vec<FileBlockInfo>,
898}
899
900#[derive(Debug, Clone, Serialize, Deserialize)]
901pub struct FileBlockInfo {
902 pub etag: String,
904 pub seq: i32,
906}
907
908impl FileUploadFinishRequest {
909 pub fn new(upload_id: impl Into<String>, block_infos: Vec<FileBlockInfo>) -> Self {
910 Self {
911 upload_id: upload_id.into(),
912 block_infos,
913 }
914 }
915}
916
917#[derive(Debug, Clone, Serialize, Deserialize)]
919pub struct FileUploadFinishRespData {
920 pub file_token: String,
922}
923
924impl ApiResponseTrait for FileUploadFinishRespData {
925 fn data_format() -> ResponseFormat {
926 ResponseFormat::Data
927 }
928}
929
930#[derive(Debug, Clone, Serialize, Deserialize)]
932pub struct CreateImportTaskRequest {
933 pub file_extension: String,
935 pub file_token: String,
937 #[serde(rename = "type")]
939 pub import_type: String,
940 pub parent_token: String,
942 pub file_name: String,
944 pub parent_type: String,
946}
947
948impl CreateImportTaskRequest {
949 pub fn new(
950 file_extension: impl Into<String>,
951 file_token: impl Into<String>,
952 import_type: impl Into<String>,
953 parent_token: impl Into<String>,
954 file_name: impl Into<String>,
955 parent_type: impl Into<String>,
956 ) -> Self {
957 Self {
958 file_extension: file_extension.into(),
959 file_token: file_token.into(),
960 import_type: import_type.into(),
961 parent_token: parent_token.into(),
962 file_name: file_name.into(),
963 parent_type: parent_type.into(),
964 }
965 }
966}
967
968#[derive(Debug, Clone, Serialize, Deserialize)]
970pub struct CreateImportTaskRespData {
971 pub ticket: String,
973}
974
975impl ApiResponseTrait for CreateImportTaskRespData {
976 fn data_format() -> ResponseFormat {
977 ResponseFormat::Data
978 }
979}
980
981#[derive(Debug, Clone, Serialize, Deserialize)]
983pub struct GetImportTaskRequest {
984 pub ticket: String,
986}
987
988impl GetImportTaskRequest {
989 pub fn new(ticket: impl Into<String>) -> Self {
990 Self {
991 ticket: ticket.into(),
992 }
993 }
994}
995
996#[derive(Debug, Clone, Serialize, Deserialize)]
998pub struct GetImportTaskRespData {
999 pub result: ImportTaskResult,
1001}
1002
1003#[derive(Debug, Clone, Serialize, Deserialize)]
1004pub struct ImportTaskResult {
1005 #[serde(rename = "type")]
1007 pub task_type: String,
1008 pub ticket: String,
1010 pub job_status: i32,
1012 pub job_error_msg: Option<String>,
1014 pub token: Option<String>,
1016 pub url: Option<String>,
1018}
1019
1020impl ApiResponseTrait for GetImportTaskRespData {
1021 fn data_format() -> ResponseFormat {
1022 ResponseFormat::Data
1023 }
1024}
1025
1026impl Service for FileService {
1027 fn config(&self) -> &Config {
1028 &self.config
1029 }
1030
1031 fn service_name() -> &'static str {
1032 "file"
1033 }
1034
1035 fn service_version() -> &'static str {
1036 "v1"
1037 }
1038}
1039
1040#[cfg(test)]
1041mod tests {
1042 use super::*;
1043 use crate::core::api_resp::ResponseFormat;
1044 use rstest::rstest;
1045
1046 fn create_test_config() -> Config {
1047 Config::builder()
1048 .app_id("test_app_id")
1049 .app_secret("test_app_secret")
1050 .build()
1051 }
1052
1053 fn create_test_service() -> FileService {
1054 FileService::new(create_test_config())
1055 }
1056
1057 #[test]
1058 fn test_file_service_new() {
1059 let config = create_test_config();
1060 let service = FileService::new(config.clone());
1061
1062 assert_eq!(service.config.app_id, config.app_id);
1063 assert_eq!(FileService::service_name(), "file");
1064 assert_eq!(FileService::service_version(), "v1");
1065 }
1066
1067 #[test]
1068 fn test_service_trait_implementation() {
1069 let service = create_test_service();
1070 assert_eq!(service.config().app_id, "test_app_id");
1071 assert_eq!(FileService::service_name(), "file");
1072 assert_eq!(FileService::service_version(), "v1");
1073 }
1074
1075 #[test]
1078 fn test_get_file_meta_request() {
1079 let docs = vec![
1080 ("file_token_1".to_string(), "doc".to_string()),
1081 ("file_token_2".to_string(), "sheet".to_string()),
1082 ];
1083 let request = GetFileMetaRequest::new(docs.clone());
1084
1085 assert_eq!(request.request_docs.len(), 2);
1086 assert_eq!(request.request_docs[0].doc_token, "file_token_1");
1087 assert_eq!(request.request_docs[0].doc_type, "doc");
1088 assert_eq!(request.request_docs[1].doc_token, "file_token_2");
1089 assert_eq!(request.request_docs[1].doc_type, "sheet");
1090 assert_eq!(request.with_url, Some(true));
1091 }
1092
1093 #[test]
1094 fn test_get_file_meta_request_serialization() {
1095 let docs = vec![("test_token".to_string(), "doc".to_string())];
1096 let request = GetFileMetaRequest::new(docs);
1097
1098 let json = serde_json::to_string(&request).unwrap();
1099 assert!(json.contains("request_docs"));
1100 assert!(json.contains("with_url"));
1101 assert!(json.contains("test_token"));
1102 assert!(json.contains("doc"));
1103
1104 let deserialized: GetFileMetaRequest = serde_json::from_str(&json).unwrap();
1105 assert_eq!(deserialized.request_docs.len(), 1);
1106 assert_eq!(deserialized.request_docs[0].doc_token, "test_token");
1107 }
1108
1109 #[test]
1110 fn test_get_file_meta_resp_data_api_response_trait() {
1111 assert_eq!(GetFileMetaRespData::data_format(), ResponseFormat::Data);
1112 }
1113
1114 #[test]
1115 fn test_get_file_statistics_request() {
1116 let request = GetFileStatisticsRequest::new("test_file_token");
1117 assert_eq!(request.file_token, "test_file_token");
1118
1119 let request2 = GetFileStatisticsRequest::new("another_token".to_string());
1120 assert_eq!(request2.file_token, "another_token");
1121 }
1122
1123 #[test]
1124 fn test_get_file_statistics_resp_data() {
1125 let resp_data = GetFileStatisticsRespData {
1126 uv: 100,
1127 pv: 250,
1128 like_count: 15,
1129 comment_count: 8,
1130 };
1131
1132 assert_eq!(resp_data.uv, 100);
1133 assert_eq!(resp_data.pv, 250);
1134 assert_eq!(resp_data.like_count, 15);
1135 assert_eq!(resp_data.comment_count, 8);
1136 assert_eq!(
1137 GetFileStatisticsRespData::data_format(),
1138 ResponseFormat::Data
1139 );
1140
1141 let json = serde_json::to_string(&resp_data).unwrap();
1143 let deserialized: GetFileStatisticsRespData = serde_json::from_str(&json).unwrap();
1144 assert_eq!(deserialized.uv, resp_data.uv);
1145 assert_eq!(deserialized.pv, resp_data.pv);
1146 }
1147
1148 #[test]
1149 fn test_list_file_view_records_request_builder() {
1150 let request = ListFileViewRecordsRequest::new("test_token")
1151 .with_page_token("next_page")
1152 .with_page_size(20);
1153
1154 assert_eq!(request.file_token, "test_token");
1155 assert_eq!(request.page_token, Some("next_page".to_string()));
1156 assert_eq!(request.page_size, Some(20));
1157 }
1158
1159 #[test]
1160 fn test_list_file_view_records_request_minimal() {
1161 let request = ListFileViewRecordsRequest::new("minimal_token");
1162 assert_eq!(request.file_token, "minimal_token");
1163 assert_eq!(request.page_token, None);
1164 assert_eq!(request.page_size, None);
1165 }
1166
1167 #[test]
1168 fn test_list_file_view_records_resp_data() {
1169 let records = vec![
1170 FileViewRecord {
1171 viewer_id: "user1".to_string(),
1172 viewer_name: "John Doe".to_string(),
1173 view_time: "2023-12-01T10:00:00Z".to_string(),
1174 view_device: "web".to_string(),
1175 },
1176 FileViewRecord {
1177 viewer_id: "user2".to_string(),
1178 viewer_name: "Jane Smith".to_string(),
1179 view_time: "2023-12-01T11:00:00Z".to_string(),
1180 view_device: "mobile".to_string(),
1181 },
1182 ];
1183
1184 let resp_data = ListFileViewRecordsRespData {
1185 has_more: true,
1186 page_token: Some("next_token".to_string()),
1187 items: records.clone(),
1188 };
1189
1190 assert!(resp_data.has_more);
1191 assert_eq!(resp_data.page_token, Some("next_token".to_string()));
1192 assert_eq!(resp_data.items.len(), 2);
1193 assert_eq!(resp_data.items[0].viewer_name, "John Doe");
1194 assert_eq!(resp_data.items[1].view_device, "mobile");
1195 assert_eq!(
1196 ListFileViewRecordsRespData::data_format(),
1197 ResponseFormat::Data
1198 );
1199 }
1200
1201 #[test]
1202 fn test_create_file_request() {
1203 let request = CreateFileRequest::new("My Document", "doc", "parent_folder_token");
1204
1205 assert_eq!(request.title, "My Document");
1206 assert_eq!(request.file_type, "doc");
1207 assert_eq!(request.parent_token, "parent_folder_token");
1208 }
1209
1210 #[test]
1211 fn test_create_file_request_serialization() {
1212 let request = CreateFileRequest::new("Test File", "sheet", "folder123");
1213 let json = serde_json::to_string(&request).unwrap();
1214
1215 assert!(json.contains("Test File"));
1216 assert!(json.contains("\"type\":\"sheet\""));
1217 assert!(json.contains("parent_token"));
1218
1219 let deserialized: CreateFileRequest = serde_json::from_str(&json).unwrap();
1220 assert_eq!(deserialized.title, "Test File");
1221 assert_eq!(deserialized.file_type, "sheet");
1222 }
1223
1224 #[test]
1225 fn test_copy_file_request() {
1226 let request = CopyFileRequest::new("source_token", "Copy of Document", "target_folder");
1227
1228 assert_eq!(request.file_token, "source_token");
1229 assert_eq!(request.name, "Copy of Document");
1230 assert_eq!(request.copy_type, "copy");
1231 assert_eq!(request.parent_token, "target_folder");
1232 }
1233
1234 #[test]
1235 fn test_delete_file_request() {
1236 let request = DeleteFileRequest::new("file_to_delete");
1237 assert_eq!(request.file_token, "file_to_delete");
1238
1239 let request2 = DeleteFileRequest::new("another_file".to_string());
1240 assert_eq!(request2.file_token, "another_file");
1241 }
1242
1243 #[test]
1244 fn test_create_file_shortcut_request() {
1245 let request = CreateFileShortcutRequest::new(
1246 "doc",
1247 "original_file_token",
1248 "Shortcut to Document",
1249 "shortcut_folder",
1250 );
1251
1252 assert_eq!(request.refer_entity.entity_type, "doc");
1253 assert_eq!(request.refer_entity.token, "original_file_token");
1254 assert_eq!(request.name, "Shortcut to Document");
1255 assert_eq!(request.parent_token, "shortcut_folder");
1256 }
1257
1258 #[test]
1259 fn test_search_files_request_builder() {
1260 let request = SearchFilesRequest::new("important documents")
1261 .with_count(50)
1262 .with_offset(100)
1263 .with_owner_ids(vec!["user1".to_string(), "user2".to_string()]);
1264
1265 assert_eq!(request.search_key, "important documents");
1266 assert_eq!(request.count, Some(50));
1267 assert_eq!(request.offset, Some(100));
1268 assert_eq!(
1269 request.owner_ids,
1270 Some(vec!["user1".to_string(), "user2".to_string()])
1271 );
1272 }
1273
1274 #[test]
1275 fn test_search_files_request_minimal() {
1276 let request = SearchFilesRequest::new("test");
1277 assert_eq!(request.search_key, "test");
1278 assert_eq!(request.count, None);
1279 assert_eq!(request.offset, None);
1280 assert_eq!(request.owner_ids, None);
1281 }
1282
1283 #[test]
1284 fn test_file_upload_prepare_request() {
1285 let request = FileUploadPrepareRequest::new("document.pdf", "upload_folder", 1024000)
1286 .with_block_size(4096)
1287 .with_checksum("sha256:abcdef123456");
1288
1289 assert_eq!(request.file_name, "document.pdf");
1290 assert_eq!(request.parent_token, "upload_folder");
1291 assert_eq!(request.size, 1024000);
1292 assert_eq!(request.block_size, Some(4096));
1293 assert_eq!(request.checksum, Some("sha256:abcdef123456".to_string()));
1294 }
1295
1296 #[test]
1297 fn test_file_upload_prepare_request_minimal() {
1298 let request = FileUploadPrepareRequest::new("simple.txt", "folder", 500);
1299 assert_eq!(request.file_name, "simple.txt");
1300 assert_eq!(request.size, 500);
1301 assert_eq!(request.block_size, None);
1302 assert_eq!(request.checksum, None);
1303 }
1304
1305 #[test]
1306 fn test_file_upload_part_request_builder() {
1307 let test_chunk = vec![1, 2, 3, 4, 5];
1308 let request = FileUploadPartRequest::builder()
1309 .upload_id("upload_123")
1310 .seq(1)
1311 .size(5)
1312 .checksum("chunk_checksum")
1313 .file_chunk(test_chunk.clone())
1314 .build();
1315
1316 assert_eq!(request.upload_id, "upload_123");
1317 assert_eq!(request.seq, 1);
1318 assert_eq!(request.size, 5);
1319 assert_eq!(request.checksum, Some("chunk_checksum".to_string()));
1320 assert_eq!(request.api_req.file, test_chunk);
1321 }
1322
1323 #[test]
1324 fn test_file_upload_part_request_builder_minimal() {
1325 let request = FileUploadPartRequest::builder()
1326 .upload_id("minimal_upload")
1327 .seq(0)
1328 .size(100)
1329 .build();
1330
1331 assert_eq!(request.upload_id, "minimal_upload");
1332 assert_eq!(request.seq, 0);
1333 assert_eq!(request.size, 100);
1334 assert_eq!(request.checksum, None);
1335 }
1336
1337 #[test]
1338 fn test_file_upload_finish_request() {
1339 let block_infos = vec![
1340 FileBlockInfo {
1341 etag: "etag1".to_string(),
1342 seq: 1,
1343 },
1344 FileBlockInfo {
1345 etag: "etag2".to_string(),
1346 seq: 2,
1347 },
1348 ];
1349 let request = FileUploadFinishRequest::new("upload_123", block_infos.clone());
1350
1351 assert_eq!(request.upload_id, "upload_123");
1352 assert_eq!(request.block_infos.len(), 2);
1353 assert_eq!(request.block_infos[0].etag, "etag1");
1354 assert_eq!(request.block_infos[1].seq, 2);
1355 }
1356
1357 #[test]
1358 fn test_create_import_task_request() {
1359 let request = CreateImportTaskRequest::new(
1360 "pdf",
1361 "source_file_token",
1362 "import_type_doc",
1363 "target_folder",
1364 "imported_document.docx",
1365 "folder",
1366 );
1367
1368 assert_eq!(request.file_extension, "pdf");
1369 assert_eq!(request.file_token, "source_file_token");
1370 assert_eq!(request.import_type, "import_type_doc");
1371 assert_eq!(request.parent_token, "target_folder");
1372 assert_eq!(request.file_name, "imported_document.docx");
1373 assert_eq!(request.parent_type, "folder");
1374 }
1375
1376 #[test]
1377 fn test_get_import_task_request() {
1378 let request = GetImportTaskRequest::new("task_ticket_123");
1379 assert_eq!(request.ticket, "task_ticket_123");
1380
1381 let request2 = GetImportTaskRequest::new("another_ticket".to_string());
1382 assert_eq!(request2.ticket, "another_ticket");
1383 }
1384
1385 #[rstest]
1388 #[case(GetFileMetaRespData { metas: vec![] })]
1389 #[case(GetFileStatisticsRespData { uv: 0, pv: 0, like_count: 0, comment_count: 0 })]
1390 #[case(ListFileViewRecordsRespData { has_more: false, page_token: None, items: vec![] })]
1391 #[case(CreateFileRespData { token: "test".to_string(), url: "http://test.com".to_string() })]
1392 fn test_response_data_serialization<T>(#[case] data: T)
1393 where
1394 T: serde::Serialize + for<'de> serde::Deserialize<'de> + PartialEq + std::fmt::Debug,
1395 {
1396 let json = serde_json::to_string(&data).unwrap();
1397 let deserialized: T = serde_json::from_str(&json).unwrap();
1398 assert_eq!(data, deserialized);
1399 }
1400
1401 #[test]
1402 fn test_file_meta_serialization() {
1403 let file_meta = FileMeta {
1404 doc_token: "test_token".to_string(),
1405 doc_type: "doc".to_string(),
1406 title: "Test Document".to_string(),
1407 owner_id: "owner123".to_string(),
1408 create_time: "2023-01-01T00:00:00Z".to_string(),
1409 update_time: "2023-01-02T00:00:00Z".to_string(),
1410 url: Some("https://example.com/doc".to_string()),
1411 };
1412
1413 let json = serde_json::to_string(&file_meta).unwrap();
1414 let deserialized: FileMeta = serde_json::from_str(&json).unwrap();
1415
1416 assert_eq!(deserialized.doc_token, file_meta.doc_token);
1417 assert_eq!(deserialized.title, file_meta.title);
1418 assert_eq!(deserialized.url, file_meta.url);
1419 }
1420
1421 #[test]
1422 fn test_search_file_item_serialization() {
1423 let item = SearchFileItem {
1424 token: "file_token".to_string(),
1425 name: "Important File".to_string(),
1426 file_type: "doc".to_string(),
1427 url: "https://example.com/file".to_string(),
1428 owner_id: "user123".to_string(),
1429 };
1430
1431 let json = serde_json::to_string(&item).unwrap();
1432 assert!(json.contains("\"type\":\"doc\""));
1433
1434 let deserialized: SearchFileItem = serde_json::from_str(&json).unwrap();
1435 assert_eq!(deserialized.file_type, "doc");
1436 assert_eq!(deserialized.name, "Important File");
1437 }
1438
1439 #[test]
1440 fn test_import_task_result_serialization() {
1441 let result = ImportTaskResult {
1442 task_type: "import".to_string(),
1443 ticket: "task_123".to_string(),
1444 job_status: 1,
1445 job_error_msg: Some("Error occurred".to_string()),
1446 token: Some("result_token".to_string()),
1447 url: Some("https://result.com".to_string()),
1448 };
1449
1450 let json = serde_json::to_string(&result).unwrap();
1451 assert!(json.contains("\"type\":\"import\""));
1452
1453 let deserialized: ImportTaskResult = serde_json::from_str(&json).unwrap();
1454 assert_eq!(deserialized.task_type, "import");
1455 assert_eq!(deserialized.job_status, 1);
1456 assert_eq!(
1457 deserialized.job_error_msg,
1458 Some("Error occurred".to_string())
1459 );
1460 }
1461
1462 #[test]
1465 fn test_empty_file_meta_request() {
1466 let request = GetFileMetaRequest::new(vec![]);
1467 assert_eq!(request.request_docs.len(), 0);
1468 assert_eq!(request.with_url, Some(true));
1469 }
1470
1471 #[test]
1472 fn test_large_file_upload_prepare() {
1473 let large_size = i64::MAX;
1474 let request = FileUploadPrepareRequest::new("huge_file.dat", "folder", large_size);
1475 assert_eq!(request.size, large_size);
1476 }
1477
1478 #[test]
1479 fn test_file_upload_part_zero_size() {
1480 let request = FileUploadPartRequest::builder()
1481 .upload_id("test")
1482 .seq(0)
1483 .size(0)
1484 .build();
1485 assert_eq!(request.size, 0);
1486 }
1487
1488 #[test]
1489 fn test_search_files_empty_search_key() {
1490 let request = SearchFilesRequest::new("");
1491 assert_eq!(request.search_key, "");
1492 }
1493
1494 #[test]
1495 fn test_search_files_negative_values() {
1496 let request = SearchFilesRequest::new("test")
1497 .with_count(-1)
1498 .with_offset(-10);
1499 assert_eq!(request.count, Some(-1));
1500 assert_eq!(request.offset, Some(-10));
1501 }
1502
1503 #[test]
1504 fn test_list_file_view_records_empty_response() {
1505 let resp_data = ListFileViewRecordsRespData {
1506 has_more: false,
1507 page_token: None,
1508 items: vec![],
1509 };
1510 assert!(!resp_data.has_more);
1511 assert_eq!(resp_data.items.len(), 0);
1512 }
1513
1514 #[test]
1515 fn test_file_upload_finish_empty_blocks() {
1516 let request = FileUploadFinishRequest::new("upload_id", vec![]);
1517 assert_eq!(request.block_infos.len(), 0);
1518 }
1519
1520 #[rstest]
1523 #[case(GetFileMetaRespData::data_format(), ResponseFormat::Data)]
1524 #[case(GetFileStatisticsRespData::data_format(), ResponseFormat::Data)]
1525 #[case(ListFileViewRecordsRespData::data_format(), ResponseFormat::Data)]
1526 #[case(CreateFileRespData::data_format(), ResponseFormat::Data)]
1527 #[case(CopyFileRespData::data_format(), ResponseFormat::Data)]
1528 #[case(DeleteFileRespData::data_format(), ResponseFormat::Data)]
1529 #[case(CreateFileShortcutRespData::data_format(), ResponseFormat::Data)]
1530 #[case(SearchFilesRespData::data_format(), ResponseFormat::Data)]
1531 #[case(FileUploadPrepareRespData::data_format(), ResponseFormat::Data)]
1532 #[case(FileUploadPartRespData::data_format(), ResponseFormat::Data)]
1533 #[case(FileUploadFinishRespData::data_format(), ResponseFormat::Data)]
1534 #[case(CreateImportTaskRespData::data_format(), ResponseFormat::Data)]
1535 #[case(GetImportTaskRespData::data_format(), ResponseFormat::Data)]
1536 fn test_api_response_trait_format(
1537 #[case] actual: ResponseFormat,
1538 #[case] expected: ResponseFormat,
1539 ) {
1540 assert_eq!(actual, expected);
1541 }
1542
1543 #[test]
1546 fn test_list_file_view_records_builder_chain() {
1547 let request = ListFileViewRecordsRequest::new("token")
1548 .with_page_token("page1")
1549 .with_page_size(25)
1550 .with_page_token("page2"); assert_eq!(request.page_token, Some("page2".to_string()));
1553 assert_eq!(request.page_size, Some(25));
1554 }
1555
1556 #[test]
1557 fn test_search_files_builder_chain() {
1558 let owners = vec![
1559 "user1".to_string(),
1560 "user2".to_string(),
1561 "user3".to_string(),
1562 ];
1563 let request = SearchFilesRequest::new("documents")
1564 .with_count(100)
1565 .with_offset(50)
1566 .with_owner_ids(owners.clone())
1567 .with_count(200); assert_eq!(request.count, Some(200));
1570 assert_eq!(request.offset, Some(50));
1571 assert_eq!(request.owner_ids, Some(owners));
1572 }
1573
1574 #[test]
1575 fn test_file_upload_prepare_builder_chain() {
1576 let request = FileUploadPrepareRequest::new("file.dat", "folder", 1000)
1577 .with_block_size(512)
1578 .with_checksum("checksum1")
1579 .with_block_size(1024) .with_checksum("checksum2"); assert_eq!(request.block_size, Some(1024));
1583 assert_eq!(request.checksum, Some("checksum2".to_string()));
1584 }
1585
1586 #[test]
1589 fn test_unicode_file_names() {
1590 let request = CreateFileRequest::new("文档测试🚀", "doc", "folder");
1591 assert_eq!(request.title, "文档测试🚀");
1592
1593 let json = serde_json::to_string(&request).unwrap();
1594 let deserialized: CreateFileRequest = serde_json::from_str(&json).unwrap();
1595 assert_eq!(deserialized.title, "文档测试🚀");
1596 }
1597
1598 #[test]
1599 fn test_special_characters_in_search() {
1600 let request = SearchFilesRequest::new("file@#$%^&*()[]{}");
1601 assert_eq!(request.search_key, "file@#$%^&*()[]{}");
1602 }
1603
1604 #[test]
1605 fn test_long_file_names() {
1606 let long_name = "a".repeat(1000);
1607 let request = CreateFileRequest::new(&long_name, "doc", "folder");
1608 assert_eq!(request.title.len(), 1000);
1609 }
1610
1611 #[test]
1614 fn test_file_upload_part_request_default() {
1615 let request = FileUploadPartRequest::default();
1616 assert_eq!(request.upload_id, "");
1617 assert_eq!(request.seq, 0);
1618 assert_eq!(request.size, 0);
1619 assert_eq!(request.checksum, None);
1620 }
1621
1622 #[test]
1623 fn test_request_cloning() {
1624 let original = CreateFileRequest::new("Original", "doc", "folder");
1625 let cloned = original.clone();
1626
1627 assert_eq!(original.title, cloned.title);
1628 assert_eq!(original.file_type, cloned.file_type);
1629 assert_eq!(original.parent_token, cloned.parent_token);
1630 }
1631
1632 #[test]
1635 fn test_file_upload_part_builder_serialization_error() {
1636 let request = FileUploadPartRequest::builder()
1638 .upload_id("test")
1639 .seq(1)
1640 .size(0)
1641 .build();
1642
1643 assert_eq!(request.upload_id, "test");
1645 assert_eq!(request.seq, 1);
1646 }
1647
1648 #[test]
1649 fn test_api_response_trait_consistency() {
1650 let formats = vec![
1652 GetFileMetaRespData::data_format(),
1653 GetFileStatisticsRespData::data_format(),
1654 CreateFileRespData::data_format(),
1655 DeleteFileRespData::data_format(),
1656 ];
1657
1658 for format in formats {
1659 assert_eq!(format, ResponseFormat::Data);
1660 }
1661 }
1662}