Skip to content

Commit 392dd9a

Browse files
committed
feat: introduce network-group API
1 parent c7d7cb8 commit 392dd9a

File tree

12 files changed

+864
-46
lines changed

12 files changed

+864
-46
lines changed

Cargo.toml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ keywords = ["clevercloud", "sdk", "logging", "metrics", "jsonschemas"]
1313
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1414

1515
[dependencies]
16+
base64 = { version = "^0.22.1", optional = true }
1617
chrono = { version = "^0.4.40", features = ["serde"] }
17-
oauth10a = { version = "^2.1.1", default-features = false, features = [
18+
oauth10a = { path = "../oauth10a-rust", default-features = false, features = [
1819
"client",
20+
"sse",
1921
] }
20-
log = { version = "^0.4.26", optional = true }
22+
log = { version = "^0.4.27", optional = true }
23+
rand_core = { version = "0.9.3", features = ["os_rng"], optional = true }
2124
schemars = { version = "^0.8.22", features = [
2225
"chrono",
2326
"indexmap1",
@@ -31,10 +34,16 @@ serde_json = "^1.0.140"
3134
thiserror = "^2.0.12"
3235
tracing = { version = "^0.1.41", optional = true }
3336
uuid = { version = "^1.16.0", features = ["serde", "v4"] }
37+
x25519-dalek = { version = "2.0.1", features = [
38+
"zeroize",
39+
"static_secrets",
40+
], optional = true }
41+
zeroize = { version = "1.8.1", optional = true }
3442

3543
[features]
36-
default = ["logging"]
44+
default = ["logging", "network-group"]
3745
jsonschemas = ["schemars"]
3846
logging = ["oauth10a/logging", "tracing/log-always", "log"]
3947
metrics = ["oauth10a/metrics"]
4048
tracing = ["oauth10a/tracing", "dep:tracing"]
49+
network-group = ["dep:base64", "x25519-dalek", "zeroize", "rand_core"]

src/lib.rs

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
//! # Clever-Cloud Sdk
22
//!
3-
//! This module provides a client and structures to interact with clever-cloud
4-
//! api.
3+
//! This module provides a client and structures to interact with Clever Cloud API.
54
6-
use std::fmt::Debug;
5+
use core::fmt;
76

7+
use ::oauth10a::client::{Execute, reqwest::IntoUrl};
88
use serde::{Deserialize, Serialize, de::DeserializeOwned};
99

1010
use crate::oauth10a::{
11-
Client as OAuthClient, ClientError, Request, RestClient,
11+
Client as OAuthClient, ClientError, RestClient,
1212
reqwest::{self, Method},
1313
};
1414

@@ -46,7 +46,7 @@ pub fn default_consumer_secret() -> String {
4646
// -----------------------------------------------------------------------------
4747
// Credentials structure
4848

49-
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
49+
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
5050
#[serde(untagged)]
5151
pub enum Credentials {
5252
OAuth1 {
@@ -71,6 +71,16 @@ pub enum Credentials {
7171
},
7272
}
7373

74+
impl fmt::Debug for Credentials {
75+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76+
match self {
77+
Self::OAuth1 { .. } => f.write_str("OAuth1"),
78+
Self::Basic { .. } => f.write_str("Basic"),
79+
Self::Bearer { .. } => f.write_str("Bearer"),
80+
}
81+
}
82+
}
83+
7484
impl Default for Credentials {
7585
#[tracing::instrument(skip_all)]
7686
fn default() -> Self {
@@ -208,80 +218,69 @@ pub struct Client {
208218
endpoint: String,
209219
}
210220

211-
impl Request for Client {
221+
impl Execute for Client {
212222
type Error = ClientError;
213223

214224
#[cfg_attr(feature = "tracing", tracing::instrument)]
225+
fn execute(
226+
&self,
227+
request: reqwest::Request,
228+
) -> impl Future<Output = Result<reqwest::Response, Self::Error>> + Send + 'static {
229+
self.inner.execute(request)
230+
}
231+
}
232+
233+
impl<X: IntoUrl + Send + fmt::Debug> RestClient<X> for Client {
215234
fn request<T, U>(
216235
&self,
217236
method: &Method,
218-
endpoint: &str,
237+
endpoint: X,
219238
payload: &T,
220-
) -> impl Future<Output = Result<U, Self::Error>>
239+
) -> impl Future<Output = Result<U, Self::Error>> + Send
221240
where
222-
T: Serialize + Debug + Send + Sync,
223-
U: DeserializeOwned + Debug + Send + Sync,
241+
T: ?Sized + Serialize + fmt::Debug + Send + Sync,
242+
U: DeserializeOwned + fmt::Debug + Send + Sync,
224243
{
225244
self.inner.request(method, endpoint, payload)
226245
}
227246

228247
#[cfg_attr(feature = "tracing", tracing::instrument)]
229-
fn execute(
230-
&self,
231-
request: reqwest::Request,
232-
) -> impl Future<Output = Result<reqwest::Response, Self::Error>> {
233-
self.inner.execute(request)
234-
}
235-
}
236-
237-
impl RestClient for Client {
238-
type Error = ClientError;
239-
240-
#[cfg_attr(feature = "tracing", tracing::instrument)]
241-
fn get<T>(&self, endpoint: &str) -> impl Future<Output = Result<T, Self::Error>>
248+
fn get<T>(&self, endpoint: X) -> impl Future<Output = Result<T, Self::Error>>
242249
where
243-
T: DeserializeOwned + Debug + Send + Sync,
250+
T: DeserializeOwned + fmt::Debug + Send + Sync,
244251
{
245252
self.inner.get(endpoint)
246253
}
247254

248255
#[cfg_attr(feature = "tracing", tracing::instrument)]
249-
fn post<T, U>(
250-
&self,
251-
endpoint: &str,
252-
payload: &T,
253-
) -> impl Future<Output = Result<U, Self::Error>>
256+
fn post<T, U>(&self, endpoint: X, payload: &T) -> impl Future<Output = Result<U, Self::Error>>
254257
where
255-
T: Serialize + Debug + Send + Sync,
256-
U: DeserializeOwned + Debug + Send + Sync,
258+
T: Serialize + fmt::Debug + Send + Sync + ?Sized,
259+
U: DeserializeOwned + fmt::Debug + Send + Sync,
257260
{
258261
self.inner.post(endpoint, payload)
259262
}
260263

261264
#[cfg_attr(feature = "tracing", tracing::instrument)]
262-
fn put<T, U>(&self, endpoint: &str, payload: &T) -> impl Future<Output = Result<U, Self::Error>>
265+
fn put<T, U>(&self, endpoint: X, payload: &T) -> impl Future<Output = Result<U, Self::Error>>
263266
where
264-
T: Serialize + Debug + Send + Sync,
265-
U: DeserializeOwned + Debug + Send + Sync,
267+
T: Serialize + fmt::Debug + Send + Sync + ?Sized,
268+
U: DeserializeOwned + fmt::Debug + Send + Sync,
266269
{
267270
self.inner.put(endpoint, payload)
268271
}
269272

270273
#[cfg_attr(feature = "tracing", tracing::instrument)]
271-
fn patch<T, U>(
272-
&self,
273-
endpoint: &str,
274-
payload: &T,
275-
) -> impl Future<Output = Result<U, Self::Error>>
274+
fn patch<T, U>(&self, endpoint: X, payload: &T) -> impl Future<Output = Result<U, Self::Error>>
276275
where
277-
T: Serialize + Debug + Send + Sync,
278-
U: DeserializeOwned + Debug + Send + Sync,
276+
T: Serialize + fmt::Debug + Send + Sync + ?Sized,
277+
U: DeserializeOwned + fmt::Debug + Send + Sync,
279278
{
280279
self.inner.patch(endpoint, payload)
281280
}
282281

283282
#[cfg_attr(feature = "tracing", tracing::instrument)]
284-
fn delete(&self, endpoint: &str) -> impl Future<Output = Result<(), Self::Error>> {
283+
fn delete(&self, endpoint: X) -> impl Future<Output = Result<(), Self::Error>> {
285284
self.inner.delete(endpoint)
286285
}
287286
}

src/v4/functions/deployments.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::{
1010
use chrono::{DateTime, Utc};
1111
use log::{Level, debug, log_enabled};
1212
use oauth10a::client::{
13-
ClientError, Request, RestClient,
13+
ClientError, Execute, RestClient,
1414
reqwest::{
1515
self, Body, Method,
1616
header::{CONTENT_LENGTH, CONTENT_TYPE, HeaderValue},

src/v4/http_error.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use oauth10a::client::reqwest::StatusCode;
2+
use serde::{Deserialize, Serialize};
3+
4+
pub type HttpOutput = (StatusCode, HttpError);
5+
6+
mod http_error_context {
7+
use std::collections::HashMap;
8+
9+
use serde::{Deserialize, Serialize};
10+
11+
#[derive(Debug, Deserialize, Serialize)]
12+
pub struct Empty;
13+
14+
#[derive(Debug, Deserialize, Serialize)]
15+
pub struct FieldError {
16+
#[serde(rename = "value")]
17+
value: String,
18+
#[serde(rename = "reason")]
19+
reason: Option<String>,
20+
}
21+
22+
#[derive(Debug, Deserialize, Serialize)]
23+
pub struct Field {
24+
#[serde(rename = "name")]
25+
name: String,
26+
#[serde(rename = "error")]
27+
error: FieldError,
28+
}
29+
30+
#[derive(Debug, Deserialize, Serialize)]
31+
pub struct GatewayError {
32+
#[serde(rename = "originalStatus")]
33+
original_status: i32,
34+
#[serde(rename = "originalBody")]
35+
original_body: String,
36+
}
37+
38+
#[derive(Debug, Deserialize, Serialize)]
39+
pub struct Input {
40+
#[serde(rename = "names")]
41+
names: Vec<String>,
42+
}
43+
44+
type MapFieldError = HashMap<String, FieldError>;
45+
46+
#[derive(Debug, Deserialize, Serialize)]
47+
pub struct MultipleFields {
48+
#[serde(rename = "fields")]
49+
fields: MapFieldError,
50+
}
51+
52+
#[derive(Debug, Deserialize, Serialize)]
53+
pub struct Resource {
54+
#[serde(rename = "kind")]
55+
kind: String,
56+
#[serde(rename = "name")]
57+
name: String,
58+
}
59+
60+
#[derive(Debug, Deserialize, Serialize)]
61+
pub struct Operation {
62+
#[serde(rename = "operation")]
63+
operation: String,
64+
#[serde(rename = "kind")]
65+
kind: String,
66+
#[serde(rename = "name")]
67+
name: Option<String>,
68+
}
69+
70+
#[derive(Debug, Deserialize, Serialize)]
71+
pub struct Selector {
72+
#[serde(rename = "path")]
73+
path: Vec<String>,
74+
}
75+
}
76+
77+
#[derive(Debug, Deserialize, Serialize)]
78+
#[serde(untagged)]
79+
pub enum HttpErrorContext {
80+
Empty(http_error_context::Empty),
81+
Field(http_error_context::Field),
82+
Gateway(http_error_context::GatewayError),
83+
Input(http_error_context::Input),
84+
MultipleFields(http_error_context::MultipleFields),
85+
Operation(http_error_context::Operation),
86+
Resource(http_error_context::Resource),
87+
Selector(http_error_context::Selector),
88+
}
89+
90+
#[derive(Debug, Deserialize, Serialize)]
91+
pub struct HttpError {
92+
#[serde(rename = "apiRequestId")]
93+
pub api_request_id: String,
94+
#[serde(rename = "code")]
95+
pub code: String,
96+
#[serde(rename = "context")]
97+
pub context: HttpErrorContext,
98+
#[serde(rename = "error")]
99+
pub error: String,
100+
}
101+
102+
// TODO: unit tests
103+
// maybe use https://github.com/Orange-OpenSource/hurl or something

src/v4/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@
44
55
pub mod addon_provider;
66
pub mod functions;
7+
pub mod http_error;
8+
#[cfg(feature = "network-group")]
9+
pub mod network_group;
710
pub mod products;

src/v4/network_group/delete.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use oauth10a::client::{ClientError, RestClient};
2+
use tracing::debug;
3+
4+
use crate::Client;
5+
6+
use super::{OwnerId, network_group_id::NetworkGroupId, peer::PeerId};
7+
8+
#[derive(thiserror::Error, Debug)]
9+
pub enum Error {
10+
#[error("failed to delete for owner '{0}', network group '{1}', peer id '{2}', {0}")]
11+
Delete(String, NetworkGroupId, PeerId, ClientError),
12+
}
13+
14+
pub async fn delete(
15+
client: &Client,
16+
owner_id: &OwnerId,
17+
ng_id: &NetworkGroupId,
18+
peer_id: PeerId,
19+
) -> Result<(), Error> {
20+
let endpoint = format!(
21+
"{}/v4/networkgroups/organisations/{owner_id}/networkgroups/{ng_id}/external-peers/{peer_id}",
22+
client.endpoint,
23+
);
24+
25+
#[cfg(feature = "tracing")]
26+
debug!("execute a request to delete peer from Network Group, endpoint: '{endpoint}'");
27+
28+
client
29+
.delete(&endpoint)
30+
.await
31+
.map_err(|e| Error::Delete(owner_id.to_owned(), *ng_id, peer_id, e))
32+
}

src/v4/network_group/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//! # Network Group module
2+
//!
3+
//! This module provide structures and helpers to interact with Clever Cloud's
4+
//! Network Group API.
5+
6+
pub type MemberId = String;
7+
8+
pub type OwnerId = String;
9+
10+
pub mod delete;
11+
pub mod network_group_id;
12+
pub mod peer;
13+
pub mod wannabe_external_peer;
14+
pub mod wireguard;
15+
pub mod wireguard_configuration;

0 commit comments

Comments
 (0)