mithril_client/certificate_client/
fetch.rs1use anyhow::{anyhow, Context};
2use async_trait::async_trait;
3use slog::{crit, Logger};
4use std::sync::Arc;
5
6use mithril_common::certificate_chain::{CertificateRetriever, CertificateRetrieverError};
7use mithril_common::entities::Certificate;
8use mithril_common::messages::CertificateMessage;
9
10use crate::aggregator_client::{AggregatorClient, AggregatorClientError, AggregatorRequest};
11use crate::certificate_client::CertificateClient;
12use crate::{MithrilCertificate, MithrilCertificateListItem, MithrilResult};
13
14#[inline]
15pub(super) async fn list(
16 client: &CertificateClient,
17) -> MithrilResult<Vec<MithrilCertificateListItem>> {
18 let response = client
19 .aggregator_client
20 .get_content(AggregatorRequest::ListCertificates)
21 .await
22 .with_context(|| "CertificateClient can not get the certificate list")?;
23 let items = serde_json::from_str::<Vec<MithrilCertificateListItem>>(&response)
24 .with_context(|| "CertificateClient can not deserialize certificate list")?;
25
26 Ok(items)
27}
28
29#[inline]
30pub(super) async fn get(
31 client: &CertificateClient,
32 certificate_hash: &str,
33) -> MithrilResult<Option<MithrilCertificate>> {
34 client.retriever.get(certificate_hash).await
35}
36
37pub(super) struct InternalCertificateRetriever {
41 aggregator_client: Arc<dyn AggregatorClient>,
42 logger: Logger,
43}
44
45impl InternalCertificateRetriever {
46 pub(super) fn new(
47 aggregator_client: Arc<dyn AggregatorClient>,
48 logger: Logger,
49 ) -> InternalCertificateRetriever {
50 InternalCertificateRetriever {
51 aggregator_client,
52 logger,
53 }
54 }
55
56 pub(super) async fn get(
57 &self,
58 certificate_hash: &str,
59 ) -> MithrilResult<Option<MithrilCertificate>> {
60 let response = self
61 .aggregator_client
62 .get_content(AggregatorRequest::GetCertificate {
63 hash: certificate_hash.to_string(),
64 })
65 .await;
66
67 match response {
68 Err(AggregatorClientError::RemoteServerLogical(_)) => Ok(None),
69 Err(e) => Err(e.into()),
70 Ok(response) => {
71 let message =
72 serde_json::from_str::<CertificateMessage>(&response).inspect_err(|e| {
73 crit!(
74 self.logger, "Could not create certificate from API message";
75 "error" => e.to_string(),
76 "raw_message" => response
77 );
78 })?;
79
80 Ok(Some(message))
81 }
82 }
83 }
84}
85
86#[cfg_attr(target_family = "wasm", async_trait(?Send))]
87#[cfg_attr(not(target_family = "wasm"), async_trait)]
88impl CertificateRetriever for InternalCertificateRetriever {
89 async fn get_certificate_details(
90 &self,
91 certificate_hash: &str,
92 ) -> Result<Certificate, CertificateRetrieverError> {
93 self.get(certificate_hash)
94 .await
95 .map_err(CertificateRetrieverError)?
96 .map(|message| message.try_into())
97 .transpose()
98 .map_err(CertificateRetrieverError)?
99 .ok_or(CertificateRetrieverError(anyhow!(format!(
100 "Certificate does not exist: '{}'",
101 certificate_hash
102 ))))
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use mithril_common::test_utils::fake_data;
109
110 use crate::certificate_client::tests_utils::CertificateClientTestBuilder;
111
112 use super::*;
113
114 #[tokio::test]
115 async fn get_certificate_list() {
116 let expected = vec![
117 MithrilCertificateListItem {
118 hash: "cert-hash-123".to_string(),
119 ..MithrilCertificateListItem::dummy()
120 },
121 MithrilCertificateListItem {
122 hash: "cert-hash-456".to_string(),
123 ..MithrilCertificateListItem::dummy()
124 },
125 ];
126 let message = expected.clone();
127 let certificate_client = CertificateClientTestBuilder::default()
128 .config_aggregator_client_mock(|mock| {
129 mock.expect_get_content()
130 .return_once(move |_| Ok(serde_json::to_string(&message).unwrap()));
131 })
132 .build();
133 let items = certificate_client.list().await.unwrap();
134
135 assert_eq!(expected, items);
136 }
137
138 #[tokio::test]
139 async fn get_certificate_empty_list() {
140 let certificate_client = CertificateClientTestBuilder::default()
141 .config_aggregator_client_mock(|mock| {
142 mock.expect_get_content().return_once(move |_| {
143 Ok(serde_json::to_string::<Vec<MithrilCertificateListItem>>(&vec![]).unwrap())
144 });
145 })
146 .build();
147 let items = certificate_client.list().await.unwrap();
148
149 assert!(items.is_empty());
150 }
151
152 #[tokio::test]
153 async fn test_show_ok_some() {
154 let certificate_hash = "cert-hash-123".to_string();
155 let certificate = fake_data::certificate(certificate_hash.clone());
156 let expected_certificate = certificate.clone();
157
158 let certificate_client = CertificateClientTestBuilder::default()
159 .config_aggregator_client_mock(|mock| {
160 mock.expect_get_content()
161 .return_once(move |_| {
162 let message: CertificateMessage = certificate.try_into().unwrap();
163 Ok(serde_json::to_string(&message).unwrap())
164 })
165 .times(1);
166 })
167 .build();
168
169 let cert = certificate_client
170 .get("cert-hash-123")
171 .await
172 .unwrap()
173 .expect("The certificate should be found")
174 .try_into()
175 .unwrap();
176
177 assert_eq!(expected_certificate, cert);
178 }
179
180 #[tokio::test]
181 async fn test_show_ok_none() {
182 let certificate_client = CertificateClientTestBuilder::default()
183 .config_aggregator_client_mock(|mock| {
184 mock.expect_get_content()
185 .return_once(move |_| {
186 Err(AggregatorClientError::RemoteServerLogical(anyhow!(
187 "an error"
188 )))
189 })
190 .times(1);
191 })
192 .build();
193
194 assert!(certificate_client
195 .get("cert-hash-123")
196 .await
197 .unwrap()
198 .is_none());
199 }
200
201 #[tokio::test]
202 async fn test_show_ko() {
203 let certificate_client = CertificateClientTestBuilder::default()
204 .config_aggregator_client_mock(|mock| {
205 mock.expect_get_content()
206 .return_once(move |_| {
207 Err(AggregatorClientError::RemoteServerTechnical(anyhow!(
208 "an error"
209 )))
210 })
211 .times(1);
212 })
213 .build();
214
215 certificate_client
216 .get("cert-hash-123")
217 .await
218 .expect_err("The certificate client should fail here.");
219 }
220}