notifica_common/configuration/
mod.rs1use core::fmt;
2use std::{collections::HashMap, error::Error};
3
4use serde::{Deserialize, Serialize};
5
6use crate::configuration::{
7 email::EmailEventBody,
8 push::{ProvidersConfig, PushEventBody},
9 request::{RequestEvent, RequestEventBody},
10};
11
12#[cfg(test)]
13use crate::configuration::push::ExpoConfig;
14
15pub mod email;
16pub mod push;
17pub mod request;
18
19#[derive(Clone, Debug, Serialize, Deserialize)]
20pub struct CommonEvent<T> {
21 pub template_path: String,
22 pub default_params: Option<T>,
23}
24
25#[derive(Clone, Debug, Serialize, Deserialize)]
26pub struct WebhookDto {
27 pub email: Option<EmailEventBody>,
28 pub push: Option<PushEventBody>,
29 pub request: Option<RequestEventBody>,
30 pub automation: Option<RequestEventBody>,
31}
32
33#[derive(Clone, Debug, Serialize, Deserialize)]
34pub struct QueueDto {
35 pub tenant_id: String,
36 pub event_name: String,
37 pub email: Option<EmailEventBody>,
38 pub push: Option<PushEventBody>,
39 pub request: Option<RequestEventBody>,
40 pub automation: Option<RequestEventBody>,
41}
42
43pub enum Dto<'a> {
44 QueueDto(&'a mut QueueDto),
45 WebhookDto(&'a mut WebhookDto),
46}
47
48#[derive(Debug, Serialize, Deserialize, Clone)]
49pub struct Config {
50 pub tenant_id: String,
51 pub email: Option<HashMap<String, CommonEvent<EmailEventBody>>>,
52 pub push: Option<HashMap<String, CommonEvent<PushEventBody>>>,
53 pub automation: Option<HashMap<String, RequestEvent>>,
54 pub request: Option<HashMap<String, RequestEvent>>,
55 pub config: Option<Configuration>,
56 pub webhooks: HashMap<String, WebhookActionSettings>,
57}
58
59#[derive(Clone, Debug, Serialize, Deserialize)]
60pub struct Configuration {
61 push: Option<ProvidersConfig>,
62}
63
64#[derive(Default)]
65pub struct TenantConfig(HashMap<String, Config>);
66
67#[derive(Clone, Debug, Serialize, Deserialize)]
68pub struct WebhookActionSettings {
69 pub request: Option<RequestEvent>,
70 pub email: Option<CommonEvent<EmailEventBody>>,
71 pub push: Option<CommonEvent<PushEventBody>>,
72 pub automation: Option<RequestEvent>,
73}
74
75pub enum WebhookAction {
76 Request(Option<RequestEvent>),
77 Automation(Option<RequestEvent>),
78 Email(Option<CommonEvent<EmailEventBody>>),
79 Push(Option<CommonEvent<PushEventBody>>),
80}
81
82impl Config {
83 pub fn get_email_actions(self) -> Vec<String> {
84 self.email.unwrap_or_default().into_keys().collect()
85 }
86
87 pub fn get_template_by_email_action(self, action: String) -> String {
88 self.email
89 .unwrap_or_default()
90 .get(&action)
91 .unwrap()
92 .template_path
93 .clone()
94 }
95
96 pub fn get_expo_access_token(self) -> Result<String, TenantConfigError> {
97 if self.config.is_none() {
98 return Err(TenantConfigError::ExtraConfigMissing);
99 }
100
101 let unwrapped_config = self.config.unwrap();
102
103 if unwrapped_config.push.is_none() {
104 return Err(TenantConfigError::ExtraConfigPushSettingsMissing);
105 }
106
107 let unwrapped_push_settings = unwrapped_config.push.unwrap();
108
109 if unwrapped_push_settings.expo.is_none() {
110 return Err(TenantConfigError::ExtraConfigPushSettingsMissing);
111 }
112
113 Ok(unwrapped_push_settings.expo.unwrap().access_token)
114 }
115}
116
117#[derive(Debug)]
118pub enum TenantConfigError {
119 Missing,
120 WrongStructure,
121 WebhookActionSettingsMissing,
122 WebhookRequestActionMissing,
123 WebhookPushActionMissing,
124 WebhookEmailActionMissing,
125 QueueEmailActionMissing,
126 QueuePushActionMissing,
127 QueueActionMissing,
128 QueueRequestActionMissing,
129 QueueAutomationActionMissing,
130 ExtraConfigExpoTokenMissing,
131 ExtraConfigPushSettingsMissing,
132 ExtraConfigMissing,
133}
134
135impl fmt::Display for TenantConfigError {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 match self {
138 TenantConfigError::Missing => write!(f, "Tenant configuration is missing"),
139 TenantConfigError::WebhookActionSettingsMissing => {
140 write!(f, "Webhook action settings is missing")
141 }
142 TenantConfigError::QueueActionMissing => {
143 write!(f, "Queue action is missing")
144 }
145 TenantConfigError::WebhookEmailActionMissing => {
146 write!(f, "Webhook email action is missing")
147 }
148 TenantConfigError::WebhookPushActionMissing => {
149 write!(f, "Webhook push action is missing")
150 }
151 TenantConfigError::WebhookRequestActionMissing => {
152 write!(f, "Webhook request action is missing")
153 }
154 TenantConfigError::QueueRequestActionMissing => {
155 write!(f, "Queue request action is missing")
156 }
157 TenantConfigError::QueueEmailActionMissing => {
158 write!(f, "Queue email action is missing")
159 }
160 TenantConfigError::QueuePushActionMissing => {
161 write!(f, "Queue push action is missing")
162 }
163 TenantConfigError::QueueAutomationActionMissing => {
164 write!(f, "Queue automation action is missing")
165 }
166 TenantConfigError::WrongStructure => write!(f, "Config is broken"),
167 TenantConfigError::ExtraConfigMissing => write!(f, "Extra config is missing"),
168 TenantConfigError::ExtraConfigExpoTokenMissing => {
169 write!(f, "Extra config expo token is missing")
170 }
171 TenantConfigError::ExtraConfigPushSettingsMissing => {
172 write!(f, "Extra push config expo token is missing")
173 }
174 }
175 }
176}
177
178impl Error for TenantConfigError {}
179
180pub type TenantConfigResult = Result<Config, TenantConfigError>;
181pub type TenantResult = Result<TenantConfig, TenantConfigError>;
182
183impl TenantConfig {
184 pub fn new() -> Self {
185 Self::default()
186 }
187
188 pub fn insert(&mut self, key: String, value: Config) {
189 self.0.insert(key, value);
190 }
191
192 pub fn get(&self, key: &str) -> TenantConfigResult {
193 if let Some(value) = self.0.get(key) {
194 let res = value.clone();
195 return Ok(res);
196 }
197 Err(TenantConfigError::Missing)
198 }
199
200 pub fn keys(&self) -> std::collections::hash_map::Keys<'_, std::string::String, Config> {
201 self.0.keys()
202 }
203
204 pub fn contains_key(&self, key: &str) -> bool {
205 self.0.contains_key(key)
206 }
207
208 pub fn is_empty(&self) -> bool {
209 self.0.is_empty()
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use std::collections::HashMap;
217
218 fn create_test_config() -> Config {
219 Config {
220 tenant_id: "test_tenant".to_string(),
221 email: Some(HashMap::new()),
222 push: Some(HashMap::new()),
223 request: Some(HashMap::new()),
224 automation: Some(HashMap::new()),
225 config: None,
226 webhooks: HashMap::new(),
227 }
228 }
229
230 fn create_test_config_with_email_actions() -> Config {
231 let mut email_map = HashMap::new();
232 email_map.insert(
233 "welcome".to_string(),
234 CommonEvent {
235 template_path: "test/welcome.html".to_string(),
236 default_params: Some(EmailEventBody {
237 target_email: "test@example.com".to_string(),
238 target_name: Some("Test User".to_string()),
239 sender_name: Some("Test App".to_string()),
240 subject: Some("Welcome".to_string()),
241 params: None,
242 }),
243 },
244 );
245
246 Config {
247 tenant_id: "test_tenant".to_string(),
248 email: Some(email_map),
249 push: Some(HashMap::new()),
250 request: Some(HashMap::new()),
251 automation: Some(HashMap::new()),
252 config: None,
253 webhooks: HashMap::new(),
254 }
255 }
256
257 fn create_test_config_with_expo_token() -> Config {
258 let expo_config = ExpoConfig {
259 access_token: "test_token_123".to_string(),
260 };
261 let providers_config = ProvidersConfig {
262 ios: None,
263 aos: None,
264 expo: Some(expo_config),
265 };
266 let configuration = Configuration {
267 push: Some(providers_config),
268 };
269
270 Config {
271 tenant_id: "test_tenant".to_string(),
272 email: Some(HashMap::new()),
273 push: Some(HashMap::new()),
274 request: Some(HashMap::new()),
275 automation: Some(HashMap::new()),
276 config: Some(configuration),
277 webhooks: HashMap::new(),
278 }
279 }
280
281 #[test]
282 fn test_config_get_email_actions() {
283 let config = create_test_config_with_email_actions();
284 let actions = config.get_email_actions();
285 assert_eq!(actions.len(), 1);
286 assert!(actions.contains(&"welcome".to_string()));
287 }
288
289 #[test]
290 fn test_config_get_email_actions_empty() {
291 let config = create_test_config();
292 let actions = config.get_email_actions();
293 assert!(actions.is_empty());
294 }
295
296 #[test]
297 fn test_config_get_template_by_email_action() {
298 let config = create_test_config_with_email_actions();
299 let template_path = config.get_template_by_email_action("welcome".to_string());
300 assert_eq!(template_path, "test/welcome.html");
301 }
302
303 #[test]
304 #[should_panic]
305 fn test_config_get_template_by_email_action_invalid() {
306 let config = create_test_config();
307 config.get_template_by_email_action("invalid".to_string());
308 }
309
310 #[test]
311 fn test_config_get_expo_access_token_success() {
312 let config = create_test_config_with_expo_token();
313 let result = config.get_expo_access_token();
314 assert!(result.is_ok());
315 assert_eq!(result.unwrap(), "test_token_123");
316 }
317
318 #[test]
319 fn test_config_get_expo_access_token_missing_config() {
320 let config = create_test_config();
321 let result = config.get_expo_access_token();
322 assert!(result.is_err());
323 assert!(matches!(
324 result.unwrap_err(),
325 TenantConfigError::ExtraConfigMissing
326 ));
327 }
328
329 #[test]
330 fn test_tenant_config_new() {
331 let tenant_config = TenantConfig::new();
332 assert!(tenant_config.is_empty());
333 }
334
335 #[test]
336 fn test_tenant_config_insert_and_get() {
337 let mut tenant_config = TenantConfig::new();
338 let config = create_test_config();
339 tenant_config.insert("test_tenant".to_string(), config.clone());
340
341 let result = tenant_config.get("test_tenant");
342 assert!(result.is_ok());
343 let retrieved_config = result.unwrap();
344 assert_eq!(retrieved_config.tenant_id, "test_tenant");
345 }
346
347 #[test]
348 fn test_tenant_config_get_missing() {
349 let tenant_config = TenantConfig::new();
350 let result = tenant_config.get("missing_tenant");
351 assert!(result.is_err());
352 assert!(matches!(result.unwrap_err(), TenantConfigError::Missing));
353 }
354
355 #[test]
356 fn test_tenant_config_contains_key() {
357 let mut tenant_config = TenantConfig::new();
358 let config = create_test_config();
359 tenant_config.insert("test_tenant".to_string(), config);
360
361 assert!(tenant_config.contains_key("test_tenant"));
362 assert!(!tenant_config.contains_key("missing_tenant"));
363 }
364
365 #[test]
366 fn test_tenant_config_keys() {
367 let mut tenant_config = TenantConfig::new();
368 let config1 = create_test_config();
369 let config2 = create_test_config();
370 tenant_config.insert("tenant1".to_string(), config1);
371 tenant_config.insert("tenant2".to_string(), config2);
372
373 let keys: Vec<&String> = tenant_config.keys().collect();
374 assert_eq!(keys.len(), 2);
375 assert!(keys.contains(&&"tenant1".to_string()));
376 assert!(keys.contains(&&"tenant2".to_string()));
377 }
378
379 #[test]
380 fn test_tenant_config_error_display() {
381 let error = TenantConfigError::Missing;
382 assert_eq!(error.to_string(), "Tenant configuration is missing");
383
384 let error = TenantConfigError::WrongStructure;
385 assert_eq!(error.to_string(), "Config is broken");
386
387 let error = TenantConfigError::WebhookActionSettingsMissing;
388 assert_eq!(error.to_string(), "Webhook action settings is missing");
389 }
390}