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