mongodb_atlas_admin/
endpoint.rs1use async_trait::async_trait;
2use digest_auth::AuthContext;
3use http::{
4 header::{AUTHORIZATION, CONTENT_TYPE, WWW_AUTHENTICATE},
5 HeaderValue, Method,
6};
7use reqwest::{RequestBuilder, Response};
8use serde::{Deserialize, Serialize};
9use std::borrow::Cow;
10
11#[async_trait]
12pub trait Endpoint {
13 type Data;
14 type Error: std::error::Error;
15
16 fn method() -> Method;
17
18 fn path(&self) -> Cow<'static, str>;
19
20 fn body(&self) -> Result<Option<Vec<u8>>, Self::Error> {
21 Ok(None)
22 }
23
24 async fn get_data(res: Response) -> Result<Self::Data, Self::Error>;
25}
26
27pub trait EndpointError {}
28
29fn apply_data(request: RequestBuilder, data: Option<Vec<u8>>) -> RequestBuilder {
30 if let Some(vec) = data {
31 request
32 .header(CONTENT_TYPE, HeaderValue::from_static("application/json"))
33 .body(vec)
34 } else {
35 request
36 }
37}
38
39pub struct Client {
40 username: String,
41 password: String,
42}
43
44impl Client {
45 pub fn new<U: Into<String>, P: Into<String>>(username: U, password: P) -> Self {
46 Self {
47 username: username.into(),
48 password: password.into(),
49 }
50 }
51
52 pub async fn execute_endpoint<T: Endpoint + 'static>(
53 &self,
54 a: T,
55 ) -> Result<T::Data, Box<dyn std::error::Error>> {
56 let path = T::path(&a);
57
58 let uri = format!("/api/atlas/v1.0{}", &path);
59 let url = format!("https://cloud.mongodb.com{}", &uri);
60
61 let resp = reqwest::get(&url).await?;
62
63 let www_authenticate = resp.headers().get(WWW_AUTHENTICATE).unwrap().to_str()?;
64 let body = T::body(&a)?;
65 let context = AuthContext::new_with_method(
66 &self.username,
67 &self.password,
68 uri,
69 body.as_ref(),
70 T::method().into(),
71 );
72
73 let mut prompt = digest_auth::parse(www_authenticate).unwrap();
74 let answer = prompt.respond(&context).unwrap().to_string();
75 let client = reqwest::Client::new();
76 let req = client
77 .request(T::method(), url)
78 .header(AUTHORIZATION, answer);
79
80 let req = apply_data(req, body);
81
82 let resp = req.send().await?;
83
84 let result = T::get_data(resp).await?;
85
86 Ok(result)
87 }
88}
89
90#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
91#[error("Error {error}: {reason}")]
92pub struct RequestError {
93 detail: String,
94 error: i32,
95 #[serde(rename = "errorCode")]
96 error_code: String,
97 parameters: Vec<String>,
98 reason: String,
99}
100
101#[derive(Debug)]
102pub struct Pagination {
103 items_per_page: i32,
104 page_number: i32,
105}
106
107impl Pagination {
108 pub fn new() -> Self {
109 Self {
110 ..Default::default()
111 }
112 }
113
114 pub fn items_per_page(self, items_per_page: i32) -> Self {
115 Self {
116 items_per_page,
117 ..self
118 }
119 }
120
121 pub fn page_number(self, page_number: i32) -> Self {
122 Self {
123 page_number,
124 ..self
125 }
126 }
127
128 pub fn to_query_params(&self) -> String {
129 format!(
130 "includeCount=true&itemsPerPage={}&pageNum={}",
131 self.items_per_page, self.page_number
132 )
133 }
134}
135
136impl Default for Pagination {
137 fn default() -> Self {
138 Self {
139 items_per_page: 100,
140 page_number: 1,
141 }
142 }
143}