autopush_common/db/
models.rs1#[cfg(any(test, feature = "bigtable"))]
2use lazy_static::lazy_static;
3#[cfg(any(test, feature = "bigtable"))]
4use regex::RegexSet;
5
6#[cfg(any(test, feature = "bigtable"))]
7use crate::errors::{ApcErrorKind, Result};
8#[cfg(any(test, feature = "bigtable"))]
9use crate::notification::{STANDARD_NOTIFICATION_PREFIX, TOPIC_NOTIFICATION_PREFIX};
10#[cfg(any(test, feature = "bigtable"))]
11use uuid::Uuid;
12
13#[cfg(any(test, feature = "bigtable"))]
15#[derive(Debug)]
16pub(crate) struct RangeKey {
17 pub(crate) channel_id: Uuid,
19 pub(crate) topic: Option<String>,
21 pub(crate) sortkey_timestamp: Option<u64>,
23 #[allow(unused)]
25 pub(crate) legacy_version: Option<String>,
26}
27
28#[cfg(any(test, feature = "bigtable"))]
29impl RangeKey {
30 pub(crate) fn parse_chidmessageid(key: &str) -> Result<RangeKey> {
32 lazy_static! {
33 static ref RE: RegexSet = RegexSet::new([
34 format!("^{TOPIC_NOTIFICATION_PREFIX}:\\S+:\\S+$").as_str(),
35 format!("^{STANDARD_NOTIFICATION_PREFIX}:\\d+:\\S+$").as_str(),
36 "^\\S{3,}:\\S+$"
37 ])
38 .unwrap();
39 }
40 if !RE.is_match(key) {
41 return Err(ApcErrorKind::GeneralError("Invalid chidmessageid".into()).into());
42 }
43
44 let v: Vec<&str> = key.split(':').collect();
45 match v[0] {
46 "01" => {
48 if v.len() != 3 {
49 return Err(ApcErrorKind::GeneralError("Invalid topic key".into()).into());
50 }
51 let (channel_id, topic) = (v[1], v[2]);
52 let channel_id = Uuid::parse_str(channel_id)?;
53 Ok(RangeKey {
54 channel_id,
55 topic: Some(topic.to_string()),
56 sortkey_timestamp: None,
57 legacy_version: None,
58 })
59 }
60 "02" => {
62 if v.len() != 3 {
63 return Err(ApcErrorKind::GeneralError("Invalid topic key".into()).into());
64 }
65 let (sortkey, channel_id) = (v[1], v[2]);
66 let channel_id = Uuid::parse_str(channel_id)?;
67 Ok(RangeKey {
68 channel_id,
69 topic: None,
70 sortkey_timestamp: Some(sortkey.parse()?),
71 legacy_version: None,
72 })
73 }
74 _ => {
78 if v.len() != 2 {
79 return Err(ApcErrorKind::GeneralError("Invalid topic key".into()).into());
80 }
81 let (channel_id, legacy_version) = (v[0], v[1]);
82 let channel_id = Uuid::parse_str(channel_id)?;
83 Ok(RangeKey {
84 channel_id,
85 topic: None,
86 sortkey_timestamp: None,
87 legacy_version: Some(legacy_version.to_string()),
88 })
89 }
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::RangeKey;
97 use crate::util::us_since_epoch;
98 use uuid::Uuid;
99
100 #[test]
101 fn test_parse_sort_key_ver1() {
102 let chid = Uuid::new_v4();
103 let chidmessageid = format!("01:{}:mytopic", chid.hyphenated());
104 let key = RangeKey::parse_chidmessageid(&chidmessageid).unwrap();
105 assert_eq!(key.topic, Some("mytopic".to_string()));
106 assert_eq!(key.channel_id, chid);
107 assert_eq!(key.sortkey_timestamp, None);
108 }
109
110 #[test]
111 fn test_parse_sort_key_ver2() {
112 let chid = Uuid::new_v4();
113 let sortkey_timestamp = us_since_epoch();
114 let chidmessageid = format!("02:{}:{}", sortkey_timestamp, chid.hyphenated());
115 let key = RangeKey::parse_chidmessageid(&chidmessageid).unwrap();
116 assert_eq!(key.topic, None);
117 assert_eq!(key.channel_id, chid);
118 assert_eq!(key.sortkey_timestamp, Some(sortkey_timestamp));
119 }
120
121 #[test]
122 fn test_parse_sort_key_bad_values() {
123 for val in &["02j3i2o", "03:ffas:wef", "01::mytopic", "02:oops:ohnoes"] {
124 let key = RangeKey::parse_chidmessageid(val);
125 assert!(key.is_err());
126 }
127 }
128}