remoteit_api/
api_blocking.rs

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