Skip to content

Commit 67484e5

Browse files
committed
feat(common): add 'sanitize' capability to cdbv2 artifact locations
Those methodes filters out all `Unknown` locations and fails if they were all `Unknown`
1 parent cd3b820 commit 67484e5

File tree

1 file changed

+229
-0
lines changed

1 file changed

+229
-0
lines changed

mithril-common/src/messages/cardano_database.rs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use anyhow::anyhow;
12
use chrono::{DateTime, Utc};
23
use serde::{Deserialize, Serialize};
34

45
use crate::entities::{
56
AncillaryLocation, AncillaryLocations, CardanoDbBeacon, CompressionAlgorithm, DigestLocation,
67
DigestsLocations, Epoch, ImmutablesLocation, ImmutablesLocations, MultiFilesUri, TemplateUri,
78
};
9+
use crate::StdResult;
810

911
/// The message part that represents the locations of the Cardano database digests.
1012
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
@@ -15,6 +17,25 @@ pub struct DigestsMessagePart {
1517
/// Locations of the digests.
1618
pub locations: Vec<DigestLocation>,
1719
}
20+
21+
impl DigestsMessagePart {
22+
/// Return the list of locations without the unknown locations, failing if all locations are unknown.
23+
pub fn sanitized_locations(&self) -> StdResult<Vec<DigestLocation>> {
24+
let sanitized_locations: Vec<_> = self
25+
.locations
26+
.iter()
27+
.filter(|l| !matches!(l, DigestLocation::Unknown))
28+
.cloned()
29+
.collect();
30+
31+
if sanitized_locations.is_empty() {
32+
Err(anyhow!("All digests locations are unknown."))
33+
} else {
34+
Ok(sanitized_locations)
35+
}
36+
}
37+
}
38+
1839
/// The message part that represents the locations of the Cardano database immutables.
1940
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
2041
pub struct ImmutablesMessagePart {
@@ -24,6 +45,25 @@ pub struct ImmutablesMessagePart {
2445
/// Locations of the immutable files.
2546
pub locations: Vec<ImmutablesLocation>,
2647
}
48+
49+
impl ImmutablesMessagePart {
50+
/// Return the list of locations without the unknown locations, failing if all locations are unknown.
51+
pub fn sanitized_locations(&self) -> StdResult<Vec<ImmutablesLocation>> {
52+
let sanitized_locations: Vec<_> = self
53+
.locations
54+
.iter()
55+
.filter(|l| !matches!(l, ImmutablesLocation::Unknown))
56+
.cloned()
57+
.collect();
58+
59+
if sanitized_locations.is_empty() {
60+
Err(anyhow!("All locations are unknown."))
61+
} else {
62+
Ok(sanitized_locations)
63+
}
64+
}
65+
}
66+
2767
/// The message part that represents the locations of the Cardano database ancillary.
2868
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
2969
pub struct AncillaryMessagePart {
@@ -34,6 +74,24 @@ pub struct AncillaryMessagePart {
3474
pub locations: Vec<AncillaryLocation>,
3575
}
3676

77+
impl AncillaryMessagePart {
78+
/// Return the list of locations without the unknown locations, failing if all locations are unknown.
79+
pub fn sanitized_locations(&self) -> StdResult<Vec<AncillaryLocation>> {
80+
let sanitized_locations: Vec<_> = self
81+
.locations
82+
.iter()
83+
.filter(|l| !matches!(l, AncillaryLocation::Unknown))
84+
.cloned()
85+
.collect();
86+
87+
if sanitized_locations.is_empty() {
88+
Err(anyhow!("All locations are unknown."))
89+
} else {
90+
Ok(sanitized_locations)
91+
}
92+
}
93+
}
94+
3795
impl From<DigestsLocations> for DigestsMessagePart {
3896
fn from(part: DigestsLocations) -> Self {
3997
Self {
@@ -324,4 +382,175 @@ mod tests {
324382
assert_eq!(message.ancillary.locations.len(), 1);
325383
assert_eq!(AncillaryLocation::Unknown, message.ancillary.locations[0]);
326384
}
385+
386+
mod sanitize_immutable_locations {
387+
use super::*;
388+
389+
#[test]
390+
fn succeeds_and_leave_all_locations_intact_if_no_unknown_location() {
391+
let immutable_locations = ImmutablesMessagePart {
392+
locations: vec![ImmutablesLocation::CloudStorage {
393+
uri: MultiFilesUri::Template(TemplateUri(
394+
"http://whatever/{immutable_file_number}.tar.gz".to_string(),
395+
)),
396+
compression_algorithm: None,
397+
}],
398+
average_size_uncompressed: 512,
399+
};
400+
401+
let sanitize_locations = immutable_locations
402+
.sanitized_locations()
403+
.expect("Should succeed since there are no unknown locations.");
404+
assert_eq!(sanitize_locations, immutable_locations.locations);
405+
}
406+
407+
#[test]
408+
fn succeeds_and_remove_unknown_locations_if_some_locations_are_not_unknown() {
409+
let immutable_locations = ImmutablesMessagePart {
410+
locations: vec![
411+
ImmutablesLocation::CloudStorage {
412+
uri: MultiFilesUri::Template(TemplateUri(
413+
"http://whatever/{immutable_file_number}.tar.gz".to_string(),
414+
)),
415+
compression_algorithm: None,
416+
},
417+
ImmutablesLocation::Unknown,
418+
],
419+
average_size_uncompressed: 512,
420+
};
421+
422+
let sanitize_locations = immutable_locations
423+
.sanitized_locations()
424+
.expect("Should succeed since not all locations are unknown.");
425+
assert_eq!(
426+
sanitize_locations,
427+
vec![ImmutablesLocation::CloudStorage {
428+
uri: MultiFilesUri::Template(TemplateUri(
429+
"http://whatever/{immutable_file_number}.tar.gz".to_string(),
430+
)),
431+
compression_algorithm: None,
432+
}]
433+
);
434+
}
435+
436+
#[test]
437+
fn fails_if_all_locations_are_unknown() {
438+
ImmutablesMessagePart {
439+
locations: vec![ImmutablesLocation::Unknown],
440+
average_size_uncompressed: 512,
441+
}
442+
.sanitized_locations()
443+
.expect_err("Should fail since all locations are unknown.");
444+
}
445+
}
446+
447+
mod sanitize_ancillary_locations {
448+
use super::*;
449+
450+
#[test]
451+
fn succeeds_and_leave_all_locations_intact_if_no_unknown_location() {
452+
let ancillary_locations = AncillaryMessagePart {
453+
locations: vec![AncillaryLocation::CloudStorage {
454+
uri: "http://whatever/ancillary.tar.gz".to_string(),
455+
compression_algorithm: None,
456+
}],
457+
size_uncompressed: 1024,
458+
};
459+
460+
let sanitize_locations = ancillary_locations
461+
.sanitized_locations()
462+
.expect("Should succeed since there are no unknown locations.");
463+
assert_eq!(sanitize_locations, ancillary_locations.locations);
464+
}
465+
466+
#[test]
467+
fn succeeds_and_remove_unknown_locations_if_some_locations_are_not_unknown() {
468+
let ancillary_locations = AncillaryMessagePart {
469+
locations: vec![
470+
AncillaryLocation::CloudStorage {
471+
uri: "http://whatever/digests.tar.gz".to_string(),
472+
compression_algorithm: None,
473+
},
474+
AncillaryLocation::Unknown,
475+
],
476+
size_uncompressed: 512,
477+
};
478+
479+
let sanitize_locations = ancillary_locations
480+
.sanitized_locations()
481+
.expect("Should succeed since not all locations are unknown.");
482+
assert_eq!(
483+
sanitize_locations,
484+
vec![AncillaryLocation::CloudStorage {
485+
uri: "http://whatever/digests.tar.gz".to_string(),
486+
compression_algorithm: None,
487+
}]
488+
);
489+
}
490+
491+
#[test]
492+
fn fails_if_all_locations_are_unknown() {
493+
AncillaryMessagePart {
494+
locations: vec![AncillaryLocation::Unknown],
495+
size_uncompressed: 512,
496+
}
497+
.sanitized_locations()
498+
.expect_err("Should fail since all locations are unknown.");
499+
}
500+
}
501+
502+
mod sanitize_digests_locations {
503+
use super::*;
504+
505+
#[test]
506+
fn succeeds_and_leave_all_locations_intact_if_no_unknown_location() {
507+
let digests_locations = DigestsMessagePart {
508+
locations: vec![DigestLocation::CloudStorage {
509+
uri: "http://whatever/digests.tar.gz".to_string(),
510+
compression_algorithm: None,
511+
}],
512+
size_uncompressed: 512,
513+
};
514+
515+
let sanitize_locations = digests_locations
516+
.sanitized_locations()
517+
.expect("Should succeed since there are no unknown locations.");
518+
assert_eq!(sanitize_locations, digests_locations.locations);
519+
}
520+
521+
#[test]
522+
fn succeeds_and_remove_unknown_locations_if_some_locations_are_not_unknown() {
523+
let digests_locations = DigestsMessagePart {
524+
locations: vec![
525+
DigestLocation::CloudStorage {
526+
uri: "http://whatever/digests.tar.gz".to_string(),
527+
compression_algorithm: None,
528+
},
529+
DigestLocation::Unknown,
530+
],
531+
size_uncompressed: 512,
532+
};
533+
534+
let sanitize_locations = digests_locations
535+
.sanitized_locations()
536+
.expect("Should succeed since not all locations are unknown.");
537+
assert_eq!(
538+
sanitize_locations,
539+
vec![DigestLocation::CloudStorage {
540+
uri: "http://whatever/digests.tar.gz".to_string(),
541+
compression_algorithm: None,
542+
}]
543+
);
544+
}
545+
546+
#[test]
547+
fn fails_if_all_locations_are_unknown() {
548+
DigestsMessagePart {
549+
locations: vec![DigestLocation::Unknown],
550+
size_uncompressed: 512,
551+
}
552+
.sanitized_locations()
553+
.expect_err("Should fail since all locations are unknown.");
554+
}
555+
}
327556
}

0 commit comments

Comments
 (0)