use std::sync::Arc;
use std::time::Duration;
use crate::clients::actor::ActorClient;
use crate::clients::actor_collection::ActorCollectionClient;
use crate::clients::build::BuildClient;
use crate::clients::build_collection::BuildCollectionClient;
use crate::clients::dataset::DatasetClient;
use crate::clients::dataset_collection::DatasetCollectionClient;
use crate::clients::key_value_store::KeyValueStoreClient;
use crate::clients::key_value_store_collection::KeyValueStoreCollectionClient;
use crate::clients::log::LogClient;
use crate::clients::request_queue::RequestQueueClient;
use crate::clients::request_queue_collection::RequestQueueCollectionClient;
use crate::clients::run::RunClient;
use crate::clients::run_collection::RunCollectionClient;
use crate::clients::schedule::ScheduleClient;
use crate::clients::schedule_collection::ScheduleCollectionClient;
use crate::clients::store_collection::StoreCollectionClient;
use crate::clients::task::TaskClient;
use crate::clients::task_collection::TaskCollectionClient;
use crate::clients::user::UserClient;
use crate::clients::webhook::WebhookClient;
use crate::clients::webhook_collection::WebhookCollectionClient;
use crate::clients::webhook_dispatch::WebhookDispatchClient;
use crate::clients::webhook_dispatch_collection::WebhookDispatchCollectionClient;
use crate::common::build_user_agent;
use crate::http_client::{HttpBackend, HttpClient, ReqwestBackend, RetryConfig};
const DEFAULT_BASE_URL: &str = "https://api.apify.com";
const DEFAULT_MAX_RETRIES: u32 = 8;
const DEFAULT_MIN_DELAY_BETWEEN_RETRIES: Duration = Duration::from_millis(500);
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(360);
pub(crate) const ME_USER_PLACEHOLDER: &str = "me";
pub struct ApifyClientBuilder {
token: Option<String>,
base_url: String,
public_base_url: Option<String>,
max_retries: u32,
min_delay_between_retries: Duration,
timeout: Duration,
user_agent_suffix: Option<String>,
http_backend: Option<Arc<dyn HttpBackend>>,
}
impl Default for ApifyClientBuilder {
fn default() -> Self {
Self {
token: None,
base_url: DEFAULT_BASE_URL.to_string(),
public_base_url: None,
max_retries: DEFAULT_MAX_RETRIES,
min_delay_between_retries: DEFAULT_MIN_DELAY_BETWEEN_RETRIES,
timeout: DEFAULT_TIMEOUT,
user_agent_suffix: None,
http_backend: None,
}
}
}
impl ApifyClientBuilder {
pub fn token(mut self, token: impl Into<String>) -> Self {
self.token = Some(token.into());
self
}
pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
self.base_url = base_url.into();
self
}
pub fn public_base_url(mut self, public_base_url: impl Into<String>) -> Self {
self.public_base_url = Some(public_base_url.into());
self
}
pub fn max_retries(mut self, max_retries: u32) -> Self {
self.max_retries = max_retries;
self
}
pub fn min_delay_between_retries(mut self, delay: Duration) -> Self {
self.min_delay_between_retries = delay;
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn user_agent_suffix(mut self, suffix: impl Into<String>) -> Self {
self.user_agent_suffix = Some(suffix.into());
self
}
pub fn http_backend(mut self, backend: Arc<dyn HttpBackend>) -> Self {
self.http_backend = Some(backend);
self
}
pub fn build(self) -> ApifyClient {
let backend = self
.http_backend
.unwrap_or_else(|| Arc::new(ReqwestBackend::new()));
let user_agent = build_user_agent(self.user_agent_suffix.as_deref());
let retry = RetryConfig {
max_retries: self.max_retries,
min_delay_between_retries: self.min_delay_between_retries,
timeout: self.timeout,
};
let http = HttpClient::new(backend, self.token, user_agent, retry);
let trimmed = self.base_url.trim_end_matches('/');
let base_url = format!("{trimmed}/v2");
let public_trimmed = self
.public_base_url
.as_deref()
.unwrap_or(&self.base_url)
.trim_end_matches('/');
let public_base_url = format!("{public_trimmed}/v2");
ApifyClient {
http,
base_url,
public_base_url,
}
}
}
#[derive(Debug, Clone)]
pub struct ApifyClient {
http: HttpClient,
base_url: String,
public_base_url: String,
}
impl ApifyClient {
pub fn builder() -> ApifyClientBuilder {
ApifyClientBuilder::default()
}
pub fn new(token: impl Into<String>) -> Self {
ApifyClientBuilder::default().token(token).build()
}
pub(crate) fn http(&self) -> HttpClient {
self.http.clone()
}
pub fn user_agent(&self) -> &str {
self.http.user_agent()
}
pub fn api_base_url(&self) -> &str {
&self.base_url
}
pub fn actors(&self) -> ActorCollectionClient {
ActorCollectionClient::new(self.http(), &self.base_url)
}
pub fn actor(&self, id: impl Into<String>) -> ActorClient {
ActorClient::new(self.clone(), self.http(), &self.base_url, &id.into())
}
pub fn builds(&self) -> BuildCollectionClient {
BuildCollectionClient::new(self.http(), &self.base_url)
}
pub fn build(&self, id: impl Into<String>) -> BuildClient {
BuildClient::new(self.http(), &self.base_url, &id.into())
}
pub fn runs(&self) -> RunCollectionClient {
RunCollectionClient::new(self.http(), &self.base_url, "actor-runs")
}
pub fn run(&self, id: impl Into<String>) -> RunClient {
RunClient::new(
self.clone(),
self.http(),
&self.base_url,
"actor-runs",
&id.into(),
)
}
pub fn datasets(&self) -> DatasetCollectionClient {
DatasetCollectionClient::new(self.http(), &self.base_url)
}
pub fn dataset(&self, id: impl Into<String>) -> DatasetClient {
DatasetClient::new(self.http(), &self.base_url, "datasets", &id.into())
.with_public_base(&self.public_base_url)
}
pub fn key_value_stores(&self) -> KeyValueStoreCollectionClient {
KeyValueStoreCollectionClient::new(self.http(), &self.base_url)
}
pub fn key_value_store(&self, id: impl Into<String>) -> KeyValueStoreClient {
KeyValueStoreClient::new(self.http(), &self.base_url, "key-value-stores", &id.into())
.with_public_base(&self.public_base_url)
}
pub fn request_queues(&self) -> RequestQueueCollectionClient {
RequestQueueCollectionClient::new(self.http(), &self.base_url)
}
pub fn request_queue(&self, id: impl Into<String>) -> RequestQueueClient {
RequestQueueClient::new(self.http(), &self.base_url, "request-queues", &id.into())
}
pub fn tasks(&self) -> TaskCollectionClient {
TaskCollectionClient::new(self.http(), &self.base_url)
}
pub fn task(&self, id: impl Into<String>) -> TaskClient {
TaskClient::new(self.clone(), self.http(), &self.base_url, &id.into())
}
pub fn schedules(&self) -> ScheduleCollectionClient {
ScheduleCollectionClient::new(self.http(), &self.base_url)
}
pub fn schedule(&self, id: impl Into<String>) -> ScheduleClient {
ScheduleClient::new(self.http(), &self.base_url, &id.into())
}
pub fn webhooks(&self) -> WebhookCollectionClient {
WebhookCollectionClient::new(self.http(), &self.base_url)
}
pub fn webhook(&self, id: impl Into<String>) -> WebhookClient {
WebhookClient::new(self.http(), &self.base_url, &id.into())
}
pub fn webhook_dispatches(&self) -> WebhookDispatchCollectionClient {
WebhookDispatchCollectionClient::new(self.http(), &self.base_url)
}
pub fn webhook_dispatch(&self, id: impl Into<String>) -> WebhookDispatchClient {
WebhookDispatchClient::new(self.http(), &self.base_url, &id.into())
}
pub fn store(&self) -> StoreCollectionClient {
StoreCollectionClient::new(self.http(), &self.base_url)
}
pub fn log(&self, build_or_run_id: impl Into<String>) -> LogClient {
LogClient::new(self.http(), &self.base_url, "logs", &build_or_run_id.into())
}
pub fn me(&self) -> UserClient {
UserClient::new(self.http(), &self.base_url, ME_USER_PLACEHOLDER)
}
pub fn user(&self, id: impl Into<String>) -> UserClient {
UserClient::new(self.http(), &self.base_url, &id.into())
}
pub async fn set_status_message(
&self,
message: &str,
is_terminal: bool,
) -> crate::error::ApifyClientResult<crate::models::ActorRun> {
let run_id = std::env::var("ACTOR_RUN_ID").map_err(|_| {
crate::error::ApifyClientError::InvalidArgument(
"ACTOR_RUN_ID environment variable is not set".to_string(),
)
})?;
let body = serde_json::json!({
"statusMessage": message,
"isStatusMessageTerminal": is_terminal,
});
self.run(run_id).update(&body).await
}
}