1pub(crate) mod capability;
220pub mod client;
221pub mod error;
222
223#[cfg(test)]
224mod test_utils;
225
226#[cfg(feature = "websocket")]
236pub mod ws_client;
237
238pub use client::{Client, ClientBuilder};
244
245pub use error::{Error, Result};
247
248pub use error::{
250 ClientErrorExt, ErrorAnalyzer, with_context, with_operation_context, };
255
256pub use error::{
258 api_error, authentication_error, business_error, configuration_error, internal_error, network_error, rate_limit_error, serialization_error, service_unavailable_error, timeout_error, validation_error, };
270
271#[cfg(feature = "cardkit")]
277pub use openlark_cardkit::CardkitClient;
278
279#[cfg(feature = "auth")]
282pub use client::AuthClient;
283
284#[cfg(feature = "docs")]
285pub use openlark_docs::DocsClient;
286
287#[cfg(feature = "communication")]
288pub use openlark_communication::CommunicationClient;
289
290#[cfg(feature = "hr")]
291pub use openlark_hr::HrClient;
292
293#[cfg(feature = "meeting")]
294pub use openlark_meeting::MeetingClient;
295
296#[cfg(feature = "ai")]
301pub use openlark_ai::AiClient;
302
303#[cfg(feature = "workflow")]
304pub use openlark_workflow::WorkflowClient;
305
306#[cfg(feature = "platform")]
307pub use openlark_platform::PlatformClient;
308
309#[cfg(feature = "application")]
310pub use openlark_application::ApplicationClient;
311
312#[cfg(feature = "helpdesk")]
313pub use openlark_helpdesk::HelpdeskClient;
314
315#[cfg(feature = "mail")]
316pub use openlark_mail::MailClient;
317
318#[cfg(feature = "bot")]
319pub use openlark_bot::BotClient;
320
321#[cfg(feature = "analytics")]
322pub use openlark_analytics::AnalyticsClient;
323
324#[cfg(feature = "user")]
325pub use openlark_user::UserClient;
326
327#[cfg(feature = "security")]
328pub use openlark_security::SecurityClient;
329
330pub use openlark_core::{SDKResult as CoreResult, config::Config as CoreConfig};
336
337pub use openlark_core::error::{CoreError, ErrorCode, ErrorSeverity, ErrorTrait, ErrorType};
339
340pub type SDKResult<T> = openlark_core::SDKResult<T>;
346
347pub mod prelude {
366 pub use crate::{Client, ClientBuilder};
372
373 pub use crate::{Error, Result};
375
376 pub use crate::{
382 ClientErrorExt, ErrorAnalyzer, with_context, with_operation_context, };
387
388 pub use crate::{
390 api_error, authentication_error, business_error, configuration_error, internal_error, network_error, rate_limit_error, serialization_error, service_unavailable_error, timeout_error, validation_error, };
402
403 pub use openlark_core::error::{CoreError, ErrorCode, ErrorSeverity, ErrorTrait, ErrorType};
405
406 #[cfg(feature = "cardkit")]
408 pub use openlark_cardkit::CardkitClient;
409
410 #[cfg(feature = "auth")]
411 pub use crate::AuthClient;
412
413 #[cfg(feature = "docs")]
414 pub use openlark_docs::DocsClient;
415
416 #[cfg(feature = "communication")]
417 pub use openlark_communication::CommunicationClient;
418
419 #[cfg(feature = "hr")]
420 pub use openlark_hr::HrClient;
421
422 #[cfg(feature = "meeting")]
423 pub use openlark_meeting::MeetingClient;
424
425 #[cfg(feature = "ai")]
429 pub use openlark_ai::AiClient;
430
431 #[cfg(feature = "workflow")]
432 pub use crate::WorkflowClient;
433
434 #[cfg(feature = "platform")]
435 pub use crate::PlatformClient;
436
437 #[cfg(feature = "application")]
438 pub use crate::ApplicationClient;
439
440 #[cfg(feature = "helpdesk")]
441 pub use crate::HelpdeskClient;
442
443 #[cfg(feature = "mail")]
444 pub use crate::MailClient;
445
446 #[cfg(feature = "bot")]
447 pub use crate::BotClient;
448
449 #[cfg(feature = "analytics")]
450 pub use crate::AnalyticsClient;
451
452 #[cfg(feature = "user")]
453 pub use crate::UserClient;
454
455 #[cfg(feature = "security")]
456 pub use crate::SecurityClient;
457
458 pub type SDKResult<T> = openlark_core::SDKResult<T>;
469
470 pub use openlark_core::{SDKResult as CoreResult, config::Config as CoreConfig};
475}
476
477pub mod info {
479 pub const NAME: &str = "OpenLark Client";
481 pub const VERSION: &str = env!("CARGO_PKG_VERSION");
483 pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
485 pub const REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY");
487}
488
489pub mod utils;
491
492#[cfg(test)]
493#[allow(unused_imports)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn test_library_info() {
499 assert_ne!(info::NAME, "");
500 assert_ne!(info::VERSION, "");
501 assert_ne!(info::DESCRIPTION, "");
502 }
503
504 #[test]
505 fn test_enabled_features() {
506 let features = utils::get_enabled_features();
507 assert!(features.contains(&"auth"));
509 }
510
511 #[test]
512 fn test_prelude_reexports() {
513 use prelude::*;
515
516 let _builder: ClientBuilder = ClientBuilder::new();
518
519 let _config = CoreConfig::builder()
521 .app_id("test")
522 .app_secret("test")
523 .build();
524 }
525
526 #[test]
527 fn test_check_env_config_success() {
528 test_utils::with_env_vars(
529 &[
530 ("OPENLARK_APP_ID", Some("test_app_id")),
531 ("OPENLARK_APP_SECRET", Some("test_secret")),
532 ],
533 || {
534 let result = utils::check_env_config();
535 assert!(result.is_ok());
536 },
537 );
538 }
539
540 #[test]
541 fn test_check_env_config_missing_app_id() {
542 test_utils::with_env_vars(
543 &[
544 ("OPENLARK_APP_ID", None),
545 ("OPENLARK_APP_SECRET", Some("test_secret")),
546 ],
547 || {
548 let result = utils::check_env_config();
549 assert!(result.is_err());
550 },
551 );
552 }
553
554 #[test]
555 fn test_check_env_config_empty_app_id() {
556 test_utils::with_env_vars(
557 &[
558 ("OPENLARK_APP_ID", Some("")),
559 ("OPENLARK_APP_SECRET", Some("test_secret")),
560 ],
561 || {
562 let result = utils::check_env_config();
563 assert!(result.is_err());
564 },
565 );
566 }
567
568 #[test]
569 fn test_check_env_config_missing_app_secret() {
570 test_utils::with_env_vars(
571 &[
572 ("OPENLARK_APP_ID", Some("test_app_id")),
573 ("OPENLARK_APP_SECRET", None),
574 ],
575 || {
576 let result = utils::check_env_config();
577 assert!(result.is_err());
578 },
579 );
580 }
581
582 #[test]
583 fn test_check_env_config_empty_app_secret() {
584 test_utils::with_env_vars(
585 &[
586 ("OPENLARK_APP_ID", Some("test_app_id")),
587 ("OPENLARK_APP_SECRET", Some("")),
588 ],
589 || {
590 let result = utils::check_env_config();
591 assert!(result.is_err());
592 },
593 );
594 }
595
596 #[test]
597 fn test_check_env_config_invalid_base_url() {
598 test_utils::with_env_vars(
599 &[
600 ("OPENLARK_APP_ID", Some("test_app_id")),
601 ("OPENLARK_APP_SECRET", Some("test_secret")),
602 ("OPENLARK_BASE_URL", Some("invalid_url")),
603 ],
604 || {
605 let result = utils::check_env_config();
606 assert!(result.is_err());
607 },
608 );
609 }
610
611 #[test]
612 fn test_check_env_config_valid_base_url() {
613 test_utils::with_env_vars(
614 &[
615 ("OPENLARK_APP_ID", Some("test_app_id")),
616 ("OPENLARK_APP_SECRET", Some("test_secret")),
617 ("OPENLARK_BASE_URL", Some("https://open.feishu.cn")),
618 ],
619 || {
620 let result = utils::check_env_config();
621 assert!(result.is_ok());
622 },
623 );
624 }
625
626 #[test]
627 fn test_check_env_config_invalid_timeout() {
628 test_utils::with_env_vars(
629 &[
630 ("OPENLARK_APP_ID", Some("test_app_id")),
631 ("OPENLARK_APP_SECRET", Some("test_secret")),
632 ("OPENLARK_TIMEOUT", Some("not_a_number")),
633 ],
634 || {
635 let result = utils::check_env_config();
636 assert!(result.is_err());
637 },
638 );
639 }
640
641 #[test]
642 fn test_check_env_config_valid_timeout() {
643 test_utils::with_env_vars(
644 &[
645 ("OPENLARK_APP_ID", Some("test_app_id")),
646 ("OPENLARK_APP_SECRET", Some("test_secret")),
647 ("OPENLARK_TIMEOUT", Some("30")),
648 ],
649 || {
650 let result = utils::check_env_config();
651 assert!(result.is_ok());
652 },
653 );
654 }
655
656 #[test]
657 fn test_create_config_from_env_success() {
658 test_utils::with_env_vars(
659 &[
660 ("OPENLARK_APP_ID", Some("test_app_id")),
661 ("OPENLARK_APP_SECRET", Some("test_secret")),
662 ("OPENLARK_BASE_URL", Some("https://open.feishu.cn")),
663 ],
664 || {
665 let result = utils::create_config_from_env();
666 assert!(result.is_ok());
667 let config = result.unwrap();
668 assert_eq!(config.app_id(), "test_app_id");
669 assert_eq!(config.app_secret(), "test_secret");
670 },
671 );
672 }
673
674 #[test]
675 fn test_create_config_from_env_uses_canonical_env_interpretation() {
676 test_utils::with_env_vars(
679 &[
680 ("OPENLARK_APP_ID", Some("test_app_id")),
681 ("OPENLARK_APP_SECRET", Some("test_secret")),
682 ("OPENLARK_TIMEOUT", Some("45")),
683 ("OPENLARK_ENABLE_LOG", Some("0")),
684 ("OPENLARK_RETRY_COUNT", Some("5")),
685 ],
686 || {
687 let config = utils::create_config_from_env().unwrap();
688 assert_eq!(
689 config.req_timeout(),
690 Some(std::time::Duration::from_secs(45))
691 );
692 assert!(!config.enable_log());
693 assert_eq!(config.retry_count(), 5);
694 },
695 );
696
697 test_utils::with_env_vars(
698 &[
699 ("OPENLARK_APP_ID", Some("test_app_id")),
700 ("OPENLARK_APP_SECRET", Some("test_secret")),
701 ("OPENLARK_ENABLE_LOG", None),
702 ],
703 || {
704 let config = utils::create_config_from_env().unwrap();
705 assert!(
706 config.enable_log(),
707 "未设 OPENLARK_ENABLE_LOG 时应为 core 默认 true"
708 );
709 },
710 );
711 }
712
713 #[test]
714 fn test_create_config_from_env_missing_vars() {
715 test_utils::with_env_vars(
716 &[("OPENLARK_APP_ID", None), ("OPENLARK_APP_SECRET", None)],
717 || {
718 let result = utils::create_config_from_env();
719 assert!(result.is_err());
720 },
721 );
722 }
723
724 #[test]
725 fn test_get_config_summary() {
726 let config = openlark_core::config::Config::builder()
727 .app_id("test_app_id")
728 .app_secret("test_secret_key")
729 .base_url("https://open.feishu.cn")
730 .req_timeout(std::time::Duration::from_secs(30))
731 .build();
732
733 let summary = utils::get_config_summary(&config);
734 assert_eq!(summary.app_id, "test_app_id");
735 assert!(summary.app_secret_set);
736 assert_eq!(summary.base_url, "https://open.feishu.cn");
737 assert_eq!(
738 summary.req_timeout,
739 Some(std::time::Duration::from_secs(30))
740 );
741 }
742
743 #[test]
744 fn test_config_summary_friendly_description() {
745 let summary = openlark_core::config::ConfigSummary {
746 app_id: "test_app".to_string(),
747 app_secret_set: true,
748 app_type: openlark_core::constants::AppType::SelfBuild,
749 enable_token_cache: true,
750 base_url: "https://open.feishu.cn".to_string(),
751 allow_custom_base_url: false,
752 req_timeout: Some(std::time::Duration::from_secs(30)),
753 retry_count: 3,
754 enable_log: false,
755 header_count: 0,
756 max_response_size: 100 * 1024 * 1024,
757 };
758
759 let description = summary.friendly_description();
760 assert!(description.contains("test_app"));
761 assert!(description.contains("open.feishu.cn"));
762 assert!(description.contains("30s"));
763 }
764
765 #[test]
766 fn test_config_summary_friendly_description_no_timeout() {
767 let summary = openlark_core::config::ConfigSummary {
768 app_id: "test_app".to_string(),
769 app_secret_set: true,
770 app_type: openlark_core::constants::AppType::SelfBuild,
771 enable_token_cache: true,
772 base_url: "https://open.feishu.cn".to_string(),
773 allow_custom_base_url: false,
774 req_timeout: None,
775 retry_count: 3,
776 enable_log: false,
777 header_count: 0,
778 max_response_size: 100 * 1024 * 1024,
779 };
780
781 let description = summary.friendly_description();
782 assert!(description.contains("test_app"));
783 assert!(description.contains("None"));
784 }
785
786 #[test]
787 fn test_validate_feature_dependencies_success() {
788 let result = utils::validate_feature_dependencies();
790 assert!(result.is_ok());
791 }
792
793 #[test]
794 fn test_diagnose_system_success() {
795 test_utils::with_env_vars(
796 &[
797 ("OPENLARK_APP_ID", Some("test_app_id")),
798 ("OPENLARK_APP_SECRET", Some("test_secret")),
799 ],
800 || {
801 let diagnostics = utils::diagnose_system();
802 assert!(
803 diagnostics.env_config_status.contains("✅")
804 || diagnostics.env_config_status.contains("❌")
805 );
806 assert!(diagnostics.feature_deps_status.contains("✅"));
807 assert!(!diagnostics.enabled_features.is_empty());
808 },
809 );
810 }
811
812 #[test]
813 fn test_system_diagnostics_new() {
814 let diagnostics = utils::SystemDiagnostics::new();
815 assert_eq!(diagnostics.env_config_status, "未检查");
816 assert_eq!(diagnostics.feature_deps_status, "未检查");
817 assert!(diagnostics.enabled_features.is_empty());
818 assert!(diagnostics.issues.is_empty());
819 }
820
821 #[test]
822 fn test_system_diagnostics_add_issue() {
823 let mut diagnostics = utils::SystemDiagnostics::new();
824 diagnostics.add_issue("测试类别", "测试描述");
825 assert_eq!(diagnostics.issues.len(), 1);
826 assert_eq!(diagnostics.issues[0].category, "测试类别");
827 assert_eq!(diagnostics.issues[0].description, "测试描述");
828 }
829
830 #[test]
831 fn test_system_diagnostics_health_summary_healthy() {
832 let diagnostics = utils::SystemDiagnostics::new();
833 let summary = diagnostics.health_summary();
834 assert!(summary.contains("🟢"));
835 assert!(summary.contains("健康"));
836 }
837
838 #[test]
839 fn test_system_diagnostics_health_summary_with_issues() {
840 let mut diagnostics = utils::SystemDiagnostics::new();
841 diagnostics.add_issue("测试类别", "测试描述");
842 let summary = diagnostics.health_summary();
843 assert!(summary.contains("🟡"));
844 assert!(summary.contains("1"));
845 }
846
847 #[test]
848 fn test_system_diagnostics_has_critical_issues_true() {
849 let mut diagnostics = utils::SystemDiagnostics::new();
850 diagnostics.add_issue("环境变量", "配置错误");
851 assert!(diagnostics.has_critical_issues());
852 }
853
854 #[test]
855 fn test_system_diagnostics_has_critical_issues_false() {
856 let mut diagnostics = utils::SystemDiagnostics::new();
857 diagnostics.add_issue("其他问题", "一般错误");
858 assert!(!diagnostics.has_critical_issues());
859 }
860
861 #[test]
862 fn test_system_diagnostics_default() {
863 let diagnostics: utils::SystemDiagnostics = Default::default();
864 assert_eq!(diagnostics.env_config_status, "未检查");
865 }
866
867 #[test]
868 fn test_diagnostic_issue_clone() {
869 let issue = utils::DiagnosticIssue {
870 category: "测试".to_string(),
871 description: "描述".to_string(),
872 };
873 let cloned = issue.clone();
874 assert_eq!(cloned.category, "测试");
875 assert_eq!(cloned.description, "描述");
876 }
877}