open_lark/service/cloud_docs/permission/public_v2/
get.rs1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::core::{
5 api_req::ApiRequest,
6 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
7 config::Config,
8 constants::AccessTokenType,
9 endpoints::{cloud_docs::*, EndpointBuilder},
10 http::Transport,
11 query_params::QueryParams,
12 req_option::RequestOption,
13 SDKResult,
14};
15
16#[derive(Debug, Serialize, Default, Clone)]
18pub struct GetPermissionPublicV2Request {
19 #[serde(skip)]
20 api_request: ApiRequest,
21 #[serde(skip)]
23 token: String,
24 #[serde(skip)]
26 obj_type: String,
27}
28
29impl GetPermissionPublicV2Request {
30 pub fn builder() -> GetPermissionPublicV2RequestBuilder {
31 GetPermissionPublicV2RequestBuilder::default()
32 }
33
34 pub fn new(token: impl ToString, obj_type: impl ToString) -> Self {
35 Self {
36 token: token.to_string(),
37 obj_type: obj_type.to_string(),
38 ..Default::default()
39 }
40 }
41
42 pub fn for_doc(token: impl ToString) -> Self {
44 Self::new(token, "doc")
45 }
46
47 pub fn for_sheet(token: impl ToString) -> Self {
49 Self::new(token, "sheet")
50 }
51
52 pub fn for_bitable(token: impl ToString) -> Self {
54 Self::new(token, "bitable")
55 }
56
57 pub fn for_wiki(token: impl ToString) -> Self {
59 Self::new(token, "wiki")
60 }
61}
62
63#[derive(Default)]
64pub struct GetPermissionPublicV2RequestBuilder {
65 request: GetPermissionPublicV2Request,
66}
67
68impl GetPermissionPublicV2RequestBuilder {
69 pub fn token(mut self, token: impl ToString) -> Self {
71 self.request.token = token.to_string();
72 self
73 }
74
75 pub fn obj_type(mut self, obj_type: impl ToString) -> Self {
77 self.request.obj_type = obj_type.to_string();
78 self
79 }
80
81 pub fn as_doc(mut self) -> Self {
83 self.request.obj_type = "doc".to_string();
84 self
85 }
86
87 pub fn as_sheet(mut self) -> Self {
89 self.request.obj_type = "sheet".to_string();
90 self
91 }
92
93 pub fn as_bitable(mut self) -> Self {
95 self.request.obj_type = "bitable".to_string();
96 self
97 }
98
99 pub fn as_wiki(mut self) -> Self {
101 self.request.obj_type = "wiki".to_string();
102 self
103 }
104
105 pub fn build(mut self) -> GetPermissionPublicV2Request {
106 self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
107 self.request
108 }
109}
110
111crate::impl_executable_builder_owned!(
112 GetPermissionPublicV2RequestBuilder,
113 crate::service::cloud_docs::permission::PermissionService,
114 GetPermissionPublicV2Request,
115 BaseResponse<GetPermissionPublicV2Response>,
116 get_permission_public_v2
117);
118
119#[derive(Debug, Deserialize)]
121pub struct PublicSettingsV2 {
122 pub link_share_setting: String,
124 pub password_switch: bool,
126 pub allow_copy: bool,
128 pub allow_comment: bool,
130 pub allow_save_copy: bool,
132 pub access_setting: Option<String>,
134 pub watermark_setting: Option<String>,
136 pub allow_share_partner_tenant: Option<bool>,
138 pub external_access_entity: Option<serde_json::Value>,
140 pub comment_entity: Option<serde_json::Value>,
142 pub share_scope: Option<String>,
144 pub expire_time: Option<i64>,
146}
147
148#[derive(Debug, Deserialize)]
150pub struct GetPermissionPublicV2Response {
151 pub permission_public: PublicSettingsV2,
153}
154
155impl ApiResponseTrait for GetPermissionPublicV2Response {
156 fn data_format() -> ResponseFormat {
157 ResponseFormat::Data
158 }
159}
160
161pub async fn get_permission_public_v2(
163 request: GetPermissionPublicV2Request,
164 config: &Config,
165 option: Option<RequestOption>,
166) -> SDKResult<BaseResponse<GetPermissionPublicV2Response>> {
167 let mut api_req = request.api_request;
168 api_req.http_method = Method::GET;
169 api_req.api_path =
170 EndpointBuilder::replace_param(DRIVE_V2_PERMISSIONS_PUBLIC, "token", &request.token);
171
172 api_req
174 .query_params
175 .insert(QueryParams::TYPE, request.obj_type);
176
177 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
178
179 let api_resp = Transport::request(api_req, config, option).await?;
180 Ok(api_resp)
181}
182
183impl PublicSettingsV2 {
184 pub fn is_link_share_enabled(&self) -> bool {
186 self.link_share_setting == "tenant_readable"
187 || self.link_share_setting == "tenant_editable"
188 || self.link_share_setting == "anyone_readable"
189 || self.link_share_setting == "anyone_editable"
190 }
191
192 pub fn is_tenant_accessible(&self) -> bool {
194 self.link_share_setting == "tenant_readable" || self.link_share_setting == "tenant_editable"
195 }
196
197 pub fn is_anyone_accessible(&self) -> bool {
199 self.link_share_setting == "anyone_readable" || self.link_share_setting == "anyone_editable"
200 }
201
202 pub fn is_editable(&self) -> bool {
204 self.link_share_setting == "tenant_editable" || self.link_share_setting == "anyone_editable"
205 }
206
207 pub fn is_readonly(&self) -> bool {
209 self.link_share_setting == "tenant_readable" || self.link_share_setting == "anyone_readable"
210 }
211
212 pub fn has_password_protection(&self) -> bool {
214 self.password_switch
215 }
216
217 pub fn allows_external_share(&self) -> bool {
219 self.allow_share_partner_tenant.unwrap_or(false)
220 }
221
222 pub fn has_expire_time(&self) -> bool {
224 self.expire_time.is_some()
225 }
226
227 pub fn is_expired(&self) -> bool {
229 if let Some(expire_time) = self.expire_time {
230 let now = chrono::Utc::now().timestamp();
231 now > expire_time
232 } else {
233 false
234 }
235 }
236
237 pub fn share_level_description(&self) -> &'static str {
239 match self.link_share_setting.as_str() {
240 "closed" => "关闭分享",
241 "tenant_readable" => "组织内可读",
242 "tenant_editable" => "组织内可编辑",
243 "anyone_readable" => "任何人可读",
244 "anyone_editable" => "任何人可编辑",
245 _ => "未知设置",
246 }
247 }
248
249 pub fn permissions_summary(&self) -> String {
251 let mut features = Vec::new();
252
253 if self.allow_copy {
254 features.push("允许复制");
255 }
256 if self.allow_comment {
257 features.push("允许评论");
258 }
259 if self.allow_save_copy {
260 features.push("允许保存副本");
261 }
262 if self.password_switch {
263 features.push("密码保护");
264 }
265 if self.allows_external_share() {
266 features.push("组织外分享");
267 }
268
269 if features.is_empty() {
270 "基础权限".to_string()
271 } else {
272 features.join(", ")
273 }
274 }
275
276 pub fn security_level(&self) -> &'static str {
278 if self.link_share_setting == "closed" {
279 "最安全"
280 } else if self.password_switch {
281 "较安全"
282 } else if self.is_tenant_accessible() && !self.allows_external_share() {
283 "中等安全"
284 } else if self.is_anyone_accessible() || self.allows_external_share() {
285 "较低安全"
286 } else {
287 "未知"
288 }
289 }
290
291 pub fn share_scope_description(&self) -> Option<&str> {
293 self.share_scope.as_deref()
294 }
295
296 pub fn expire_time_formatted(&self) -> Option<String> {
298 self.expire_time.map(|timestamp| {
299 let datetime =
300 chrono::DateTime::from_timestamp(timestamp, 0).unwrap_or_else(chrono::Utc::now);
301 datetime.format("%Y-%m-%d %H:%M:%S").to_string()
302 })
303 }
304
305 pub fn remaining_valid_time(&self) -> Option<i64> {
307 if let Some(expire_time) = self.expire_time {
308 let now = chrono::Utc::now().timestamp();
309 if expire_time > now {
310 Some(expire_time - now)
311 } else {
312 Some(0) }
314 } else {
315 None }
317 }
318
319 pub fn advanced_features_status(&self) -> Vec<String> {
321 let mut features = Vec::new();
322
323 if let Some(access_setting) = &self.access_setting {
324 features.push(format!("访问设置: {access_setting}"));
325 }
326
327 if let Some(watermark) = &self.watermark_setting {
328 features.push(format!("水印: {watermark}"));
329 }
330
331 if self.comment_entity.is_some() {
332 features.push("自定义评论权限".to_string());
333 }
334
335 if self.external_access_entity.is_some() {
336 features.push("外部访问配置".to_string());
337 }
338
339 features
340 }
341}
342
343impl GetPermissionPublicV2Response {
344 pub fn public_settings(&self) -> &PublicSettingsV2 {
346 &self.permission_public
347 }
348
349 pub fn allows_external_access(&self) -> bool {
351 self.permission_public.is_link_share_enabled()
352 }
353
354 pub fn settings_summary(&self) -> String {
356 format!(
357 "{} - {} (安全级别: {})",
358 self.permission_public.share_level_description(),
359 self.permission_public.permissions_summary(),
360 self.permission_public.security_level()
361 )
362 }
363
364 pub fn has_advanced_config(&self) -> bool {
366 self.permission_public.external_access_entity.is_some()
367 || self.permission_public.comment_entity.is_some()
368 || self.permission_public.share_scope.is_some()
369 }
370
371 pub fn security_recommendations(&self) -> Vec<String> {
373 let mut recommendations = Vec::new();
374
375 if !self.permission_public.password_switch && self.permission_public.is_anyone_accessible()
376 {
377 recommendations.push("建议开启密码保护以提高安全性".to_string());
378 }
379
380 if self.permission_public.allow_copy && self.permission_public.is_anyone_accessible() {
381 recommendations.push("建议限制复制权限以防止内容泄露".to_string());
382 }
383
384 if self.permission_public.is_editable() && self.permission_public.is_anyone_accessible() {
385 recommendations.push("建议将编辑权限限制在组织内".to_string());
386 }
387
388 if self.permission_public.allows_external_share() {
389 recommendations.push("开启了组织外分享,请确保内容安全".to_string());
390 }
391
392 if self.permission_public.is_expired() {
393 recommendations.push("文档分享已过期,需要重新设置".to_string());
394 } else if let Some(remaining) = self.permission_public.remaining_valid_time() {
395 if remaining < 86400 {
396 recommendations.push("文档分享即将过期,请注意及时续期".to_string());
398 }
399 }
400
401 if recommendations.is_empty() {
402 recommendations.push("当前权限设置合理".to_string());
403 }
404
405 recommendations
406 }
407
408 pub fn advanced_features_report(&self) -> String {
410 let features = self.permission_public.advanced_features_status();
411 if features.is_empty() {
412 "未启用高级功能".to_string()
413 } else {
414 format!("高级功能: {}", features.join(", "))
415 }
416 }
417
418 pub fn expiration_status(&self) -> String {
420 if let Some(expire_time) = self.permission_public.expire_time_formatted() {
421 if self.permission_public.is_expired() {
422 format!("已过期: {expire_time}")
423 } else if let Some(remaining) = self.permission_public.remaining_valid_time() {
424 let days = remaining / 86400;
425 let hours = (remaining % 86400) / 3600;
426 if days > 0 {
427 format!("剩余: {days}天{hours}小时 (过期时间: {expire_time})")
428 } else {
429 format!("剩余: {hours}小时 (过期时间: {expire_time})")
430 }
431 } else {
432 format!("过期时间: {expire_time}")
433 }
434 } else {
435 "永久有效".to_string()
436 }
437 }
438}
439
440#[cfg(test)]
441#[allow(unused_variables, unused_unsafe)]
442mod tests {
443 use super::*;
444
445 #[test]
446 fn test_get_permission_public_v2_request_builder() {
447 let request = GetPermissionPublicV2Request::builder()
448 .token("doccnxxxxxx")
449 .as_doc()
450 .build();
451
452 assert_eq!(request.token, "doccnxxxxxx");
453 assert_eq!(request.obj_type, "doc");
454 }
455
456 #[test]
457 fn test_convenience_methods() {
458 let request = GetPermissionPublicV2Request::for_doc("doccnxxxxxx");
459 assert_eq!(request.obj_type, "doc");
460
461 let request = GetPermissionPublicV2Request::for_sheet("shtcnxxxxxx");
462 assert_eq!(request.obj_type, "sheet");
463
464 let request = GetPermissionPublicV2Request::for_bitable("bblcnxxxxxx");
465 assert_eq!(request.obj_type, "bitable");
466
467 let request = GetPermissionPublicV2Request::for_wiki("wikicnxxxxxx");
468 assert_eq!(request.obj_type, "wiki");
469 }
470
471 #[test]
472 fn test_public_settings_v2_methods() {
473 let settings = PublicSettingsV2 {
474 link_share_setting: "tenant_editable".to_string(),
475 password_switch: true,
476 allow_copy: true,
477 allow_comment: true,
478 allow_save_copy: false,
479 access_setting: Some("advanced".to_string()),
480 watermark_setting: Some("visible".to_string()),
481 allow_share_partner_tenant: Some(true),
482 external_access_entity: Some(serde_json::json!({})),
483 comment_entity: None,
484 share_scope: Some("limited".to_string()),
485 expire_time: Some(chrono::Utc::now().timestamp() + 86400), };
487
488 assert!(settings.is_link_share_enabled());
489 assert!(settings.is_tenant_accessible());
490 assert!(!settings.is_anyone_accessible());
491 assert!(settings.is_editable());
492 assert!(!settings.is_readonly());
493 assert!(settings.has_password_protection());
494 assert!(settings.allows_external_share());
495 assert!(settings.has_expire_time());
496 assert!(!settings.is_expired());
497 assert_eq!(settings.share_level_description(), "组织内可编辑");
498 assert_eq!(settings.security_level(), "较安全");
499 assert_eq!(settings.share_scope_description(), Some("limited"));
500
501 let features = settings.advanced_features_status();
502 assert!(!features.is_empty());
503 assert!(features.iter().any(|f| f.contains("访问设置")));
504 assert!(features.iter().any(|f| f.contains("水印")));
505 }
506
507 #[test]
508 fn test_expiration_logic() {
509 let expired_settings = PublicSettingsV2 {
510 link_share_setting: "anyone_readable".to_string(),
511 password_switch: false,
512 allow_copy: false,
513 allow_comment: false,
514 allow_save_copy: false,
515 access_setting: None,
516 watermark_setting: None,
517 allow_share_partner_tenant: None,
518 external_access_entity: None,
519 comment_entity: None,
520 share_scope: None,
521 expire_time: Some(chrono::Utc::now().timestamp() - 3600), };
523
524 assert!(expired_settings.is_expired());
525 assert_eq!(expired_settings.remaining_valid_time(), Some(0));
526
527 let permanent_settings = PublicSettingsV2 {
528 link_share_setting: "tenant_readable".to_string(),
529 password_switch: false,
530 allow_copy: false,
531 allow_comment: false,
532 allow_save_copy: false,
533 access_setting: None,
534 watermark_setting: None,
535 allow_share_partner_tenant: None,
536 external_access_entity: None,
537 comment_entity: None,
538 share_scope: None,
539 expire_time: None,
540 };
541
542 assert!(!permanent_settings.is_expired());
543 assert_eq!(permanent_settings.remaining_valid_time(), None);
544 }
545}