dingtalk_sdk/client/
async_client.rs1use std::sync::Arc;
2
3use reqx::{advanced::PermissiveRetryEligibility, prelude::Client as HttpClient};
4use url::Url;
5
6use crate::{
7 api::{EnterpriseService, WebhookService},
8 auth::AppCredentials,
9 client::shared::{self, BuilderConfig, SharedClientState},
10 error::{Error, Result},
11};
12
13#[derive(Debug, Clone, Default)]
15pub struct ClientBuilder {
16 config: BuilderConfig,
17}
18
19shared::impl_builder_methods!(ClientBuilder);
20
21impl ClientBuilder {
22 pub fn build(self) -> Result<Client> {
24 let base_urls = self.config.normalized_base_urls()?;
25 let webhook_http = self.build_http_client(&base_urls.webhook)?;
26 let enterprise_http = self.build_http_client(&base_urls.enterprise)?;
27
28 Ok(Client {
29 inner: Arc::new(Inner {
30 webhook_http,
31 enterprise_http,
32 shared: SharedClientState::new(
33 base_urls,
34 self.config.cache_access_token,
35 self.config.token_refresh_margin,
36 self.config.body_snippet,
37 ),
38 }),
39 })
40 }
41
42 fn build_http_client(&self, base_url: &Url) -> Result<HttpClient> {
43 let mut builder = HttpClient::builder(base_url.as_str())
44 .profile(self.config.profile)
45 .client_name(self.config.client_name.clone())
46 .connect_timeout(self.config.connect_timeout);
47
48 if let Some(request_timeout) = self.config.request_timeout {
49 builder = builder.request_timeout(request_timeout);
50 }
51
52 if let Some(total_timeout) = self.config.total_timeout {
53 builder = builder.total_timeout(total_timeout);
54 }
55
56 if self.config.no_system_proxy {
57 builder = builder.no_proxy(["*"]);
58 }
59
60 if let Some(retry_policy) = &self.config.retry_policy {
61 builder = builder.retry_policy(retry_policy.clone());
62 }
63
64 if self.config.retry_non_idempotent {
65 builder = builder.retry_eligibility(Arc::new(PermissiveRetryEligibility));
66 }
67
68 for (name, value) in &self.config.default_headers {
69 builder = builder.try_default_header(name, value)?;
70 }
71
72 builder.build().map_err(Error::from)
73 }
74}
75
76#[derive(Clone)]
77pub struct Client {
79 inner: Arc<Inner>,
80}
81
82struct Inner {
83 webhook_http: HttpClient,
84 enterprise_http: HttpClient,
85 shared: SharedClientState,
86}
87
88impl Client {
89 #[must_use]
91 pub fn builder() -> ClientBuilder {
92 ClientBuilder::new()
93 }
94
95 pub fn new() -> Result<Self> {
97 Self::builder().build()
98 }
99
100 #[must_use]
102 pub fn webhook(&self, token: impl Into<String>, secret: Option<String>) -> WebhookService {
103 WebhookService::new(self.clone(), token, secret)
104 }
105
106 #[must_use]
108 pub fn enterprise(
109 &self,
110 appkey: impl Into<String>,
111 appsecret: impl Into<String>,
112 robot_code: impl Into<String>,
113 ) -> EnterpriseService {
114 EnterpriseService::new(self.clone(), appkey, appsecret, robot_code)
115 }
116
117 pub(crate) fn webhook_http(&self) -> &HttpClient {
118 &self.inner.webhook_http
119 }
120
121 pub(crate) fn enterprise_http(&self) -> &HttpClient {
122 &self.inner.enterprise_http
123 }
124
125 pub(crate) fn webhook_base_url(&self) -> &Url {
126 self.inner.shared.webhook_base_url()
127 }
128
129 pub(crate) fn webhook_endpoint(&self, segments: &[&str]) -> Result<Url> {
130 self.inner.shared.webhook_endpoint(segments)
131 }
132
133 pub(crate) fn enterprise_endpoint(&self, segments: &[&str]) -> Result<Url> {
134 self.inner.shared.enterprise_endpoint(segments)
135 }
136
137 pub(crate) fn cached_access_token(&self, credentials: &AppCredentials) -> Option<String> {
138 self.inner.shared.cached_access_token(credentials)
139 }
140
141 pub(crate) fn store_access_token(
142 &self,
143 credentials: &AppCredentials,
144 token: String,
145 expires_in_seconds: Option<i64>,
146 ) {
147 self.inner
148 .shared
149 .store_access_token(credentials, token, expires_in_seconds);
150 }
151
152 pub(crate) fn body_snippet(&self) -> crate::transport::BodySnippetConfig {
153 self.inner.shared.body_snippet()
154 }
155}