1use serde::Serialize;
4use serde_json::Value;
5
6use crate::client::ApifyClient;
7use crate::clients::actor_version::ActorVersionClient;
8use crate::clients::actor_version_collection::ActorVersionCollectionClient;
9use crate::clients::base::{
10 delete_resource, get_resource, post_with_body, update_resource, ResourceContext,
11};
12use crate::clients::build::BuildClient;
13use crate::clients::build_collection::BuildCollectionClient;
14use crate::clients::run::{LastRunOptions, RunClient};
15use crate::clients::run_collection::RunCollectionClient;
16use crate::clients::webhook_collection::WebhookCollectionClient;
17use crate::common::{parse_data_envelope, QueryParams};
18use crate::error::ApifyClientResult;
19use crate::http_client::HttpClient;
20use crate::models::{Actor, ActorRun, Build};
21
22#[derive(Debug, Default, Clone)]
25pub struct ActorStartOptions {
26 pub build: Option<String>,
28 pub memory_mbytes: Option<i64>,
30 pub timeout_secs: Option<i64>,
32 pub wait_for_finish: Option<i64>,
34 pub max_items: Option<i64>,
36 pub max_total_charge_usd: Option<f64>,
38 pub content_type: Option<String>,
40 pub restart_on_error: Option<bool>,
42 pub force_permission_level: Option<String>,
44 pub webhooks: Option<Vec<serde_json::Value>>,
47}
48
49impl ActorStartOptions {
50 pub(crate) fn apply(&self, params: &mut QueryParams) {
53 params
54 .add_str("build", self.build.clone())
55 .add_int("memory", self.memory_mbytes)
56 .add_int("timeout", self.timeout_secs)
57 .add_int("waitForFinish", self.wait_for_finish)
58 .add_int("maxItems", self.max_items)
59 .add_float("maxTotalChargeUsd", self.max_total_charge_usd)
60 .add_bool("restartOnError", self.restart_on_error)
61 .add_str("forcePermissionLevel", self.force_permission_level.clone())
62 .add_str("webhooks", self.encoded_webhooks());
63 }
64
65 fn encoded_webhooks(&self) -> Option<String> {
67 use base64::Engine;
68 let webhooks = self.webhooks.as_ref()?;
69 let json = serde_json::to_vec(webhooks).ok()?;
70 Some(base64::engine::general_purpose::STANDARD.encode(json))
71 }
72}
73
74#[derive(Debug, Default, Clone)]
76pub struct ActorBuildOptions {
77 pub beta_packages: Option<bool>,
79 pub tag: Option<String>,
81 pub use_cache: Option<bool>,
83 pub wait_for_finish: Option<i64>,
85}
86
87#[derive(Debug, Clone)]
92pub struct ActorClient {
93 root: ApifyClient,
94 ctx: ResourceContext,
95 base_url: String,
96 id: String,
97}
98
99impl ActorClient {
100 pub(crate) fn new(root: ApifyClient, http: HttpClient, base_url: &str, id: &str) -> Self {
101 Self {
102 root,
103 ctx: ResourceContext::single(http, base_url, "actors", id),
104 base_url: base_url.to_string(),
105 id: id.to_string(),
106 }
107 }
108
109 pub async fn get(&self) -> ApifyClientResult<Option<Actor>> {
111 get_resource(&self.ctx, None, &QueryParams::new()).await
112 }
113
114 pub async fn update<T: Serialize>(&self, new_fields: &T) -> ApifyClientResult<Actor> {
116 update_resource(&self.ctx, None, new_fields).await
117 }
118
119 pub async fn delete(&self) -> ApifyClientResult<()> {
121 delete_resource(&self.ctx, None).await
122 }
123
124 pub async fn start<T: Serialize>(
129 &self,
130 input: Option<&T>,
131 options: ActorStartOptions,
132 ) -> ApifyClientResult<ActorRun> {
133 let body = match input {
134 Some(value) => Some(serde_json::to_vec(value)?),
135 None => None,
136 };
137 self.start_with_body(body, "application/json", options)
138 .await
139 }
140
141 pub async fn start_raw(
149 &self,
150 input: &[u8],
151 options: ActorStartOptions,
152 ) -> ApifyClientResult<ActorRun> {
153 self.start_with_body(Some(input.to_vec()), "application/octet-stream", options)
154 .await
155 }
156
157 async fn start_with_body(
161 &self,
162 body: Option<Vec<u8>>,
163 default_content_type: &str,
164 options: ActorStartOptions,
165 ) -> ApifyClientResult<ActorRun> {
166 let mut params = QueryParams::new();
167 options.apply(&mut params);
168 let content_type = options
169 .content_type
170 .clone()
171 .unwrap_or_else(|| default_content_type.to_string());
172 post_with_body(&self.ctx, Some("runs"), ¶ms, body, &content_type).await
173 }
174
175 pub async fn call<T: Serialize>(
183 &self,
184 input: Option<&T>,
185 options: ActorStartOptions,
186 wait_secs: Option<i64>,
187 ) -> ApifyClientResult<ActorRun> {
188 let run = self.start(input, options).await?;
189 self.root.run(run.id).wait_for_finish(wait_secs).await
191 }
192
193 pub async fn call_raw(
196 &self,
197 input: &[u8],
198 options: ActorStartOptions,
199 wait_secs: Option<i64>,
200 ) -> ApifyClientResult<ActorRun> {
201 let run = self.start_raw(input, options).await?;
202 self.root.run(run.id).wait_for_finish(wait_secs).await
203 }
204
205 pub async fn build(
207 &self,
208 version_number: &str,
209 options: ActorBuildOptions,
210 ) -> ApifyClientResult<Build> {
211 let mut params = QueryParams::new();
212 params
213 .add_str("version", Some(version_number.to_string()))
214 .add_bool("betaPackages", options.beta_packages)
215 .add_str("tag", options.tag)
216 .add_bool("useCache", options.use_cache)
217 .add_int("waitForFinish", options.wait_for_finish);
218 post_with_body(&self.ctx, Some("builds"), ¶ms, None, "application/json").await
219 }
220
221 pub async fn default_build(
226 &self,
227 wait_for_finish: Option<i64>,
228 ) -> ApifyClientResult<BuildClient> {
229 let mut params = QueryParams::new();
230 params.add_int("waitForFinish", wait_for_finish);
231 let url = params.apply_to_url(&self.ctx.url(Some("builds/default")));
232 let response = self
233 .ctx
234 .http
235 .call(crate::http_client::HttpRequest {
236 method: crate::http_client::HttpMethod::Get,
237 url,
238 headers: Default::default(),
239 body: None,
240 timeout: crate::clients::base::DEFAULT_REQUEST_TIMEOUT,
241 })
242 .await?;
243 let build: Build = parse_data_envelope(&response.body)?;
244 Ok(BuildClient::new(
245 self.ctx.http.clone(),
246 &self.base_url,
247 &build.id,
248 ))
249 }
250
251 pub async fn validate_input<T: Serialize>(&self, input: &T) -> ApifyClientResult<Value> {
256 self.validate_input_for_build(input, None).await
257 }
258
259 pub async fn validate_input_for_build<T: Serialize>(
266 &self,
267 input: &T,
268 build: Option<&str>,
269 ) -> ApifyClientResult<Value> {
270 let body = serde_json::to_vec(input)?;
271 let mut params = QueryParams::new();
272 params.add_str("build", build);
273 crate::clients::base::post_action_raw(
276 &self.ctx,
277 Some("validate-input"),
278 ¶ms,
279 Some(body),
280 Some("application/json"),
281 )
282 .await
283 }
284
285 pub fn last_run(&self, status: Option<&str>) -> RunClient {
292 self.last_run_with_options(LastRunOptions {
293 status: status.map(str::to_owned),
294 origin: None,
295 })
296 }
297
298 pub fn last_run_with_options(&self, options: LastRunOptions) -> RunClient {
307 let mut client = RunClient::new(
308 self.root.clone(),
309 self.ctx.http.clone(),
310 &self.ctx.url(None),
311 "runs",
312 "last",
313 );
314 if let Some(status) = options.status.as_deref() {
315 client.set_base_param("status", status);
316 }
317 if let Some(origin) = options.origin.as_deref() {
318 client.set_base_param("origin", origin);
319 }
320 client
321 }
322
323 pub fn builds(&self) -> BuildCollectionClient {
325 BuildCollectionClient::with_base(self.ctx.http.clone(), &self.ctx.url(None), "builds")
326 }
327
328 pub fn runs(&self) -> RunCollectionClient {
330 RunCollectionClient::new(self.ctx.http.clone(), &self.ctx.url(None), "runs")
331 }
332
333 pub fn version(&self, version_number: &str) -> ActorVersionClient {
335 ActorVersionClient::new(self.ctx.http.clone(), &self.ctx.url(None), version_number)
336 }
337
338 pub fn versions(&self) -> ActorVersionCollectionClient {
340 ActorVersionCollectionClient::new(self.ctx.http.clone(), &self.ctx.url(None))
341 }
342
343 pub fn webhooks(&self) -> WebhookCollectionClient {
345 WebhookCollectionClient::with_base(self.ctx.http.clone(), &self.ctx.url(None))
346 }
347
348 pub fn id(&self) -> &str {
350 &self.id
351 }
352}