1use de::Deserialize;
8use serde::{de, Deserializer};
9use sha2::{Digest, Sha256};
10
11use classy::client::{InvalidUri, Service, Uri};
12
13use crate::policy_context::api::Metadata;
14
15const SERVICE_NAME_SUFFIX: &str = "service";
16
17pub fn deserialize_service<'de, D>(deserializer: D) -> Result<Service, D::Error>
19where
20 D: Deserializer<'de>,
21{
22 use serde::de::Error;
23
24 let string: String = Deserialize::deserialize(deserializer)?;
25
26 let metadata = Metadata::new();
27 let service = new_service(string.as_str(), &metadata)
28 .map_err(|err: InvalidUri| Error::custom(error_message(string.as_str(), err)))?;
29
30 Ok(service)
31}
32
33pub fn deserialize_service_opt<'de, D>(deserializer: D) -> Result<Option<Service>, D::Error>
35where
36 D: Deserializer<'de>,
37{
38 use serde::de::Error;
39
40 let string: Option<String> = Deserialize::deserialize(deserializer)?;
41
42 match string {
43 Some(string) => {
44 let metadata = Metadata::new();
45 let service = new_service(string.as_str(), &metadata)
46 .map_err(|err: InvalidUri| Error::custom(error_message(string.as_str(), err)))?;
47 Ok(Some(service))
48 }
49 None => Ok(None),
50 }
51}
52
53pub fn deserialize_service_vec<'de, D>(deserializer: D) -> Result<Vec<Service>, D::Error>
55where
56 D: Deserializer<'de>,
57{
58 use serde::de::Error;
59
60 let strings: Vec<String> = Deserialize::deserialize(deserializer)?;
61
62 let metadata = Metadata::new();
63
64 let services: Vec<Service> = strings
65 .iter()
66 .map(|string| {
67 let service = new_service(string.as_str(), &metadata)
68 .map_err(|err: InvalidUri| Error::custom(error_message(string.as_str(), err)))?;
69
70 Ok(service)
71 })
72 .collect::<Result<Vec<_>, _>>()?;
73
74 Ok(services)
75}
76
77pub fn deserialize_service_opt_vec<'de, D>(
79 deserializer: D,
80) -> Result<Option<Vec<Service>>, D::Error>
81where
82 D: Deserializer<'de>,
83{
84 use serde::de::Error;
85
86 let strings: Option<Vec<String>> = Deserialize::deserialize(deserializer)?;
87
88 match strings {
89 Some(strings) => {
90 let metadata = Metadata::new();
91
92 let services: Vec<Service> = strings
93 .iter()
94 .map(|string| {
95 let service =
96 new_service(string.as_str(), &metadata).map_err(|err: InvalidUri| {
97 Error::custom(error_message(string.as_str(), err))
98 })?;
99
100 Ok(service)
101 })
102 .collect::<Result<Vec<_>, _>>()?;
103
104 Ok(Some(services))
105 }
106 None => Ok(None),
107 }
108}
109
110pub fn service_name(policy_name: &str, authority: &str) -> String {
112 let full_name = format!("{policy_name}-{authority}");
113
114 let digest = &Sha256::digest(full_name.as_str());
115 let hex = &format!("{digest:x}")[..7];
116
117 let compliant_full_name: String = full_name
118 .chars()
119 .map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })
120 .collect();
121
122 let capped_name = &compliant_full_name[..std::cmp::min(47, compliant_full_name.len())];
123
124 format!("{}-{}-{}", capped_name.to_owned(), hex, SERVICE_NAME_SUFFIX)
125}
126
127fn new_service(string: &str, metadata: &Metadata) -> Result<Service, InvalidUri> {
128 let uri: Uri = string.parse()?;
129 let service_namespace = metadata.policy_metadata.policy_namespace.as_str();
130 let service_name = service_name(
131 metadata.policy_metadata.policy_name.as_str(),
132 uri.authority(),
133 );
134 Ok(Service::from(service_name.as_str(), service_namespace, uri))
135}
136
137fn error_message(string: &str, err: InvalidUri) -> String {
138 format!("Error parsing {string} as Uri: {err}")
139}
140
141#[cfg(test)]
142mod test {
143 use std::vec::IntoIter;
144
145 use serde::de::value::{Error as ValueError, SeqDeserializer, StrDeserializer};
146 use serde::de::{Error, IntoDeserializer};
147 use serde_derive::Deserialize;
148 use serde_json::json;
149
150 use classy::hl::Service;
151
152 use crate::client::{
153 deserialize_service, deserialize_service_opt, deserialize_service_opt_vec,
154 deserialize_service_vec, service_name,
155 };
156
157 #[test]
158 fn service_name_test() {
159 let name = service_name("policy", "authority");
160
161 assert_eq!(name, "policy-authority-15aeecc-service");
162 }
163
164 #[test]
165 fn service_name_is_idempotent() {
166 let name = service_name("policy", "authority");
167 let name2 = service_name("policy", "authority");
168
169 assert_eq!(name, name2);
170 }
171
172 #[test]
173 fn service_name_long_does_not_exceed_63_chars() {
174 let authority = "a-really-long-authority-exceeding-max-large-service-name";
175 let name = service_name("policy", authority);
176
177 assert_eq!(name.len(), 63);
178 assert_eq!(
179 name,
180 "policy-a-really-long-authority-exceeding-max-la-3b21324-service"
181 );
182 }
183
184 #[test]
185 fn service_name_two_long_names_that_end_different_have_different_hashes() {
186 let authority = "a-really-long-authority-exceeding-max-large-service-name";
187 let authority_2 = "a-really-long-authority-exceeding-max-large-service-name-2";
188 let name = service_name("policy", authority);
189 let name2 = service_name("policy", authority_2);
190
191 assert_ne!(name, name2);
192 }
193
194 #[test]
195 fn service_name_two_different_names_that_have_same_sanitized_name_have_different_hashes() {
196 let authority = "authority:port";
197 let authority_2 = "authority@port";
198 let name = service_name("policy", authority);
199 let name2 = service_name("policy", authority_2);
200
201 assert_ne!(name, name2);
202 }
203
204 #[test]
205 fn service_name_invalid_chars_in_authority_are_converted() {
206 let authority = "username:password@[ipv6]:port";
207 let name = service_name("policy", authority);
208
209 assert_eq!(name, "policy-username-password--ipv6--port-c248764-service");
210 }
211
212 #[test]
213 fn deserialize_service_successful_parsing() {
214 let uri_string = "https://localhost:8080/api";
215
216 let result = deserialize_service(deserializer(uri_string));
217
218 assert!(result.is_ok());
219 let service = result.unwrap();
220 assert_eq!(service.uri().to_string(), uri_string.to_string());
221 assert_eq!(
222 service.cluster_name(),
223 "NoPolicyId-localhost-8080-a5ec11e-service.NoPolicyNamespace.svc"
224 );
225 }
226
227 #[test]
228 fn deserialize_service_invalid_uri() {
229 let uri_string = "localhost:8080/api";
230
231 let result = deserialize_service(deserializer(uri_string));
232
233 assert_eq!(
234 result.unwrap_err(),
235 ValueError::custom("Error parsing localhost:8080/api as Uri: invalid format")
236 )
237 }
238
239 #[test]
240 fn deserialize_service_vec_successful_parsing() {
241 let uri_string_1 = "https://localhost:8080/api";
242 let uri_string_2 = "https://another-host:9090/api";
243 let vec = vec![uri_string_1, uri_string_2];
244
245 let result = deserialize_service_vec(vec_deserializer(vec));
246
247 assert!(result.is_ok(), "{:?}", result.err());
248 let services = result.unwrap();
249 assert_eq!(services.len(), 2);
250 let service1 = services.first().unwrap();
251 assert_eq!(service1.uri().to_string(), uri_string_1.to_string());
252 assert_eq!(
253 service1.cluster_name(),
254 "NoPolicyId-localhost-8080-a5ec11e-service.NoPolicyNamespace.svc"
255 );
256 let service2 = services.get(1).unwrap();
257 assert_eq!(service2.uri().to_string(), uri_string_2.to_string());
258 assert_eq!(
259 service2.cluster_name(),
260 "NoPolicyId-another-host-9090-f35336e-service.NoPolicyNamespace.svc"
261 );
262 }
263
264 #[test]
265 fn deserialize_service_vec_invalid_uri_first_element() {
266 let uri_string_1 = "localhost:8080/api";
267 let uri_string_2 = "https://another-host:9090/api";
268 let vec = vec![uri_string_1, uri_string_2];
269
270 let result = deserialize_service_vec(vec_deserializer(vec));
271
272 assert_eq!(
273 result.unwrap_err(),
274 ValueError::custom("Error parsing localhost:8080/api as Uri: invalid format")
275 )
276 }
277
278 #[test]
279 fn deserialize_service_vec_invalid_uri_last_element() {
280 let uri_string_1 = "https://localhost:8080/api";
281 let uri_string_2 = "another-host:9090/api";
282 let vec = vec![uri_string_1, uri_string_2];
283
284 let result = deserialize_service_vec(vec_deserializer(vec));
285
286 assert_eq!(
287 result.unwrap_err(),
288 ValueError::custom("Error parsing another-host:9090/api as Uri: invalid format")
289 )
290 }
291
292 #[test]
293 fn deserialize_opt_service_successful_parsing() {
294 let uri_string = "https://localhost:8080/api";
295
296 let test: TestOpt = serde_json::from_value(json!({ "service": uri_string })).unwrap();
297
298 let option = test.service;
299 assert!(option.is_some());
300
301 let service = option.unwrap();
302 assert_eq!(service.uri().to_string(), uri_string.to_string());
303 assert_eq!(
304 service.cluster_name(),
305 "NoPolicyId-localhost-8080-a5ec11e-service.NoPolicyNamespace.svc"
306 );
307 }
308
309 #[test]
310 fn deserialize_opt_service_invalid_uri() {
311 let result: Result<TestOpt, serde_json::Error> =
312 serde_json::from_value(json!({ "service": "localhost:8080/api" }));
313
314 assert_eq!(
315 result.unwrap_err().to_string(),
316 "Error parsing localhost:8080/api as Uri: invalid format"
317 )
318 }
319
320 #[test]
321 fn deserialize_opt_missing_field() {
322 let test: TestOpt = serde_json::from_value(json!({})).unwrap();
323
324 assert!(test.service.is_none());
325 }
326
327 #[test]
328 fn deserialize_service_opt_vec_successful_parsing() {
329 let uri_string_1 = "https://localhost:8080/api";
330 let uri_string_2 = "https://another-host:9090/api";
331
332 let test: TestOptVec =
333 serde_json::from_value(json!({ "services": [uri_string_1, uri_string_2] })).unwrap();
334
335 let option = test.services;
336 assert!(option.is_some());
337
338 let services = option.unwrap();
339 assert_eq!(services.len(), 2);
340 let service1 = services.first().unwrap();
341 assert_eq!(service1.uri().to_string(), uri_string_1.to_string());
342 assert_eq!(
343 service1.cluster_name(),
344 "NoPolicyId-localhost-8080-a5ec11e-service.NoPolicyNamespace.svc"
345 );
346 let service2 = services.get(1).unwrap();
347 assert_eq!(service2.uri().to_string(), uri_string_2.to_string());
348 assert_eq!(
349 service2.cluster_name(),
350 "NoPolicyId-another-host-9090-f35336e-service.NoPolicyNamespace.svc"
351 );
352 }
353
354 #[test]
355 fn deserialize_service_opt_vec_invalid_first_uri() {
356 let result: Result<TestOptVec, serde_json::Error> = serde_json::from_value(
357 json!({ "services": ["localhost:8080/api", "https://another-host:9090/api"] }),
358 );
359
360 assert_eq!(
361 result.unwrap_err().to_string(),
362 "Error parsing localhost:8080/api as Uri: invalid format"
363 )
364 }
365
366 #[test]
367 fn deserialize_service_opt_vec_invalid_last_uri() {
368 let result: Result<TestOptVec, serde_json::Error> = serde_json::from_value(
369 json!({ "services": ["https://localhost:8080/api", "another-host:9090/api"] }),
370 );
371
372 assert_eq!(
373 result.unwrap_err().to_string(),
374 "Error parsing another-host:9090/api as Uri: invalid format"
375 )
376 }
377
378 #[test]
379 fn deserialize_service_opt_vec_missing_field() {
380 let test: TestOptVec = serde_json::from_value(json!({})).unwrap();
381
382 assert!(test.services.is_none());
383 }
384
385 #[derive(Debug, Deserialize)]
386 struct TestOpt {
387 #[serde(default)]
388 #[serde(deserialize_with = "deserialize_service_opt")]
389 pub service: Option<Service>,
390 }
391
392 #[derive(Debug, Deserialize)]
393 struct TestOptVec {
394 #[serde(default)]
395 #[serde(deserialize_with = "deserialize_service_opt_vec")]
396 pub services: Option<Vec<Service>>,
397 }
398
399 fn deserializer(uri_string: &str) -> StrDeserializer<serde::de::value::Error> {
400 let deserializer: StrDeserializer<ValueError> = uri_string.into_deserializer();
401 deserializer
402 }
403
404 fn vec_deserializer(vec: Vec<&str>) -> SeqDeserializer<IntoIter<&str>, ValueError> {
405 let deserializer = vec.into_deserializer();
406 deserializer
407 }
408}