drogue_client/admin/v1/
client.rs

1use super::data::*;
2use crate::core::CoreClient;
3use crate::error::ClientError;
4use crate::openid::TokenProvider;
5use std::{fmt::Debug, sync::Arc};
6use tracing::instrument;
7use url::Url;
8
9/// A client for drogue cloud application administration 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
17enum AdministrationOperation {
18    Transfer,
19    Accept,
20    Members,
21}
22
23type ClientResult<T> = Result<T, ClientError>;
24
25impl CoreClient for Client {
26    fn client(&self) -> &reqwest::Client {
27        &self.client
28    }
29
30    fn token_provider(&self) -> &dyn TokenProvider {
31        self.token_provider.as_ref()
32    }
33}
34
35impl Client {
36    /// Create a new client instance.
37    pub fn new(
38        client: reqwest::Client,
39        api_url: Url,
40        token_provider: impl TokenProvider + 'static,
41    ) -> Self {
42        Self {
43            client,
44            api_url,
45            token_provider: Arc::new(token_provider),
46        }
47    }
48
49    fn url(&self, application: &str, operation: AdministrationOperation) -> ClientResult<Url> {
50        let mut url = self.api_url.clone();
51
52        {
53            let mut path = url
54                .path_segments_mut()
55                .map_err(|_| ClientError::Request("Failed to get paths".into()))?;
56
57            path.extend(&["api", "admin", "v1alpha1", "apps"]);
58            if !application.is_empty() {
59                path.push(application);
60            }
61            match operation {
62                AdministrationOperation::Transfer => path.push("transfer-ownership"),
63                AdministrationOperation::Accept => path.push("accept-ownership"),
64                AdministrationOperation::Members => path.push("members"),
65            };
66        }
67
68        Ok(url)
69    }
70
71    /// Get the application members and their roles
72    #[instrument]
73    pub async fn get_members<A>(&self, application: A) -> ClientResult<Option<Members>>
74    where
75        A: AsRef<str> + Debug,
76    {
77        self.read(self.url(application.as_ref(), AdministrationOperation::Members)?)
78            .await
79    }
80
81    /// Update the application members and their roles
82    #[instrument]
83    pub async fn update_members<A>(&self, application: A, members: Members) -> ClientResult<bool>
84    where
85        A: AsRef<str> + Debug,
86    {
87        self.update(
88            self.url(application.as_ref(), AdministrationOperation::Members)?,
89            Some(members),
90        )
91        .await
92    }
93
94    /// Transfer the application ownership to another user
95    #[instrument]
96    pub async fn initiate_app_transfer<A, U>(
97        &self,
98        application: A,
99        username: U,
100    ) -> ClientResult<bool>
101    where
102        A: AsRef<str> + Debug,
103        U: AsRef<str> + Debug,
104    {
105        let payload = TransferOwnership {
106            new_user: username.as_ref().to_string(),
107        };
108
109        self.update(
110            self.url(application.as_ref(), AdministrationOperation::Transfer)?,
111            Some(payload),
112        )
113        .await
114    }
115
116    /// Cancel the application ownership transfer
117    #[instrument]
118    pub async fn cancel_app_transfer<A>(&self, application: A) -> ClientResult<bool>
119    where
120        A: AsRef<str> + Debug,
121    {
122        self.delete(self.url(application.as_ref(), AdministrationOperation::Transfer)?)
123            .await
124    }
125
126    /// Accept the application ownership transfer
127    #[instrument]
128    pub async fn accept_app_transfer<A>(&self, application: A) -> ClientResult<bool>
129    where
130        A: AsRef<str> + Debug,
131    {
132        self.update(
133            self.url(application.as_ref(), AdministrationOperation::Accept)?,
134            None::<()>,
135        )
136        .await
137    }
138
139    /// Read the application ownership transfer state
140    #[instrument]
141    pub async fn read_app_transfer<A>(
142        &self,
143        application: A,
144    ) -> ClientResult<Option<TransferOwnership>>
145    where
146        A: AsRef<str> + Debug,
147    {
148        self.read(self.url(application.as_ref(), AdministrationOperation::Transfer)?)
149            .await
150    }
151}