autopush_common/db/
mod.rs1use std::collections::{HashMap, HashSet};
12use std::result::Result as StdResult;
13
14use derive_builder::Builder;
15use serde::Serializer;
16use serde_derive::{Deserialize, Serialize};
17use uuid::Uuid;
18
19#[cfg(feature = "bigtable")]
20pub mod bigtable;
21pub mod client;
22pub mod error;
23pub mod models;
24pub mod reporter;
25pub mod routing;
26
27pub mod mock;
29
30pub use reporter::spawn_pool_periodic_reporter;
31
32use crate::notification::Notification;
33use crate::util::timing::ms_since_epoch;
34use models::RangeKey;
35
36pub const USER_RECORD_VERSION: u64 = 1;
37
38#[derive(Eq, Debug, PartialEq)]
39pub enum StorageType {
40 INVALID,
41 #[cfg(feature = "bigtable")]
42 BigTable,
43}
44
45impl From<&str> for StorageType {
46 fn from(name: &str) -> Self {
47 match name.to_lowercase().as_str() {
48 #[cfg(feature = "bigtable")]
49 "bigtable" => Self::BigTable,
50 _ => Self::INVALID,
51 }
52 }
53}
54
55#[allow(clippy::vec_init_then_push)] impl StorageType {
58 fn available<'a>() -> Vec<&'a str> {
59 #[allow(unused_mut)]
60 let mut result: Vec<&str> = Vec::new();
61 #[cfg(feature = "bigtable")]
62 result.push("Bigtable");
63 result
64 }
65
66 pub fn from_dsn(dsn: &Option<String>) -> Self {
67 debug!("Supported data types: {:?}", StorageType::available());
68 debug!("Checking DSN: {:?}", &dsn);
69 if dsn.is_none() {
70 let default = Self::available()[0];
71 info!("No DSN specified, failing over to old default dsn: {default}");
72 return Self::from(default);
73 }
74 let dsn = dsn.clone().unwrap_or_default();
75 #[cfg(feature = "bigtable")]
76 if dsn.starts_with("grpc") {
77 trace!("Found grpc");
78 if let Ok(cred) = std::env::var("GOOGLE_APPLICATION_CREDENTIALS") {
84 trace!("Env: {:?}", cred);
85 }
86 return Self::BigTable;
87 }
88 Self::INVALID
89 }
90}
91
92#[derive(Clone, Debug, Default, Deserialize)]
95pub struct DbSettings {
96 pub dsn: Option<String>,
98 pub db_settings: String,
103}
104pub fn uuid_serializer<S>(x: &Uuid, s: S) -> StdResult<S::Ok, S::Error>
111where
112 S: Serializer,
113{
114 s.serialize_str(&x.simple().to_string())
115}
116
117#[derive(Clone, Default, Debug)]
118pub struct CheckStorageResponse {
119 pub include_topic: bool,
123 pub messages: Vec<Notification>,
125 pub timestamp: Option<u64>,
127}
128
129#[derive(Deserialize, PartialEq, Debug, Clone, Serialize, Builder)]
131#[builder(default, setter(strip_option))]
132pub struct User {
133 #[serde(serialize_with = "uuid_serializer")]
136 pub uaid: Uuid,
137 pub connected_at: u64,
139 pub router_type: String,
141 pub router_data: Option<HashMap<String, serde_json::Value>>,
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub node_id: Option<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub record_version: Option<u64>,
149 #[serde(skip_serializing_if = "Option::is_none")]
153 pub current_timestamp: Option<u64>,
154 #[serde(skip_serializing)]
156 pub version: Option<Uuid>,
157 priv_channels: HashSet<Uuid>,
170}
171
172impl Default for User {
173 fn default() -> Self {
174 let uaid = Uuid::new_v4();
175 Self {
177 uaid,
178 connected_at: ms_since_epoch(),
179 router_type: "webpush".to_string(),
180 router_data: None,
181 node_id: None,
182 record_version: Some(USER_RECORD_VERSION),
183 current_timestamp: None,
184 version: Some(Uuid::new_v4()),
185 priv_channels: HashSet::new(),
186 }
187 }
188}
189
190impl User {
191 pub fn builder() -> UserBuilder {
193 UserBuilder::default()
194 }
195
196 pub fn channel_count(&self) -> usize {
197 self.priv_channels.len()
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::{User, USER_RECORD_VERSION};
204
205 #[test]
206 fn user_defaults() {
207 let user = User::builder().current_timestamp(22).build().unwrap();
208 assert_eq!(user.current_timestamp, Some(22));
209 assert_eq!(user.router_type, "webpush".to_owned());
210 assert_eq!(user.record_version, Some(USER_RECORD_VERSION));
211 }
212}