Skip to content

Commit a646200

Browse files
committed
feat(aggregator-client): add missing query for use in mithril-signer
add `GetAggregatorFeatures`, `PostRegisterSignatureQuery`, and `PostRegisterSignerQuery` queries.
1 parent b6483bd commit a646200

File tree

7 files changed

+417
-1
lines changed

7 files changed

+417
-1
lines changed

internal/mithril-aggregator-client/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl AggregatorHttpClientError {
6363
}
6464
}
6565

66-
async fn get_root_cause(response: Response) -> String {
66+
pub(crate) async fn get_root_cause(response: Response) -> String {
6767
let error_code = response.status();
6868
let canonical_reason = error_code.canonical_reason().unwrap_or_default().to_lowercase();
6969
let is_json = response
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use anyhow::anyhow;
2+
use async_trait::async_trait;
3+
use reqwest::StatusCode;
4+
use slog::debug;
5+
6+
use mithril_common::messages::AggregatorFeaturesMessage;
7+
8+
use crate::query::{AggregatorQuery, QueryContext, QueryMethod};
9+
use crate::{AggregatorHttpClientError, AggregatorHttpClientResult};
10+
11+
/// Query to get the features of the aggregator
12+
pub struct GetAggregatorFeaturesQuery {}
13+
14+
impl GetAggregatorFeaturesQuery {
15+
/// Instantiate a query to get the features of the aggregator
16+
pub fn current() -> Self {
17+
Self {}
18+
}
19+
}
20+
21+
#[cfg_attr(target_family = "wasm", async_trait(?Send))]
22+
#[cfg_attr(not(target_family = "wasm"), async_trait)]
23+
impl AggregatorQuery for GetAggregatorFeaturesQuery {
24+
type Response = AggregatorFeaturesMessage;
25+
type Body = ();
26+
27+
fn method() -> QueryMethod {
28+
QueryMethod::Get
29+
}
30+
31+
fn route(&self) -> String {
32+
"/".to_string()
33+
}
34+
35+
async fn handle_response(
36+
&self,
37+
context: QueryContext,
38+
) -> AggregatorHttpClientResult<Self::Response> {
39+
debug!(context.logger, "Retrieve aggregator features message");
40+
41+
match context.response.status() {
42+
StatusCode::OK => Ok(context
43+
.response
44+
.json::<AggregatorFeaturesMessage>()
45+
.await
46+
.map_err(|e| AggregatorHttpClientError::JsonParseFailed(anyhow!(e)))?),
47+
_ => Err(context.unhandled_status_code().await),
48+
}
49+
}
50+
}
51+
52+
#[cfg(test)]
53+
mod tests {
54+
use serde_json::json;
55+
56+
use mithril_common::test::double::Dummy;
57+
58+
use crate::test::{assert_error_matches, setup_server_and_client};
59+
60+
use super::*;
61+
62+
#[tokio::test]
63+
async fn test_aggregator_features_ok_200() {
64+
let (server, client) = setup_server_and_client();
65+
let message_expected = AggregatorFeaturesMessage::dummy();
66+
let _server_mock = server.mock(|when, then| {
67+
when.path("/");
68+
then.status(200).body(json!(message_expected).to_string());
69+
});
70+
71+
let message = client.send(GetAggregatorFeaturesQuery::current()).await.unwrap();
72+
73+
assert_eq!(message_expected, message);
74+
}
75+
76+
#[tokio::test]
77+
async fn test_aggregator_features_ko_500() {
78+
let (server, client) = setup_server_and_client();
79+
server.mock(|when, then| {
80+
when.any_request();
81+
then.status(500).body("an error occurred");
82+
});
83+
84+
let error = client.send(GetAggregatorFeaturesQuery::current()).await.unwrap_err();
85+
86+
assert_error_matches!(error, AggregatorHttpClientError::RemoteServerTechnical(_));
87+
}
88+
89+
#[tokio::test]
90+
async fn test_aggregator_features_ko_json_serialization() {
91+
let (server, client) = setup_server_and_client();
92+
server.mock(|when, then| {
93+
when.any_request();
94+
then.status(200).body("this is not a json");
95+
});
96+
97+
let error = client.send(GetAggregatorFeaturesQuery::current()).await.unwrap_err();
98+
99+
assert_error_matches!(error, AggregatorHttpClientError::JsonParseFailed(_));
100+
}
101+
}

internal/mithril-aggregator-client/src/query/get/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
mod get_aggregator_features;
12
mod get_certificate;
23
mod get_certificates_list;
34
mod get_epoch_settings;
45
mod get_protocol_configuration;
56

7+
pub use get_aggregator_features::*;
68
pub use get_certificate::*;
79
pub use get_certificates_list::*;
810
pub use get_epoch_settings::*;

internal/mithril-aggregator-client/src/query/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
//!
66
mod api;
77
mod get;
8+
mod post;
89

910
pub(crate) use api::*;
1011
pub use get::*;
12+
pub use post::*;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod post_register_signature;
2+
mod post_register_signer;
3+
4+
pub use post_register_signature::*;
5+
pub use post_register_signer::*;
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
use async_trait::async_trait;
2+
use reqwest::StatusCode;
3+
use slog::debug;
4+
5+
use mithril_common::messages::RegisterSignatureMessageHttp;
6+
7+
use crate::query::{AggregatorQuery, QueryContext, QueryMethod};
8+
use crate::{AggregatorHttpClientError, AggregatorHttpClientResult};
9+
10+
/// Query to register a signature to a Mithril Aggregator.
11+
pub struct PostRegisterSignatureQuery {
12+
message: RegisterSignatureMessageHttp,
13+
}
14+
15+
impl PostRegisterSignatureQuery {
16+
/// Instantiate a new query to register a signature
17+
pub fn new(message: RegisterSignatureMessageHttp) -> Self {
18+
Self { message }
19+
}
20+
}
21+
22+
#[cfg_attr(target_family = "wasm", async_trait(?Send))]
23+
#[cfg_attr(not(target_family = "wasm"), async_trait)]
24+
impl AggregatorQuery for PostRegisterSignatureQuery {
25+
type Response = ();
26+
type Body = RegisterSignatureMessageHttp;
27+
28+
fn method() -> QueryMethod {
29+
QueryMethod::Post
30+
}
31+
32+
fn route(&self) -> String {
33+
"register-signatures".to_string()
34+
}
35+
36+
fn body(&self) -> Option<Self::Body> {
37+
Some(self.message.clone())
38+
}
39+
40+
async fn handle_response(
41+
&self,
42+
context: QueryContext,
43+
) -> AggregatorHttpClientResult<Self::Response> {
44+
debug!(context.logger, "Register signature"; "signed_entity" => ?self.message.signed_entity_type);
45+
46+
match context.response.status() {
47+
StatusCode::CREATED | StatusCode::ACCEPTED => Ok(()),
48+
StatusCode::GONE => {
49+
let root_cause = AggregatorHttpClientError::get_root_cause(context.response).await;
50+
debug!(context.logger, "Message already certified or expired"; "details" => &root_cause);
51+
52+
Ok(())
53+
}
54+
_ => Err(context.unhandled_status_code().await),
55+
}
56+
}
57+
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
use httpmock::Method::POST;
62+
63+
use mithril_common::entities::ClientError;
64+
use mithril_common::test::double::Dummy;
65+
66+
use crate::test::{TestLogger, assert_error_matches, setup_server_and_client};
67+
68+
use super::*;
69+
70+
#[tokio::test]
71+
async fn test_register_signature_ok_201() {
72+
let expected_message = RegisterSignatureMessageHttp::dummy();
73+
let (server, client) = setup_server_and_client();
74+
let _server_mock = server.mock(|when, then| {
75+
when.method(POST)
76+
.path("/register-signatures")
77+
.body(serde_json::to_string(&expected_message).unwrap());
78+
then.status(201);
79+
});
80+
81+
let register_signature =
82+
client.send(PostRegisterSignatureQuery::new(expected_message)).await;
83+
register_signature.expect("unexpected error");
84+
}
85+
86+
#[tokio::test]
87+
async fn test_register_signature_ok_202() {
88+
let (server, client) = setup_server_and_client();
89+
let _server_mock = server.mock(|when, then| {
90+
when.method(POST).path("/register-signatures");
91+
then.status(202);
92+
});
93+
94+
let register_signature = client
95+
.send(PostRegisterSignatureQuery::new(
96+
RegisterSignatureMessageHttp::dummy(),
97+
))
98+
.await;
99+
register_signature.expect("unexpected error");
100+
}
101+
102+
#[tokio::test]
103+
async fn test_register_signature_ko_400() {
104+
let (server, client) = setup_server_and_client();
105+
let _server_mock = server.mock(|when, then| {
106+
when.method(POST).path("/register-signatures");
107+
then.status(400).body(
108+
serde_json::to_vec(&ClientError::new(
109+
"error".to_string(),
110+
"an error".to_string(),
111+
))
112+
.unwrap(),
113+
);
114+
});
115+
116+
let error = client
117+
.send(PostRegisterSignatureQuery::new(
118+
RegisterSignatureMessageHttp::dummy(),
119+
))
120+
.await
121+
.unwrap_err();
122+
123+
assert_error_matches!(error, AggregatorHttpClientError::RemoteServerLogical(_));
124+
}
125+
126+
#[tokio::test]
127+
async fn test_register_signature_ok_410_log_response_body() {
128+
let (logger, log_inspector) = TestLogger::memory();
129+
130+
let (server, mut client) = setup_server_and_client();
131+
client.logger = logger;
132+
let _server_mock = server.mock(|when, then| {
133+
when.method(POST).path("/register-signatures");
134+
then.status(410).body(
135+
serde_json::to_vec(&ClientError::new(
136+
"already_aggregated".to_string(),
137+
"too late".to_string(),
138+
))
139+
.unwrap(),
140+
);
141+
});
142+
143+
client
144+
.send(PostRegisterSignatureQuery::new(
145+
RegisterSignatureMessageHttp::dummy(),
146+
))
147+
.await
148+
.expect("Should not fail when status is 410 (GONE)");
149+
150+
assert!(log_inspector.contains_log("already_aggregated"));
151+
assert!(log_inspector.contains_log("too late"));
152+
}
153+
154+
#[tokio::test]
155+
async fn test_register_signature_ko_409() {
156+
let (server, client) = setup_server_and_client();
157+
let _server_mock = server.mock(|when, then| {
158+
when.method(POST).path("/register-signatures");
159+
then.status(409);
160+
});
161+
162+
let error = client
163+
.send(PostRegisterSignatureQuery::new(
164+
RegisterSignatureMessageHttp::dummy(),
165+
))
166+
.await
167+
.unwrap_err();
168+
169+
assert_error_matches!(error, AggregatorHttpClientError::RemoteServerLogical(_));
170+
}
171+
172+
#[tokio::test]
173+
async fn test_register_signature_ko_500() {
174+
let (server, client) = setup_server_and_client();
175+
let _server_mock = server.mock(|when, then| {
176+
when.method(POST).path("/register-signatures");
177+
then.status(500).body("an error occurred");
178+
});
179+
180+
let error = client
181+
.send(PostRegisterSignatureQuery::new(
182+
RegisterSignatureMessageHttp::dummy(),
183+
))
184+
.await
185+
.unwrap_err();
186+
187+
assert_error_matches!(error, AggregatorHttpClientError::RemoteServerTechnical(_));
188+
}
189+
}

0 commit comments

Comments
 (0)