-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathapi.rs
More file actions
123 lines (106 loc) · 3.96 KB
/
api.rs
File metadata and controls
123 lines (106 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use super::{
ChallengeResponseData, GetTaskRequest, GetTaskResponseData, LoginRequest, LoginResponseData,
Response, SubmitProofRequest, SubmitProofResponseData,
};
use crate::config::CoordinatorConfig;
use core::time::Duration;
use reqwest::{header::CONTENT_TYPE, Url};
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
use serde::Serialize;
pub struct Api {
pub base_url: Url,
send_timeout: Duration,
pub client: ClientWithMiddleware,
}
impl Api {
pub fn new(cfg: CoordinatorConfig) -> anyhow::Result<Self> {
let retry_wait_duration = Duration::from_secs(cfg.retry_wait_time_sec);
let retry_policy = ExponentialBackoff::builder()
.retry_bounds(retry_wait_duration / 2, retry_wait_duration)
.build_with_max_retries(cfg.retry_count);
let client = ClientBuilder::new(reqwest::Client::new())
.with(RetryTransientMiddleware::new_with_policy(retry_policy))
.build();
Ok(Self {
base_url: Url::parse(&cfg.base_url)?,
send_timeout: core::time::Duration::from_secs(cfg.connection_timeout_sec),
client,
})
}
fn build_url(&self, method: &str) -> anyhow::Result<Url> {
self.base_url.join(method).map_err(|e| anyhow::anyhow!(e))
}
async fn post_with_token<Req, Resp>(
&self,
method: &str,
req: &Req,
token: &String,
) -> anyhow::Result<Resp>
where
Req: ?Sized + Serialize,
Resp: serde::de::DeserializeOwned,
{
let url = self.build_url(method)?;
let request_body = serde_json::to_string(req)?;
let size = request_body.len();
log::info!("[coordinator client], {method}, sent request");
log::debug!("[coordinator client], {method}, request: {request_body}, token: {token}, request size: {size}");
let response = self
.client
.post(url)
.header(CONTENT_TYPE, "application/json")
.bearer_auth(token)
.body(request_body)
.timeout(self.send_timeout)
.send()
.await?;
if response.status() != http::status::StatusCode::OK {
anyhow::bail!(
"[coordinator client], {method}, status not ok: {}",
response.status()
)
}
let response_body = response.text().await?;
log::info!("[coordinator client], {method}, received response");
log::debug!("[coordinator client], {method}, response: {response_body}");
serde_json::from_str(&response_body).map_err(|e| anyhow::anyhow!(e))
}
pub async fn challenge(&self) -> anyhow::Result<Response<ChallengeResponseData>> {
let method = "/coordinator/v1/challenge";
let url = self.build_url(method)?;
let response = self
.client
.get(url)
.header(CONTENT_TYPE, "application/json")
.timeout(self.send_timeout)
.send()
.await?;
let response_body = response.text().await?;
serde_json::from_str(&response_body).map_err(|e| anyhow::anyhow!(e))
}
pub async fn login(
&self,
req: &LoginRequest,
token: &String,
) -> anyhow::Result<Response<LoginResponseData>> {
let method = "/coordinator/v1/login";
self.post_with_token(method, req, token).await
}
pub async fn get_task(
&self,
req: &GetTaskRequest,
token: &String,
) -> anyhow::Result<Response<GetTaskResponseData>> {
let method = "/coordinator/v1/get_task";
self.post_with_token(method, req, token).await
}
pub async fn submit_proof(
&self,
req: &SubmitProofRequest,
token: &String,
) -> anyhow::Result<Response<SubmitProofResponseData>> {
let method = "/coordinator/v1/submit_proof";
self.post_with_token(method, req, token).await
}
}