Skip to main content

apify_client/clients/
task.rs

1//! Client for a single Actor task (`/v2/actor-tasks/{actorTaskId}`).
2
3use serde::Serialize;
4use serde_json::Value;
5
6use crate::client::ApifyClient;
7use crate::clients::actor::ActorStartOptions;
8use crate::clients::base::{
9    delete_resource, get_resource, post_with_body, update_resource, ResourceContext,
10};
11use crate::clients::run::{LastRunOptions, RunClient};
12use crate::clients::run_collection::RunCollectionClient;
13use crate::clients::webhook_collection::WebhookCollectionClient;
14use crate::common::QueryParams;
15use crate::error::ApifyClientResult;
16use crate::http_client::HttpClient;
17use crate::models::{ActorRun, Task};
18
19/// Client for a specific Actor task.
20#[derive(Debug, Clone)]
21pub struct TaskClient {
22    root: ApifyClient,
23    ctx: ResourceContext,
24}
25
26impl TaskClient {
27    pub(crate) fn new(root: ApifyClient, http: HttpClient, base_url: &str, id: &str) -> Self {
28        Self {
29            root,
30            ctx: ResourceContext::single(http, base_url, "actor-tasks", id),
31        }
32    }
33
34    /// Fetches the task object, or `None` if it does not exist.
35    pub async fn get(&self) -> ApifyClientResult<Option<Task>> {
36        get_resource(&self.ctx, None, &QueryParams::new()).await
37    }
38
39    /// Updates the task with the given fields.
40    pub async fn update<T: Serialize>(&self, new_fields: &T) -> ApifyClientResult<Task> {
41        update_resource(&self.ctx, None, new_fields).await
42    }
43
44    /// Deletes the task.
45    pub async fn delete(&self) -> ApifyClientResult<()> {
46        delete_resource(&self.ctx, None).await
47    }
48
49    /// Starts the task and returns immediately with the created run.
50    ///
51    /// `input` overrides the task's saved input (or `None` to use the saved input).
52    pub async fn start<T: Serialize>(
53        &self,
54        input: Option<&T>,
55        options: ActorStartOptions,
56    ) -> ApifyClientResult<ActorRun> {
57        let mut params = QueryParams::new();
58        options.apply(&mut params);
59        let body = match input {
60            Some(value) => Some(serde_json::to_vec(value)?),
61            None => None,
62        };
63        post_with_body(&self.ctx, Some("runs"), &params, body, "application/json").await
64    }
65
66    /// Starts the task and waits (client-side polling) for it to finish.
67    ///
68    /// `wait_secs` controls the wait budget:
69    /// - `None` polls indefinitely until the run reaches a terminal state.
70    /// - `Some(n)` bounds the wait to roughly `n` seconds; if the run has not finished by
71    ///   then, the **last fetched (still non-terminal) run is returned** rather than an
72    ///   error. Check `status` / `is_terminal()` on the result when using `Some`.
73    pub async fn call<T: Serialize>(
74        &self,
75        input: Option<&T>,
76        options: ActorStartOptions,
77        wait_secs: Option<i64>,
78    ) -> ApifyClientResult<ActorRun> {
79        let run = self.start(input, options).await?;
80        self.root.run(run.id).wait_for_finish(wait_secs).await
81    }
82
83    /// Fetches the task's saved input, or `None` if not set.
84    pub async fn get_input(&self) -> ApifyClientResult<Option<Value>> {
85        let response =
86            crate::clients::base::get_raw(&self.ctx, Some("input"), &QueryParams::new()).await?;
87        match response {
88            Some(r) => Ok(Some(serde_json::from_slice(&r.body)?)),
89            None => Ok(None),
90        }
91    }
92
93    /// Updates the task's saved input.
94    pub async fn update_input<T: Serialize>(&self, input: &T) -> ApifyClientResult<Value> {
95        let body = serde_json::to_vec(input)?;
96        let url = self.ctx.url(Some("input"));
97        let mut headers = std::collections::HashMap::new();
98        headers.insert("Content-Type".to_string(), "application/json".to_string());
99        let response = self
100            .ctx
101            .http
102            .call(crate::http_client::HttpRequest {
103                method: crate::http_client::HttpMethod::Put,
104                url,
105                headers,
106                body: Some(body),
107                timeout: crate::clients::base::DEFAULT_REQUEST_TIMEOUT,
108            })
109            .await?;
110        Ok(serde_json::from_slice(&response.body)?)
111    }
112
113    /// Returns a client for the last run of this task, optionally filtered by run status.
114    ///
115    /// `status` filters by run status (e.g. `"SUCCEEDED"`, `"FAILED"`, `"RUNNING"`); pass `None`
116    /// to leave it unfiltered. This maps to the `status` query parameter on
117    /// `GET /v2/actor-tasks/{actorTaskId}/runs/last` and mirrors the reference client's
118    /// `lastRun({ status })`. To also filter by `origin`, use [`TaskClient::last_run_with_options`].
119    pub fn last_run(&self, status: Option<&str>) -> RunClient {
120        self.last_run_with_options(LastRunOptions {
121            status: status.map(str::to_owned),
122            origin: None,
123        })
124    }
125
126    /// Returns a client for the last run of this task, applying the given [`LastRunOptions`]
127    /// (e.g. [`LastRunOptions::status`] and/or [`LastRunOptions::origin`]).
128    ///
129    /// `status` filters by run status (e.g. `"SUCCEEDED"`, `"FAILED"`, `"RUNNING"`) and is the
130    /// spec's documented filter on `GET /v2/actor-tasks/{actorTaskId}/runs/last`. `origin` filters
131    /// by how the run was started; accepted values are the platform's run origins (e.g.
132    /// `"DEVELOPMENT"`, `"WEB"`, `"API"`, `"SCHEDULER"`). `origin` is not declared by the OpenAPI
133    /// spec and is sent only for parity with the reference client's `lastRun({ status, origin })`.
134    /// Both are sent as query parameters; leave a field as `None` to omit it.
135    pub fn last_run_with_options(&self, options: LastRunOptions) -> RunClient {
136        let mut client = RunClient::new(
137            self.root.clone(),
138            self.ctx.http.clone(),
139            &self.ctx.url(None),
140            "runs",
141            "last",
142        );
143        if let Some(status) = options.status.as_deref() {
144            client.set_base_param("status", status);
145        }
146        if let Some(origin) = options.origin.as_deref() {
147            client.set_base_param("origin", origin);
148        }
149        client
150    }
151
152    /// Returns a client for this task's run collection.
153    pub fn runs(&self) -> RunCollectionClient {
154        RunCollectionClient::new(self.ctx.http.clone(), &self.ctx.url(None), "runs")
155    }
156
157    /// Returns a client for this task's webhook collection.
158    pub fn webhooks(&self) -> WebhookCollectionClient {
159        WebhookCollectionClient::with_base(self.ctx.http.clone(), &self.ctx.url(None))
160    }
161}