edc_connector_client/
client.rs

1use std::{future::Future, sync::Arc};
2
3use reqwest::{Client, RequestBuilder, Response};
4use serde::{de::DeserializeOwned, Serialize};
5
6use crate::{
7    api::{
8        AssetApi, CatalogApi, ContractAgreementApi, ContractDefinitionApi, ContractNegotiationApi,
9        DataPlaneApi, EdrApi, PolicyApi, SecretsApi, TransferProcessApi,
10    },
11    error::{
12        BuilderError, ManagementApiError, ManagementApiErrorDetail, ManagementApiErrorDetailKind,
13    },
14    EdcResult, Error,
15};
16
17#[derive(Clone)]
18pub struct EdcConnectorClient(Arc<EdcConnectorClientInternal>);
19
20pub(crate) struct EdcConnectorClientInternal {
21    client: Client,
22    pub(crate) management_url: String,
23    pub(crate) auth: Auth,
24}
25
26impl EdcConnectorClientInternal {
27    pub(crate) fn new(client: Client, management_url: String, auth: Auth) -> Self {
28        Self {
29            client,
30            management_url,
31            auth,
32        }
33    }
34
35    pub(crate) async fn get<R: DeserializeOwned>(&self, path: impl AsRef<str>) -> EdcResult<R> {
36        let response = self
37            .client
38            .get(path.as_ref())
39            .authenticated(&self.auth)
40            .send()
41            .await?;
42
43        self.handle_response(response, as_json).await
44    }
45
46    pub(crate) async fn put(&self, path: impl AsRef<str>, body: &impl Serialize) -> EdcResult<()> {
47        let response = self
48            .client
49            .put(path.as_ref())
50            .json(body)
51            .authenticated(&self.auth)
52            .send()
53            .await?;
54
55        self.handle_response(response, empty).await
56    }
57
58    pub(crate) async fn del(&self, path: impl AsRef<str>) -> EdcResult<()> {
59        let response = self
60            .client
61            .delete(path.as_ref())
62            .authenticated(&self.auth)
63            .send()
64            .await?;
65
66        self.handle_response(response, empty).await
67    }
68
69    pub(crate) async fn post<I: Serialize, R: DeserializeOwned>(
70        &self,
71        path: impl AsRef<str>,
72        body: &I,
73    ) -> EdcResult<R> {
74        self.internal_post(path, body, as_json).await
75    }
76
77    pub(crate) async fn post_no_response<I: Serialize>(
78        &self,
79        path: impl AsRef<str>,
80        body: &I,
81    ) -> EdcResult<()> {
82        self.internal_post(path, body, empty).await
83    }
84
85    async fn internal_post<I, F, Fut, R>(
86        &self,
87        path: impl AsRef<str>,
88        body: &I,
89        handler: F,
90    ) -> EdcResult<R>
91    where
92        I: Serialize,
93        F: Fn(Response) -> Fut,
94        Fut: Future<Output = EdcResult<R>>,
95    {
96        let response = self
97            .client
98            .post(path.as_ref())
99            .json(body)
100            .authenticated(&self.auth)
101            .send()
102            .await?;
103
104        self.handle_response(response, handler).await
105    }
106
107    async fn handle_response<F, Fut, R>(&self, response: Response, handler: F) -> EdcResult<R>
108    where
109        F: Fn(Response) -> Fut,
110        Fut: Future<Output = EdcResult<R>>,
111    {
112        if response.status().is_success() {
113            handler(response).await
114        } else {
115            let status = response.status();
116            let text = response.text().await?;
117
118            let err = match serde_json::from_str::<Vec<ManagementApiErrorDetail>>(&text) {
119                Ok(parsed) => ManagementApiErrorDetailKind::Parsed(parsed),
120                Err(_) => ManagementApiErrorDetailKind::Raw(text),
121            };
122
123            Err(Error::ManagementApi(ManagementApiError {
124                status_code: status,
125                error_detail: err,
126            }))
127        }
128    }
129}
130
131async fn as_json<R: DeserializeOwned>(response: Response) -> EdcResult<R> {
132    response.json().await.map(Ok)?
133}
134
135async fn empty(_response: Response) -> EdcResult<()> {
136    Ok(())
137}
138
139impl EdcConnectorClient {
140    pub(crate) fn new(client: Client, management_url: String, auth: Auth) -> Self {
141        Self(Arc::new(EdcConnectorClientInternal::new(
142            client,
143            management_url,
144            auth,
145        )))
146    }
147
148    pub fn builder() -> EdcClientConnectorBuilder {
149        EdcClientConnectorBuilder::default()
150    }
151
152    pub fn assets(&self) -> AssetApi<'_> {
153        AssetApi::new(&self.0)
154    }
155
156    pub fn policies(&self) -> PolicyApi<'_> {
157        PolicyApi::new(&self.0)
158    }
159
160    pub fn contract_definitions(&self) -> ContractDefinitionApi<'_> {
161        ContractDefinitionApi::new(&self.0)
162    }
163
164    pub fn catalogue(&self) -> CatalogApi<'_> {
165        CatalogApi::new(&self.0)
166    }
167
168    pub fn contract_negotiations(&self) -> ContractNegotiationApi<'_> {
169        ContractNegotiationApi::new(&self.0)
170    }
171
172    pub fn contract_agreements(&self) -> ContractAgreementApi<'_> {
173        ContractAgreementApi::new(&self.0)
174    }
175
176    pub fn transfer_processes(&self) -> TransferProcessApi<'_> {
177        TransferProcessApi::new(&self.0)
178    }
179
180    pub fn data_planes(&self) -> DataPlaneApi<'_> {
181        DataPlaneApi::new(&self.0)
182    }
183
184    pub fn edrs(&self) -> EdrApi<'_> {
185        EdrApi::new(&self.0)
186    }
187
188    pub fn secrets(&self) -> SecretsApi<'_> {
189        SecretsApi::new(&self.0)
190    }
191}
192
193#[derive(Clone)]
194pub enum Auth {
195    NoAuth,
196    ApiToken(String),
197}
198
199impl Auth {
200    pub fn api_token(token: impl Into<String>) -> Auth {
201        Auth::ApiToken(token.into())
202    }
203}
204
205pub struct EdcClientConnectorBuilder {
206    management_url: Option<String>,
207    auth: Auth,
208}
209
210impl EdcClientConnectorBuilder {
211    pub fn management_url(mut self, url: impl Into<String>) -> Self {
212        self.management_url = Some(url.into());
213        self
214    }
215
216    pub fn with_auth(mut self, auth: Auth) -> Self {
217        self.auth = auth;
218        self
219    }
220
221    pub fn build(self) -> Result<EdcConnectorClient, BuilderError> {
222        let url = self
223            .management_url
224            .ok_or_else(|| BuilderError::missing_property("management_url"))?;
225        let client = Client::new();
226        Ok(EdcConnectorClient::new(client, url, self.auth))
227    }
228}
229
230impl Default for EdcClientConnectorBuilder {
231    fn default() -> Self {
232        Self {
233            management_url: Default::default(),
234            auth: Auth::NoAuth,
235        }
236    }
237}
238
239trait BuilderExt {
240    fn authenticated(self, auth: &Auth) -> Self;
241}
242
243impl BuilderExt for RequestBuilder {
244    fn authenticated(self, auth: &Auth) -> Self {
245        match auth {
246            Auth::NoAuth => self,
247            Auth::ApiToken(token) => self.header("X-Api-Key", token),
248        }
249    }
250}