1use std::sync::Arc;
4use std::time::Duration;
5
6use crate::clients::actor::ActorClient;
7use crate::clients::actor_collection::ActorCollectionClient;
8use crate::clients::build::BuildClient;
9use crate::clients::build_collection::BuildCollectionClient;
10use crate::clients::dataset::DatasetClient;
11use crate::clients::dataset_collection::DatasetCollectionClient;
12use crate::clients::key_value_store::KeyValueStoreClient;
13use crate::clients::key_value_store_collection::KeyValueStoreCollectionClient;
14use crate::clients::log::LogClient;
15use crate::clients::request_queue::RequestQueueClient;
16use crate::clients::request_queue_collection::RequestQueueCollectionClient;
17use crate::clients::run::RunClient;
18use crate::clients::run_collection::RunCollectionClient;
19use crate::clients::schedule::ScheduleClient;
20use crate::clients::schedule_collection::ScheduleCollectionClient;
21use crate::clients::store_collection::StoreCollectionClient;
22use crate::clients::task::TaskClient;
23use crate::clients::task_collection::TaskCollectionClient;
24use crate::clients::user::UserClient;
25use crate::clients::webhook::WebhookClient;
26use crate::clients::webhook_collection::WebhookCollectionClient;
27use crate::clients::webhook_dispatch::WebhookDispatchClient;
28use crate::clients::webhook_dispatch_collection::WebhookDispatchCollectionClient;
29use crate::common::build_user_agent;
30use crate::http_client::{HttpBackend, HttpClient, ReqwestBackend, RetryConfig};
31
32const DEFAULT_BASE_URL: &str = "https://api.apify.com";
34const DEFAULT_MAX_RETRIES: u32 = 8;
36const DEFAULT_MIN_DELAY_BETWEEN_RETRIES: Duration = Duration::from_millis(500);
38const DEFAULT_TIMEOUT: Duration = Duration::from_secs(360);
40pub(crate) const ME_USER_PLACEHOLDER: &str = "me";
42
43pub struct ApifyClientBuilder {
55 token: Option<String>,
56 base_url: String,
57 public_base_url: Option<String>,
58 max_retries: u32,
59 min_delay_between_retries: Duration,
60 timeout: Duration,
61 user_agent_suffix: Option<String>,
62 http_backend: Option<Arc<dyn HttpBackend>>,
63}
64
65impl Default for ApifyClientBuilder {
66 fn default() -> Self {
67 Self {
68 token: None,
69 base_url: DEFAULT_BASE_URL.to_string(),
70 public_base_url: None,
71 max_retries: DEFAULT_MAX_RETRIES,
72 min_delay_between_retries: DEFAULT_MIN_DELAY_BETWEEN_RETRIES,
73 timeout: DEFAULT_TIMEOUT,
74 user_agent_suffix: None,
75 http_backend: None,
76 }
77 }
78}
79
80impl ApifyClientBuilder {
81 pub fn token(mut self, token: impl Into<String>) -> Self {
83 self.token = Some(token.into());
84 self
85 }
86
87 pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
91 self.base_url = base_url.into();
92 self
93 }
94
95 pub fn public_base_url(mut self, public_base_url: impl Into<String>) -> Self {
99 self.public_base_url = Some(public_base_url.into());
100 self
101 }
102
103 pub fn max_retries(mut self, max_retries: u32) -> Self {
105 self.max_retries = max_retries;
106 self
107 }
108
109 pub fn min_delay_between_retries(mut self, delay: Duration) -> Self {
111 self.min_delay_between_retries = delay;
112 self
113 }
114
115 pub fn timeout(mut self, timeout: Duration) -> Self {
117 self.timeout = timeout;
118 self
119 }
120
121 pub fn user_agent_suffix(mut self, suffix: impl Into<String>) -> Self {
123 self.user_agent_suffix = Some(suffix.into());
124 self
125 }
126
127 pub fn http_backend(mut self, backend: Arc<dyn HttpBackend>) -> Self {
131 self.http_backend = Some(backend);
132 self
133 }
134
135 pub fn build(self) -> ApifyClient {
137 let backend = self
138 .http_backend
139 .unwrap_or_else(|| Arc::new(ReqwestBackend::new()));
140
141 let user_agent = build_user_agent(self.user_agent_suffix.as_deref());
142
143 let retry = RetryConfig {
144 max_retries: self.max_retries,
145 min_delay_between_retries: self.min_delay_between_retries,
146 timeout: self.timeout,
147 };
148
149 let http = HttpClient::new(backend, self.token, user_agent, retry);
150
151 let trimmed = self.base_url.trim_end_matches('/');
152 let base_url = format!("{trimmed}/v2");
153
154 let public_trimmed = self
155 .public_base_url
156 .as_deref()
157 .unwrap_or(&self.base_url)
158 .trim_end_matches('/');
159 let public_base_url = format!("{public_trimmed}/v2");
160
161 ApifyClient {
162 http,
163 base_url,
164 public_base_url,
165 }
166 }
167}
168
169#[derive(Debug, Clone)]
177pub struct ApifyClient {
178 http: HttpClient,
179 base_url: String,
180 public_base_url: String,
181}
182
183impl ApifyClient {
184 pub fn builder() -> ApifyClientBuilder {
186 ApifyClientBuilder::default()
187 }
188
189 pub fn new(token: impl Into<String>) -> Self {
191 ApifyClientBuilder::default().token(token).build()
192 }
193
194 pub(crate) fn http(&self) -> HttpClient {
195 self.http.clone()
196 }
197
198 pub fn user_agent(&self) -> &str {
200 self.http.user_agent()
201 }
202
203 pub fn api_base_url(&self) -> &str {
206 &self.base_url
207 }
208
209 pub fn actors(&self) -> ActorCollectionClient {
213 ActorCollectionClient::new(self.http(), &self.base_url)
214 }
215
216 pub fn actor(&self, id: impl Into<String>) -> ActorClient {
218 ActorClient::new(self.clone(), self.http(), &self.base_url, &id.into())
219 }
220
221 pub fn builds(&self) -> BuildCollectionClient {
225 BuildCollectionClient::new(self.http(), &self.base_url)
226 }
227
228 pub fn build(&self, id: impl Into<String>) -> BuildClient {
230 BuildClient::new(self.http(), &self.base_url, &id.into())
231 }
232
233 pub fn runs(&self) -> RunCollectionClient {
237 RunCollectionClient::new(self.http(), &self.base_url, "actor-runs")
238 }
239
240 pub fn run(&self, id: impl Into<String>) -> RunClient {
242 RunClient::new(
243 self.clone(),
244 self.http(),
245 &self.base_url,
246 "actor-runs",
247 &id.into(),
248 )
249 }
250
251 pub fn datasets(&self) -> DatasetCollectionClient {
255 DatasetCollectionClient::new(self.http(), &self.base_url)
256 }
257
258 pub fn dataset(&self, id: impl Into<String>) -> DatasetClient {
260 DatasetClient::new(self.http(), &self.base_url, "datasets", &id.into())
261 .with_public_base(&self.public_base_url)
262 }
263
264 pub fn key_value_stores(&self) -> KeyValueStoreCollectionClient {
268 KeyValueStoreCollectionClient::new(self.http(), &self.base_url)
269 }
270
271 pub fn key_value_store(&self, id: impl Into<String>) -> KeyValueStoreClient {
273 KeyValueStoreClient::new(self.http(), &self.base_url, "key-value-stores", &id.into())
274 .with_public_base(&self.public_base_url)
275 }
276
277 pub fn request_queues(&self) -> RequestQueueCollectionClient {
281 RequestQueueCollectionClient::new(self.http(), &self.base_url)
282 }
283
284 pub fn request_queue(&self, id: impl Into<String>) -> RequestQueueClient {
286 RequestQueueClient::new(self.http(), &self.base_url, "request-queues", &id.into())
287 }
288
289 pub fn tasks(&self) -> TaskCollectionClient {
293 TaskCollectionClient::new(self.http(), &self.base_url)
294 }
295
296 pub fn task(&self, id: impl Into<String>) -> TaskClient {
298 TaskClient::new(self.clone(), self.http(), &self.base_url, &id.into())
299 }
300
301 pub fn schedules(&self) -> ScheduleCollectionClient {
305 ScheduleCollectionClient::new(self.http(), &self.base_url)
306 }
307
308 pub fn schedule(&self, id: impl Into<String>) -> ScheduleClient {
310 ScheduleClient::new(self.http(), &self.base_url, &id.into())
311 }
312
313 pub fn webhooks(&self) -> WebhookCollectionClient {
317 WebhookCollectionClient::new(self.http(), &self.base_url)
318 }
319
320 pub fn webhook(&self, id: impl Into<String>) -> WebhookClient {
322 WebhookClient::new(self.http(), &self.base_url, &id.into())
323 }
324
325 pub fn webhook_dispatches(&self) -> WebhookDispatchCollectionClient {
327 WebhookDispatchCollectionClient::new(self.http(), &self.base_url)
328 }
329
330 pub fn webhook_dispatch(&self, id: impl Into<String>) -> WebhookDispatchClient {
332 WebhookDispatchClient::new(self.http(), &self.base_url, &id.into())
333 }
334
335 pub fn store(&self) -> StoreCollectionClient {
339 StoreCollectionClient::new(self.http(), &self.base_url)
340 }
341
342 pub fn log(&self, build_or_run_id: impl Into<String>) -> LogClient {
344 LogClient::new(self.http(), &self.base_url, "logs", &build_or_run_id.into())
345 }
346
347 pub fn me(&self) -> UserClient {
349 UserClient::new(self.http(), &self.base_url, ME_USER_PLACEHOLDER)
350 }
351
352 pub fn user(&self, id: impl Into<String>) -> UserClient {
354 UserClient::new(self.http(), &self.base_url, &id.into())
355 }
356
357 pub async fn set_status_message(
366 &self,
367 message: &str,
368 is_terminal: bool,
369 ) -> crate::error::ApifyClientResult<crate::models::ActorRun> {
370 let run_id = std::env::var("ACTOR_RUN_ID").map_err(|_| {
371 crate::error::ApifyClientError::InvalidArgument(
372 "ACTOR_RUN_ID environment variable is not set".to_string(),
373 )
374 })?;
375 let body = serde_json::json!({
376 "statusMessage": message,
377 "isStatusMessageTerminal": is_terminal,
378 });
379 self.run(run_id).update(&body).await
380 }
381}