drogue_client/command/v1/
client.rs

1use crate::core::CoreClient;
2use crate::error::ClientError;
3use crate::openid::TokenProvider;
4use serde::Serialize;
5use std::{fmt::Debug, sync::Arc};
6use tracing::instrument;
7use url::Url;
8
9/// A client for drogue cloud command and control API, backed by reqwest.
10#[derive(Clone, Debug)]
11pub struct Client {
12    client: reqwest::Client,
13    api_url: Url,
14    token_provider: Arc<dyn TokenProvider>,
15}
16
17type ClientResult<T> = Result<T, ClientError>;
18
19impl CoreClient for Client {
20    fn client(&self) -> &reqwest::Client {
21        &self.client
22    }
23
24    fn token_provider(&self) -> &dyn TokenProvider {
25        self.token_provider.as_ref()
26    }
27}
28
29impl Client {
30    /// Create a new client instance.
31    pub fn new(
32        client: reqwest::Client,
33        api_url: Url,
34        token_provider: impl TokenProvider + 'static,
35    ) -> Self {
36        Self {
37            client,
38            api_url,
39            token_provider: Arc::new(token_provider),
40        }
41    }
42
43    fn url(&self, application: &str, device: &str) -> ClientResult<Url> {
44        let mut url = self.api_url.clone();
45
46        {
47            let mut path = url
48                .path_segments_mut()
49                .map_err(|_| ClientError::Request("Failed to get paths".into()))?;
50
51            path.extend(&[
52                "api",
53                "command",
54                "v1alpha1",
55                "apps",
56                application,
57                "devices",
58                device,
59            ]);
60        }
61
62        Ok(url)
63    }
64
65    /// Send one way commands to devices.
66    ///
67    /// The result will be true if the command was accepted.
68    /// False
69    #[instrument(skip(payload))]
70    pub async fn publish_command<A, D, C, P>(
71        &self,
72        application: A,
73        device: D,
74        command: C,
75        payload: Option<P>,
76    ) -> ClientResult<Option<()>>
77    where
78        A: AsRef<str> + Debug,
79        D: AsRef<str> + Debug,
80        C: AsRef<str> + Debug,
81        P: Serialize + Send + Sync,
82    {
83        let url = self.url(application.as_ref(), device.as_ref())?;
84        let query = vec![("command".to_string(), command.as_ref().to_string())];
85
86        self.create_with_query_parameters(url, payload, Some(query))
87            .await
88    }
89}