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}