remoteit_api/
api_async.rs

1//! Enabled by the `async` feature. Contains blocking implementations of the pre-written queries.
2//!
3//! On the docs page of this module, you can only see the builder structs for the functions.
4//!
5//! Please see [`R3Client`] for the actual functions you can call.
6
7use crate::auth::{build_auth_header, get_date};
8use crate::operations::{cancel_job, delete_file, delete_file_version, get_application_types, get_devices, get_files, get_jobs, get_owned_organization, start_job, CancelJob, DeleteFile, DeleteFileVersion, DeviceState, GetApplicationTypes, GetDevices, GetFiles, GetJobs, GetOwnedOrganization, StartJob};
9use crate::{R3Client, BASE_URL, GRAPHQL_PATH};
10use bon::bon;
11use graphql_client::{GraphQLQuery, QueryBody, Response};
12use reqwest::Client;
13use reqwest::Method;
14use serde::{Deserialize, Serialize};
15use std::error::Error;
16
17#[bon]
18impl R3Client {
19    /// Sends a signed GraphQL request to the remote.it API in a blocking way.
20    ///
21    /// You probably don't want to use this function directly, but rather use the other functions in this module like [`R3Client::get_files()`].
22    ///
23    /// # Errors
24    /// - Any error that occurs during the request.
25    /// - Any error that occurs during deserialization of the response.
26    pub async fn send_remoteit_graphql_request_async<V: Serialize, R: for<'a> Deserialize<'a>>(
27        &self,
28        query_body: &QueryBody<V>,
29    ) -> Result<Response<R>, Box<dyn Error>> {
30        let date = get_date();
31        let auth_header = build_auth_header()
32            .key_id(self.credentials.access_key_id())
33            .key(self.credentials.key())
34            .content_type("application/json")
35            .method(&Method::POST)
36            .path(GRAPHQL_PATH)
37            .date(&date)
38            .call();
39        let client = Client::new();
40        let response = client
41            .post(format!("{BASE_URL}{GRAPHQL_PATH}"))
42            .header("Date", date)
43            .header("Content-Type", "application/json")
44            .header("Authorization", auth_header)
45            .json(&query_body)
46            .send()
47            .await?;
48        let response: Response<R> = response.json().await?;
49        Ok(response)
50    }
51
52    // region Scripting
53
54    /// Get a list of files that were uploaded to remote.it.
55    #[builder]
56    pub async fn get_files_async(
57        &self,
58        /// Optional organization ID for org context.
59        org_id: Option<String>,
60    ) -> Result<Response<get_files::ResponseData>, Box<dyn Error>> {
61        let request_body = GetFiles::build_query(get_files::Variables { org_id });
62        self.send_remoteit_graphql_request_async(&request_body)
63            .await
64    }
65
66    /// Delete a file from remote.it. Deletes all versions of the file.
67    #[builder]
68    pub async fn delete_file_async(
69        &self,
70        /// The ID of the file to delete.
71        /// You can get this from the response of [`R3Client::get_files()`].
72        file_id: String,
73    ) -> Result<Response<delete_file::ResponseData>, Box<dyn Error>> {
74        let request_body = DeleteFile::build_query(delete_file::Variables { file_id });
75        self.send_remoteit_graphql_request_async(&request_body)
76            .await
77    }
78
79    /// Delete a version of a file from remote.it. (Not the whole file)
80    #[builder]
81    pub async fn delete_file_version_async(
82        &self,
83        /// The ID of the file version to delete.
84        /// You can get this from the response of [`R3Client::get_files()`].
85        file_version_id: String,
86    ) -> Result<Response<delete_file_version::ResponseData>, Box<dyn Error>> {
87        let request_body =
88            DeleteFileVersion::build_query(delete_file_version::Variables { file_version_id });
89        self.send_remoteit_graphql_request_async(&request_body)
90            .await
91    }
92
93    /// Start scripting jobs on one or more devices.
94    #[builder]
95    pub async fn start_job_async(
96        &self,
97        /// The ID of the script file to run.
98        /// Note that this needs to be an executable file.
99        /// Get a list of files using [`R3Client::get_files()`].
100        file_id: String,
101        /// The IDs of the devices to run the script on.
102        /// Get a list of devices using [`R3Client::get_devices()`].
103        device_ids: Vec<String>,
104        /// Arguments to pass to the script.
105        /// These are optional.
106        /// For more information on script arguments please consult the remote.it API documentation.
107        #[builder(default)]
108        arguments: Vec<start_job::ArgumentInput>,
109    ) -> Result<Response<start_job::ResponseData>, Box<dyn Error>> {
110        let request_body = StartJob::build_query(start_job::Variables {
111            file_id,
112            device_ids,
113            arguments,
114        });
115        self.send_remoteit_graphql_request_async(&request_body)
116            .await
117    }
118
119    /// Cancel a job. See remote.it docs on more information on when jobs can be cancelled.
120    #[builder]
121    pub async fn cancel_job_async(
122        &self,
123        /// The ID of the job to cancel.
124        /// You get this after starting a job using [`R3Client::start_job()`].
125        job_id: String,
126    ) -> Result<Response<cancel_job::ResponseData>, Box<dyn Error>> {
127        let request_body = CancelJob::build_query(cancel_job::Variables { job_id });
128        self.send_remoteit_graphql_request_async(&request_body)
129            .await
130    }
131
132    /// Get a list of jobs that were started on remote.it.
133    #[builder]
134    pub async fn get_jobs_async(
135        &self,
136        /// Optional organization ID for org context.
137        org_id: Option<String>,
138        /// Optional limit how many results are returned. It is highly recommended to set a limit, because this query can take quite a while otherwise.
139        limit: Option<i64>,
140        /// Optional list of job IDs to filter by.
141        job_id_filter: Option<Vec<String>>,
142        /// Optional list of job statuses to filter by.
143        status_filter: Option<Vec<get_jobs::JobStatusEnum>>,
144    ) -> Result<Response<get_jobs::ResponseData>, Box<dyn Error>> {
145        let request_body = GetJobs::build_query(get_jobs::Variables {
146            org_id,
147            limit,
148            job_ids: job_id_filter,
149            statuses: status_filter,
150        });
151        self.send_remoteit_graphql_request_async(&request_body)
152            .await
153    }
154
155    // endregion
156    // region Organizations
157    /// Get data on your own organization, which belongs to the current user.
158    /// This Organization may or may not exist. You can create and configure your organization through the remote.it Web UI.
159    ///
160    /// # Returns
161    /// Data on your organization, if you have one.
162    #[builder]
163    pub async fn get_owned_organization_async(
164        &self,
165    ) -> Result<Response<get_owned_organization::ResponseData>, Box<dyn Error>> {
166        let request_body = GetOwnedOrganization::build_query(get_owned_organization::Variables {});
167        self.send_remoteit_graphql_request_async(&request_body)
168            .await
169    }
170    // endregion
171    // region Devices and Services
172
173    /// Get a list of application types that are available on remote.it.
174    #[builder]
175    pub async fn get_application_types_async(
176        &self,
177    ) -> Result<Response<get_application_types::ResponseData>, Box<dyn Error>> {
178        let request_body = GetApplicationTypes::build_query(get_application_types::Variables {});
179        self.send_remoteit_graphql_request_async(&request_body)
180            .await
181    }
182
183    /// Get a list of devices.
184    #[builder]
185    pub async fn get_devices_async(
186        &self,
187        /// Optional organization ID for org context.
188        org_id: Option<String>,
189        /// Optional limit for the number of devices to return.
190        limit: Option<i64>,
191        /// Optional offset for the devices. Useful for pagination.
192        offset: Option<i64>,
193        /// Optional state to filter by.
194        state: Option<DeviceState>
195    ) -> Result<Response<get_devices::ResponseData>, Box<dyn Error>> {
196        let request_body = GetDevices::build_query(get_devices::Variables {
197            org_id,
198            limit,
199            offset,
200            state: state.map(|s| s.to_string()),
201        });
202        self.send_remoteit_graphql_request_async(&request_body)
203            .await
204    }
205
206    // endregion
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212    use crate::credentials::Credentials;
213    use std::path::PathBuf;
214
215    fn get_credentials() -> Credentials {
216        Credentials::load_from_disk()
217            .custom_credentials_path(PathBuf::from(".env.remoteit"))
218            .call()
219            .unwrap()
220            .take_profile("default")
221            .unwrap()
222            .unwrap()
223    }
224
225    fn get_client() -> R3Client {
226        R3Client::builder().credentials(get_credentials()).build()
227    }
228
229    #[tokio::test]
230    async fn test_get_files_async() {
231        let response = get_client().get_files_async().call().await.unwrap();
232        assert!(response.data.is_some());
233        assert!(response.errors.is_none());
234    }
235
236    #[tokio::test]
237    async fn test_get_jobs_async() {
238        let response = get_client().get_jobs_async().limit(1).call().await.unwrap();
239        assert!(response.data.is_some());
240        assert!(response.errors.is_none());
241    }
242
243    #[tokio::test]
244    async fn test_get_jobs_with_filters_async() {
245        let response = get_client()
246            .get_jobs_async()
247            .job_id_filter(vec!["foobar".to_string()])
248            .status_filter(vec![get_jobs::JobStatusEnum::SUCCESS])
249            .call()
250            .await
251            .unwrap();
252        assert!(response.data.is_some());
253        assert!(response.errors.is_none());
254    }
255
256    #[tokio::test]
257    async fn test_get_application_types_async() {
258        let response = get_client()
259            .get_application_types_async()
260            .call()
261            .await
262            .unwrap();
263        assert!(response.data.is_some());
264        assert!(response.errors.is_none());
265    }
266
267    #[tokio::test]
268    async fn test_get_devices_async() {
269        let response = get_client().get_devices_async().call().await.unwrap();
270        assert!(response.data.is_some());
271        assert!(response.errors.is_none());
272    }
273}