//! Enabled by the `async` feature. Contains blocking implementations of the pre-written queries. //! //! On the docs page of this module, you can only see the builder structs for the functions. //! //! Please see [`R3Client`] for the actual functions you can call. use crate::auth::{build_auth_header, get_date}; use 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}; use crate::{R3Client, BASE_URL, GRAPHQL_PATH}; use bon::bon; use graphql_client::{GraphQLQuery, QueryBody, Response}; use reqwest::Client; use reqwest::Method; use serde::{Deserialize, Serialize}; use std::error::Error; #[bon] impl R3Client { /// Sends a signed GraphQL request to the remote.it API in a blocking way. /// /// You probably don't want to use this function directly, but rather use the other functions in this module like [`R3Client::get_files()`]. /// /// # Errors /// - Any error that occurs during the request. /// - Any error that occurs during deserialization of the response. pub async fn send_remoteit_graphql_request_async<V: Serialize, R: for<'a> Deserialize<'a>>( &self, query_body: &QueryBody<V>, ) -> Result<Response<R>, Box<dyn Error>> { let date = get_date(); let auth_header = build_auth_header() .key_id(&self.credentials.r3_access_key_id) .key(&self.credentials.key) .content_type("application/json") .method(&Method::POST) .path(GRAPHQL_PATH) .date(&date) .call(); let client = Client::new(); let response = client .post(format!("{BASE_URL}{GRAPHQL_PATH}")) .header("Date", date) .header("Content-Type", "application/json") .header("Authorization", auth_header) .json(&query_body) .send() .await?; let response: Response<R> = response.json().await?; Ok(response) } // region Scripting /// Get a list of files that were uploaded to remote.it. #[builder] pub async fn get_files_async( &self, /// Optional organization ID for org context. org_id: Option<String>, ) -> Result<Response<get_files::ResponseData>, Box<dyn Error>> { let request_body = GetFiles::build_query(get_files::Variables { org_id }); self.send_remoteit_graphql_request_async(&request_body) .await } /// Delete a file from remote.it. Deletes all versions of the file. #[builder] pub async fn delete_file_async( &self, /// The ID of the file to delete. /// You can get this from the response of [`R3Client::get_files()`]. file_id: String, ) -> Result<Response<delete_file::ResponseData>, Box<dyn Error>> { let request_body = DeleteFile::build_query(delete_file::Variables { file_id }); self.send_remoteit_graphql_request_async(&request_body) .await } /// Delete a version of a file from remote.it. (Not the whole file) #[builder] pub async fn delete_file_version_async( &self, /// The ID of the file version to delete. /// You can get this from the response of [`R3Client::get_files()`]. file_version_id: String, ) -> Result<Response<delete_file_version::ResponseData>, Box<dyn Error>> { let request_body = DeleteFileVersion::build_query(delete_file_version::Variables { file_version_id }); self.send_remoteit_graphql_request_async(&request_body) .await } /// Start scripting jobs on one or more devices. #[builder] pub async fn start_job_async( &self, /// The ID of the script file to run. /// Note that this needs to be an executable file. /// Get a list of files using [`R3Client::get_files()`]. file_id: String, /// The IDs of the devices to run the script on. /// Get a list of devices using [`R3Client::get_devices()`]. device_ids: Vec<String>, /// Arguments to pass to the script. /// These are optional. /// For more information on script arguments please consult the remote.it API documentation. #[builder(default)] arguments: Vec<start_job::ArgumentInput>, ) -> Result<Response<start_job::ResponseData>, Box<dyn Error>> { let request_body = StartJob::build_query(start_job::Variables { file_id, device_ids, arguments, }); self.send_remoteit_graphql_request_async(&request_body) .await } /// Cancel a job. See remote.it docs on more information on when jobs can be cancelled. #[builder] pub async fn cancel_job_async( &self, /// The ID of the job to cancel. /// You get this after starting a job using [`R3Client::start_job()`]. job_id: String, ) -> Result<Response<cancel_job::ResponseData>, Box<dyn Error>> { let request_body = CancelJob::build_query(cancel_job::Variables { job_id }); self.send_remoteit_graphql_request_async(&request_body) .await } /// Get a list of jobs that were started on remote.it. #[builder] pub async fn get_jobs_async( &self, /// Optional organization ID for org context. org_id: Option<String>, /// Optional limit how many results are returned. It is highly recommended to set a limit, because this query can take quite a while otherwise. limit: Option<i64>, /// Optional list of job IDs to filter by. job_id_filter: Option<Vec<String>>, /// Optional list of job statuses to filter by. status_filter: Option<Vec<get_jobs::JobStatusEnum>>, ) -> Result<Response<get_jobs::ResponseData>, Box<dyn Error>> { let request_body = GetJobs::build_query(get_jobs::Variables { org_id, limit, job_ids: job_id_filter, statuses: status_filter, }); self.send_remoteit_graphql_request_async(&request_body) .await } // endregion // region Organizations /// Get data on your own organization, which belongs to the current user. /// This Organization may or may not exist. You can create and configure your organization through the remote.it Web UI. /// /// # Returns /// Data on your organization, if you have one. #[builder] pub async fn get_owned_organization_async( &self, ) -> Result<Response<get_owned_organization::ResponseData>, Box<dyn Error>> { let request_body = GetOwnedOrganization::build_query(get_owned_organization::Variables {}); self.send_remoteit_graphql_request_async(&request_body) .await } // endregion // region Devices and Services /// Get a list of application types that are available on remote.it. #[builder] pub async fn get_application_types_async( &self, ) -> Result<Response<get_application_types::ResponseData>, Box<dyn Error>> { let request_body = GetApplicationTypes::build_query(get_application_types::Variables {}); self.send_remoteit_graphql_request_async(&request_body) .await } /// Get a list of devices. #[builder] pub async fn get_devices_async( &self, /// Optional organization ID for org context. org_id: Option<String>, /// Optional limit for the number of devices to return. limit: Option<i64>, /// Optional offset for the devices. Useful for pagination. offset: Option<i64>, /// Optional state to filter by. state: Option<DeviceState> ) -> Result<Response<get_devices::ResponseData>, Box<dyn Error>> { let request_body = GetDevices::build_query(get_devices::Variables { org_id, limit, offset, state: state.map(|s| s.to_string()), }); self.send_remoteit_graphql_request_async(&request_body) .await } // endregion } #[cfg(test)] mod tests { use super::*; use crate::credentials::Credentials; use std::path::PathBuf; fn get_credentials() -> Credentials { Credentials::load_from_disk() .custom_credentials_path(PathBuf::from(".env.remoteit")) .call() .unwrap() .take_profile("default") .unwrap() .unwrap() } fn get_client() -> R3Client { R3Client::builder().credentials(get_credentials()).build() } #[tokio::test] async fn test_get_files_async() { let response = get_client().get_files_async().call().await.unwrap(); assert!(response.data.is_some()); assert!(response.errors.is_none()); } #[tokio::test] async fn test_get_jobs_async() { let response = get_client().get_jobs_async().limit(1).call().await.unwrap(); assert!(response.data.is_some()); assert!(response.errors.is_none()); } #[tokio::test] async fn test_get_jobs_with_filters_async() { let response = get_client() .get_jobs_async() .job_id_filter(vec!["foobar".to_string()]) .status_filter(vec![get_jobs::JobStatusEnum::SUCCESS]) .call() .await .unwrap(); assert!(response.data.is_some()); assert!(response.errors.is_none()); } #[tokio::test] async fn test_get_application_types_async() { let response = get_client() .get_application_types_async() .call() .await .unwrap(); assert!(response.data.is_some()); assert!(response.errors.is_none()); } #[tokio::test] async fn test_get_devices_async() { let response = get_client().get_devices_async().call().await.unwrap(); assert!(response.data.is_some()); assert!(response.errors.is_none()); } }