open-lark 0.13.0

Enterprise-grade Lark/Feishu Open API SDK with comprehensive Chinese documentation and advanced error handling
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
use reqwest::Method;
use serde::{Deserialize, Serialize};

use crate::{
    core::{
        api_req::ApiRequest,
        api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
        config::Config,
        constants::AccessTokenType,
        http::Transport,
        req_option::RequestOption,
        SDKResult,
    },
    impl_executable_builder_owned,
};

/// 文件夹服务
pub struct FolderService {
    config: Config,
}

impl FolderService {
    pub fn new(config: Config) -> Self {
        Self { config }
    }

    /// 获取我的空间(root folder)元数据
    ///
    /// 该接口用于根据用户的访问凭证获取用户的根目录信息,包括根目录的token等。
    ///
    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/get-root-folder-meta>
    pub async fn get_root_folder_meta(
        &self,
        option: Option<RequestOption>,
    ) -> SDKResult<BaseResponse<GetRootFolderMetaRespData>> {
        let api_req = ApiRequest {
            http_method: Method::GET,
            api_path: "/open-apis/drive/v1/folders/root_folder_meta".to_string(),
            supported_access_token_types: vec![AccessTokenType::User],
            ..Default::default()
        };

        let api_resp = Transport::request(api_req, &self.config, option).await?;
        Ok(api_resp)
    }

    /// 获取文件夹中的文件清单
    ///
    /// 该接口用于根据文件夹的token获取文件夹中的文件清单。
    ///
    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/list>
    pub async fn list_files(
        &self,
        request: ListFilesRequest,
        option: Option<RequestOption>,
    ) -> SDKResult<BaseResponse<ListFilesRespData>> {
        let mut api_req = ApiRequest {
            http_method: Method::GET,
            api_path: format!(
                "/open-apis/drive/v1/folders/{}/children",
                request.folder_token
            ),
            ..Default::default()
        };
        api_req.supported_access_token_types = vec![AccessTokenType::User, AccessTokenType::Tenant];

        // 添加查询参数
        if let Some(page_token) = request.page_token {
            api_req
                .query_params
                .insert("page_token".to_string(), page_token);
        }
        if let Some(page_size) = request.page_size {
            api_req
                .query_params
                .insert("page_size".to_string(), page_size.to_string());
        }
        if let Some(order_by) = request.order_by {
            api_req
                .query_params
                .insert("order_by".to_string(), order_by);
        }
        if let Some(direction) = request.direction {
            api_req
                .query_params
                .insert("direction".to_string(), direction);
        }

        let api_resp = Transport::request(api_req, &self.config, option).await?;
        Ok(api_resp)
    }

    /// 获取文件夹元数据
    ///
    /// 该接口用于根据文件夹的token获取文件夹的详细元数据信息。
    ///
    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/get-folder-meta>
    pub async fn get_folder_meta(
        &self,
        request: GetFolderMetaRequest,
        option: Option<RequestOption>,
    ) -> SDKResult<BaseResponse<GetFolderMetaRespData>> {
        let api_req = ApiRequest {
            http_method: Method::GET,
            api_path: format!("/open-apis/drive/v1/folders/{}", request.folder_token),
            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
            ..Default::default()
        };

        let api_resp = Transport::request(api_req, &self.config, option).await?;
        Ok(api_resp)
    }

    /// 新建文件夹
    ///
    /// 该接口用于根据父文件夹的token在其中创建一个新的空文件夹。
    ///
    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/create_folder>
    pub async fn create_folder(
        &self,
        request: CreateFolderRequest,
        option: Option<RequestOption>,
    ) -> SDKResult<BaseResponse<CreateFolderRespData>> {
        let api_req = ApiRequest {
            http_method: Method::POST,
            api_path: "/open-apis/drive/v1/folders".to_string(),
            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
            body: serde_json::to_vec(&request)?,
            ..Default::default()
        };

        let api_resp = Transport::request(api_req, &self.config, option).await?;
        Ok(api_resp)
    }

    /// 移动或删除文件夹
    ///
    /// 该接口用于根据文件夹的token移动或删除文件夹。
    ///
    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/folder/move-delete-folder>
    pub async fn move_or_delete_folder(
        &self,
        request: MoveOrDeleteFolderRequest,
        option: Option<RequestOption>,
    ) -> SDKResult<BaseResponse<MoveOrDeleteFolderRespData>> {
        let mut api_req = ApiRequest {
            http_method: Method::POST,
            api_path: format!("/open-apis/drive/v1/folders/{}/move", request.folder_token),
            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
            ..Default::default()
        };

        // 构建请求体,只包含需要的字段
        let body = serde_json::json!({
            "type": request.operation_type,
            "parent_token": request.parent_token
        });
        api_req.body = serde_json::to_vec(&body)?;

        let api_resp = Transport::request(api_req, &self.config, option).await?;
        Ok(api_resp)
    }

    /// 查询异步任务状态
    ///
    /// 该接口用于查询异步任务的执行状态,如移动或删除文件夹等操作。
    ///
    /// <https://open.feishu.cn/document/server-docs/docs/drive-v1/file/async-task/task_check>
    pub async fn check_async_task(
        &self,
        request: CheckAsyncTaskRequest,
        option: Option<RequestOption>,
    ) -> SDKResult<BaseResponse<CheckAsyncTaskRespData>> {
        let api_req = ApiRequest {
            http_method: Method::GET,
            api_path: format!("/open-apis/drive/v1/tasks/{}", request.task_id),
            supported_access_token_types: vec![AccessTokenType::User, AccessTokenType::Tenant],
            ..Default::default()
        };

        let api_resp = Transport::request(api_req, &self.config, option).await?;
        Ok(api_resp)
    }
}

/// 获取我的空间(root folder)元数据响应数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetRootFolderMetaRespData {
    /// 用户空间的根目录 token
    pub token: String,
    /// 用户 ID
    pub user_id: String,
}

impl ApiResponseTrait for GetRootFolderMetaRespData {
    fn data_format() -> ResponseFormat {
        ResponseFormat::Data
    }
}

/// 获取文件夹中的文件清单请求参数
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ListFilesRequest {
    /// 文件夹的token
    pub folder_token: String,
    /// 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果
    pub page_token: Option<String>,
    /// 分页大小,最大200
    pub page_size: Option<i32>,
    /// 排序字段,支持:创建时间(created_time)、修改时间(edited_time)、文件类型(file_type)、大小(size)
    pub order_by: Option<String>,
    /// 排序方向,支持:升序(ASC)、降序(DESC)
    pub direction: Option<String>,
}

impl ListFilesRequest {
    pub fn new(folder_token: impl Into<String>) -> Self {
        Self {
            folder_token: folder_token.into(),
            ..Default::default()
        }
    }

    pub fn builder() -> ListFilesRequestBuilder {
        ListFilesRequestBuilder::default()
    }
}

/// 获取文件夹中的文件清单请求构建器
#[derive(Debug, Clone, Default)]
pub struct ListFilesRequestBuilder {
    request: ListFilesRequest,
}

impl ListFilesRequestBuilder {
    pub fn folder_token(mut self, folder_token: impl Into<String>) -> Self {
        self.request.folder_token = folder_token.into();
        self
    }

    pub fn page_token(mut self, page_token: impl Into<String>) -> Self {
        self.request.page_token = Some(page_token.into());
        self
    }

    pub fn page_size(mut self, page_size: i32) -> Self {
        self.request.page_size = Some(page_size);
        self
    }

    pub fn order_by(mut self, order_by: impl Into<String>) -> Self {
        self.request.order_by = Some(order_by.into());
        self
    }

    pub fn direction(mut self, direction: impl Into<String>) -> Self {
        self.request.direction = Some(direction.into());
        self
    }

    pub fn build(self) -> ListFilesRequest {
        self.request
    }
}

/// 获取文件夹中的文件清单响应数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListFilesRespData {
    /// 是否还有更多项
    pub has_more: bool,
    /// 分页标记,当 has_more 为 true 时,会返回新的 page_token,否则不返回 page_token
    pub page_token: Option<String>,
    /// 文件清单
    pub files: Vec<DriveFile>,
}

impl ApiResponseTrait for ListFilesRespData {
    fn data_format() -> ResponseFormat {
        ResponseFormat::Data
    }
}

/// 驱动文件信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriveFile {
    /// 文件的token
    pub token: String,
    /// 文件名
    pub name: String,
    /// 文件类型
    #[serde(rename = "type")]
    pub file_type: String,
    /// 父文件夹token
    pub parent_token: Option<String>,
    /// 文件链接
    pub url: Option<String>,
    /// 文件短链接
    pub short_url: Option<String>,
    /// 文件大小(字节)
    pub size: Option<i64>,
    /// 文件mime类型
    pub mime_type: Option<String>,
    /// 创建时间
    pub created_time: Option<String>,
    /// 修改时间
    pub modified_time: Option<String>,
    /// 拥有者id
    pub owner_id: Option<String>,
}

/// 获取文件夹元数据请求参数
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetFolderMetaRequest {
    /// 文件夹的token
    pub folder_token: String,
}

impl GetFolderMetaRequest {
    pub fn new(folder_token: impl Into<String>) -> Self {
        Self {
            folder_token: folder_token.into(),
        }
    }
}

/// 获取文件夹元数据响应数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetFolderMetaRespData {
    /// 文件夹token
    pub token: String,
    /// 文件夹ID
    pub id: String,
    /// 文件夹名称
    pub name: String,
    /// 父文件夹token
    pub parent_token: Option<String>,
    /// 拥有者ID
    pub owner_id: String,
    /// 创建者ID
    pub creator_id: Option<String>,
    /// 创建时间
    pub create_time: String,
    /// 修改时间
    pub edit_time: String,
    /// 文件夹描述
    pub description: Option<String>,
    /// 文件夹链接
    pub url: String,
}

impl ApiResponseTrait for GetFolderMetaRespData {
    fn data_format() -> ResponseFormat {
        ResponseFormat::Data
    }
}

/// 新建文件夹请求参数
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateFolderRequest {
    /// 文件夹名称
    pub name: String,
    /// 父文件夹token
    pub parent_token: String,
}

impl CreateFolderRequest {
    pub fn new(name: impl Into<String>, parent_token: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            parent_token: parent_token.into(),
        }
    }
}

/// 新建文件夹响应数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateFolderRespData {
    /// 新创建文件夹的token
    pub token: String,
    /// 新创建文件夹的链接
    pub url: String,
}

impl ApiResponseTrait for CreateFolderRespData {
    fn data_format() -> ResponseFormat {
        ResponseFormat::Data
    }
}

/// 移动或删除文件夹请求参数
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MoveOrDeleteFolderRequest {
    /// 文件夹token
    pub folder_token: String,
    /// 操作类型,move: 移动,delete: 删除
    #[serde(rename = "type")]
    pub operation_type: String,
    /// 移动的目标父文件夹token(删除操作时可以为空)
    pub parent_token: Option<String>,
}

impl MoveOrDeleteFolderRequest {
    /// 创建移动文件夹的请求
    pub fn move_folder(folder_token: impl Into<String>, parent_token: impl Into<String>) -> Self {
        Self {
            folder_token: folder_token.into(),
            operation_type: "move".to_string(),
            parent_token: Some(parent_token.into()),
        }
    }

    /// 创建删除文件夹的请求
    pub fn delete_folder(folder_token: impl Into<String>) -> Self {
        Self {
            folder_token: folder_token.into(),
            operation_type: "delete".to_string(),
            parent_token: None,
        }
    }
}

/// 移动或删除文件夹响应数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MoveOrDeleteFolderRespData {
    /// 异步任务ID,可以通过该ID查询任务执行状态
    pub task_id: Option<String>,
}

impl ApiResponseTrait for MoveOrDeleteFolderRespData {
    fn data_format() -> ResponseFormat {
        ResponseFormat::Data
    }
}

/// 查询异步任务状态请求参数
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CheckAsyncTaskRequest {
    /// 任务ID
    pub task_id: String,
}

impl CheckAsyncTaskRequest {
    pub fn new(task_id: impl Into<String>) -> Self {
        Self {
            task_id: task_id.into(),
        }
    }
}

/// 查询异步任务状态响应数据
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CheckAsyncTaskRespData {
    /// 任务状态,PENDING: 等待中,SUCCESS: 成功,FAILURE: 失败
    pub status: String,
    /// 任务错误信息(如果失败)
    pub error_msg: Option<String>,
}

impl ApiResponseTrait for CheckAsyncTaskRespData {
    fn data_format() -> ResponseFormat {
        ResponseFormat::Data
    }
}

// === 宏实现 ===

impl_executable_builder_owned!(
    ListFilesRequestBuilder,
    FolderService,
    ListFilesRequest,
    BaseResponse<ListFilesRespData>,
    list_files
);