diff --git a/api/src/domain/models/alert_config.rs b/api/src/domain/models/alert_config.rs index a99d600c..7045662a 100644 --- a/api/src/domain/models/alert_config.rs +++ b/api/src/domain/models/alert_config.rs @@ -22,8 +22,8 @@ pub struct AlertConfig { /// The type of alert. #[serde(rename = "type")] pub type_: AlertType, - /// A list of IDs for Monitors that this alert configuration is associated with. - pub monitor_ids: Vec, + /// A list of Monitors that this alert configuration is applied on. + pub monitors: Vec, } /// The different types of alerts that can be configured. @@ -43,6 +43,15 @@ pub struct SlackAlertConfig { pub token: String, } +/// Brief info on a Monitor using an alert configuration. +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AppliedMonitor { + /// The ID of a Monitor using an alert configuration. + pub monitor_id: Uuid, + /// The name of a Monitor using an alert configuration. + pub name: String, +} + impl AlertConfig { /// Create a new `AlertConfig` for Slack. pub fn new_slack_config( @@ -62,7 +71,7 @@ impl AlertConfig { on_late, on_error, type_: AlertType::Slack(SlackAlertConfig { channel, token }), - monitor_ids: Vec::new(), + monitors: Vec::new(), } } @@ -75,7 +84,10 @@ impl AlertConfig { monitor.monitor_id, self.alert_config_id ))); } - self.monitor_ids.push(monitor.monitor_id); + self.monitors.push(AppliedMonitor { + monitor_id: monitor.monitor_id, + name: monitor.name.clone(), + }); Ok(()) } @@ -89,13 +101,19 @@ impl AlertConfig { monitor.monitor_id, self.alert_config_id ))); } - self.monitor_ids.retain(|&id| id != monitor.monitor_id); + self.monitors + .retain(|monitor_info| monitor_info.monitor_id != monitor.monitor_id); Ok(()) } /// Check if the alert configuration is associated with a monitor. pub fn is_associated_with_monitor(&self, monitor: &Monitor) -> bool { - self.monitor_ids.contains(&monitor.monitor_id) + let monitor_ids: Vec<_> = self + .monitors + .iter() + .map(|monitor_info| monitor_info.monitor_id) + .collect(); + monitor_ids.contains(&monitor.monitor_id) } } @@ -134,26 +152,33 @@ mod tests { token: "test-token".to_string(), }) ); - assert!(alert_config.monitor_ids.is_empty()); + assert!(alert_config.monitors.is_empty()); } #[test] fn test_serialisation() { - let alert_config = AlertConfig::new_slack_config( - "test-name".to_string(), - "test-tenant".to_string(), - true, - true, - true, - "test-channel".to_string(), - "test-token".to_string(), - ); + let alert_config = AlertConfig { + alert_config_id: gen_uuid("3867e53d-9c17-4ce9-b153-eff3d8c9edec"), + name: "test-name".to_string(), + tenant: "test-tenant".to_string(), + active: true, + on_late: true, + on_error: true, + type_: AlertType::Slack(SlackAlertConfig { + channel: "test-channel".to_string(), + token: "test-token".to_string(), + }), + monitors: vec![AppliedMonitor { + monitor_id: gen_uuid("ba0cd705-4a5b-4635-9def-611b1143e4aa"), + name: "test-name".to_string(), + }], + }; let value = serde_json::to_value(&alert_config).unwrap(); assert_eq!( value, json!({ - "alert_config_id": alert_config.alert_config_id.to_string(), + "alert_config_id": "3867e53d-9c17-4ce9-b153-eff3d8c9edec", "name": "test-name", "tenant": "test-tenant", "active": true, @@ -165,7 +190,12 @@ mod tests { "token": "test-token" } }, - "monitor_ids": [] + "monitors": [ + { + "monitor_id": "ba0cd705-4a5b-4635-9def-611b1143e4aa", + "name": "test-name" + } + ] }) ); } @@ -184,17 +214,23 @@ mod tests { let monitor = Monitor::new("test-tenant".to_string(), "test-name".to_string(), 200, 100); // Sanity check to make sure we start from a clean slate. - assert_eq!(alert_config.monitor_ids, vec![]); + assert_eq!(alert_config.monitors, vec![]); assert!(!alert_config.is_associated_with_monitor(&monitor)); alert_config.associate_monitor(&monitor).unwrap(); - assert_eq!(alert_config.monitor_ids, vec![monitor.monitor_id]); + assert_eq!( + alert_config.monitors, + vec![AppliedMonitor { + monitor_id: monitor.monitor_id, + name: monitor.name.clone() + }] + ); assert!(alert_config.is_associated_with_monitor(&monitor)); alert_config.disassociate_monitor(&monitor).unwrap(); - assert_eq!(alert_config.monitor_ids, vec![]); + assert_eq!(alert_config.monitors, vec![]); assert!(!alert_config.is_associated_with_monitor(&monitor)); } @@ -219,7 +255,10 @@ mod tests { channel: "test-channel".to_string(), token: "test-token".to_string(), }), - monitor_ids: vec![gen_uuid("ba0cd705-4a5b-4635-9def-611b1143e4aa")], + monitors: vec![AppliedMonitor { + monitor_id: gen_uuid("ba0cd705-4a5b-4635-9def-611b1143e4aa"), + name: "test-name".to_string(), + }], }; let result = alert_config.associate_monitor(&monitor); @@ -255,7 +294,7 @@ mod tests { channel: "test-channel".to_string(), token: "test-token".to_string(), }), - monitor_ids: vec![], + monitors: vec![], }; let result = alert_config.disassociate_monitor(&monitor); diff --git a/api/src/infrastructure/db_schema.rs b/api/src/infrastructure/db_schema.rs index ef71f5c9..14856d8f 100644 --- a/api/src/infrastructure/db_schema.rs +++ b/api/src/infrastructure/db_schema.rs @@ -62,6 +62,7 @@ diesel::table! { monitor_alert_config (alert_config_id, monitor_id) { alert_config_id -> Uuid, monitor_id -> Uuid, + monitor_name -> Varchar, } } diff --git a/api/src/infrastructure/migrations/2024-12-13-222551_add-name-to-monitor-alert-config/down.sql b/api/src/infrastructure/migrations/2024-12-13-222551_add-name-to-monitor-alert-config/down.sql new file mode 100644 index 00000000..3b6087e9 --- /dev/null +++ b/api/src/infrastructure/migrations/2024-12-13-222551_add-name-to-monitor-alert-config/down.sql @@ -0,0 +1,5 @@ +-- Drop trigger and function +DROP TRIGGER IF EXISTS monitor_name_update ON monitor; +DROP FUNCTION IF EXISTS update_monitor_alert_config_monitor_name; + +ALTER TABLE monitor_alert_config DROP monitor_name; diff --git a/api/src/infrastructure/migrations/2024-12-13-222551_add-name-to-monitor-alert-config/up.sql b/api/src/infrastructure/migrations/2024-12-13-222551_add-name-to-monitor-alert-config/up.sql new file mode 100644 index 00000000..ac533151 --- /dev/null +++ b/api/src/infrastructure/migrations/2024-12-13-222551_add-name-to-monitor-alert-config/up.sql @@ -0,0 +1,30 @@ +-- Add name to monitor_alert_config, nullable for now. +ALTER TABLE monitor_alert_config + ADD monitor_name VARCHAR NULL; + +-- Set the names to what they are currently, then make the column non-nullable. +UPDATE monitor_alert_config + SET monitor_name = monitor.name +FROM monitor +WHERE monitor_alert_config.monitor_id = monitor.monitor_id; + +ALTER TABLE monitor_alert_config + ALTER COLUMN monitor_name SET NOT NULL; + +-- Create a trigger function for updating the monitor name in monitor_alert_config. +CREATE OR REPLACE FUNCTION update_monitor_alert_config_monitor_name() +RETURNS TRIGGER AS $$ +BEGIN + UPDATE monitor_alert_config + SET monitor_name = NEW.name + WHERE monitor_id = NEW.monitor_id; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Apply the trigger to the monitor table so that when name changes, the changes are +-- reflected in the monitor_alert_config table. +CREATE TRIGGER monitor_name_update +AFTER UPDATE OF name ON monitor +FOR EACH ROW +EXECUTE FUNCTION update_monitor_alert_config_monitor_name(); diff --git a/api/src/infrastructure/models/alert_config.rs b/api/src/infrastructure/models/alert_config.rs index b2e3866c..67c376d8 100644 --- a/api/src/infrastructure/models/alert_config.rs +++ b/api/src/infrastructure/models/alert_config.rs @@ -1,7 +1,9 @@ use diesel::prelude::*; use uuid::Uuid; -use crate::domain::models::alert_config::{AlertConfig, AlertType, SlackAlertConfig}; +use crate::domain::models::alert_config::{ + AlertConfig, AlertType, AppliedMonitor, SlackAlertConfig, +}; use crate::errors::Error; use crate::infrastructure::db_schema::{alert_config, monitor_alert_config, slack_alert_config}; @@ -31,6 +33,10 @@ pub struct AlertConfigData { pub struct MonitorAlertConfigData { pub alert_config_id: Uuid, pub monitor_id: Uuid, + // Note that this column will be kept up to date with the `name` of the corresponding `monitor` + // record by the `monitor_name_update` trigger, added in the + // `2024-12-13-222551_add-name-to-monitor-alert-config` migration. + pub monitor_name: String, } // Only used for writing data. @@ -89,9 +95,12 @@ impl AlertConfigData { } _ => return Err(Error::InvalidAlertConfig("Unknown alert type".to_owned())), }, - monitor_ids: monitor_alert_configs + monitors: monitor_alert_configs .iter() - .map(|mac| mac.monitor_id) + .map(|mac| AppliedMonitor { + monitor_id: mac.monitor_id, + name: mac.monitor_name.clone(), + }) .collect(), }) } @@ -127,11 +136,12 @@ impl NewAlertConfigData { on_error: alert_config.on_error, }, alert_config - .monitor_ids + .monitors .iter() - .map(|monitor_id| MonitorAlertConfigData { + .map(|monitor| MonitorAlertConfigData { alert_config_id: alert_config.alert_config_id, - monitor_id: *monitor_id, + monitor_id: monitor.monitor_id, + monitor_name: monitor.name.clone(), }) .collect(), specific_data, @@ -154,10 +164,12 @@ mod tests { MonitorAlertConfigData { alert_config_id: gen_uuid("41ebffb4-a188-48e9-8ec1-61380085cde3"), monitor_id: gen_uuid("02d9fd94-48dc-40e5-b2fa-fa6b66eaf2ca"), + monitor_name: "foo-monitor".to_string(), }, MonitorAlertConfigData { alert_config_id: gen_uuid("41ebffb4-a188-48e9-8ec1-61380085cde3"), monitor_id: gen_uuid("70810d10-1d86-4bde-b29d-b1f490528675"), + monitor_name: "bar-monitor".to_string(), }, ]; let alert_config_data = AlertConfigData { @@ -191,10 +203,16 @@ mod tests { }) ); assert_eq!( - alert_config.monitor_ids, + alert_config.monitors, vec![ - gen_uuid("02d9fd94-48dc-40e5-b2fa-fa6b66eaf2ca"), - gen_uuid("70810d10-1d86-4bde-b29d-b1f490528675") + AppliedMonitor { + monitor_id: gen_uuid("02d9fd94-48dc-40e5-b2fa-fa6b66eaf2ca"), + name: "foo-monitor".to_string() + }, + AppliedMonitor { + monitor_id: gen_uuid("70810d10-1d86-4bde-b29d-b1f490528675"), + name: "bar-monitor".to_string() + } ] ); } @@ -258,9 +276,15 @@ mod tests { channel: "test-channel".to_owned(), token: "test-token".to_owned(), }), - monitor_ids: vec![ - gen_uuid("02d9fd94-48dc-40e5-b2fa-fa6b66eaf2ca"), - gen_uuid("70810d10-1d86-4bde-b29d-b1f490528675"), + monitors: vec![ + AppliedMonitor { + monitor_id: gen_uuid("02d9fd94-48dc-40e5-b2fa-fa6b66eaf2ca"), + name: "foo-monitor".to_string(), + }, + AppliedMonitor { + monitor_id: gen_uuid("70810d10-1d86-4bde-b29d-b1f490528675"), + name: "bar-monitor".to_string(), + }, ], }; diff --git a/api/src/infrastructure/seeding/seeds.sql b/api/src/infrastructure/seeding/seeds.sql index c4ae3881..2491b86f 100644 --- a/api/src/infrastructure/seeding/seeds.sql +++ b/api/src/infrastructure/seeding/seeds.sql @@ -264,7 +264,7 @@ VALUES -- Monitor alert configs. INSERT INTO monitor_alert_config - (alert_config_id, monitor_id) + (alert_config_id, monitor_id, monitor_name) VALUES - ('f1b1b1b1-1b1b-1b1b-1b1b-1b1b1b1b1b1b', 'c1bf0515-df39-448b-aa95-686360a33b36'), - ('f1b1b1b1-1b1b-1b1b-1b1b-1b1b1b1b1b1b', 'f0b291fe-bd41-4787-bc2d-1329903f7a6a'); + ('f1b1b1b1-1b1b-1b1b-1b1b-1b1b1b1b1b1b', 'c1bf0515-df39-448b-aa95-686360a33b36', 'db-backup.py'), + ('f1b1b1b1-1b1b-1b1b-1b1b-1b1b1b1b1b1b', 'f0b291fe-bd41-4787-bc2d-1329903f7a6a', 'generate-orders.sh'); diff --git a/api/tests/alert_config_repo_test.rs b/api/tests/alert_config_repo_test.rs index 58fd9a05..762da380 100644 --- a/api/tests/alert_config_repo_test.rs +++ b/api/tests/alert_config_repo_test.rs @@ -5,7 +5,9 @@ use rstest::rstest; use test_utils::gen_uuid; -use cron_mon_api::domain::models::alert_config::{AlertConfig, AlertType, SlackAlertConfig}; +use cron_mon_api::domain::models::alert_config::{ + AlertConfig, AlertType, AppliedMonitor, SlackAlertConfig, +}; use cron_mon_api::errors::Error; use cron_mon_api::infrastructure::models::alert_config::NewAlertConfigData; use cron_mon_api::infrastructure::repositories::alert_config_repo::AlertConfigRepository; @@ -124,8 +126,11 @@ async fn test_get(#[future] infrastructure: Infrastructure) { let alert_config = should_be_some.unwrap(); assert_eq!(alert_config.name, "Test Slack alert (for lates)"); assert_eq!( - alert_config.monitor_ids, - vec![gen_uuid("c1bf0515-df39-448b-aa95-686360a33b36")] + alert_config.monitors, + vec![AppliedMonitor { + monitor_id: gen_uuid("c1bf0515-df39-448b-aa95-686360a33b36"), + name: "db-backup.py".to_string() + }] ) } @@ -144,9 +149,15 @@ async fn test_save_with_new(#[future] infrastructure: Infrastructure) { "#new-channel".to_string(), "new-test-token".to_string(), ); - new_alert_config.monitor_ids = vec![ - gen_uuid("c1bf0515-df39-448b-aa95-686360a33b36"), - gen_uuid("f0b291fe-bd41-4787-bc2d-1329903f7a6a"), + new_alert_config.monitors = vec![ + AppliedMonitor { + monitor_id: gen_uuid("c1bf0515-df39-448b-aa95-686360a33b36"), + name: "db-backup.py".to_string(), + }, + AppliedMonitor { + monitor_id: gen_uuid("f0b291fe-bd41-4787-bc2d-1329903f7a6a"), + name: "generate-orders.sh".to_string(), + }, ]; repo.save(&new_alert_config).await.unwrap(); @@ -166,10 +177,7 @@ async fn test_save_with_new(#[future] infrastructure: Infrastructure) { assert_eq!(new_alert_config.on_late, read_new_alert_config.on_late); assert_eq!(new_alert_config.on_error, read_new_alert_config.on_error); assert_eq!(new_alert_config.type_, read_new_alert_config.type_); - assert_eq!( - new_alert_config.monitor_ids, - read_new_alert_config.monitor_ids - ); + assert_eq!(new_alert_config.monitors, read_new_alert_config.monitors); } #[rstest] @@ -187,9 +195,15 @@ async fn test_save_with_existing(#[future] infrastructure: Infrastructure) { alert_config.active = false; alert_config.on_late = false; alert_config.on_error = false; - alert_config.monitor_ids = vec![ - gen_uuid("f0b291fe-bd41-4787-bc2d-1329903f7a6a"), - gen_uuid("cc6cf74e-b25d-4c8c-94a6-914e3f139c14"), + alert_config.monitors = vec![ + AppliedMonitor { + monitor_id: gen_uuid("f0b291fe-bd41-4787-bc2d-1329903f7a6a"), + name: "generate-orders.sh".to_string(), + }, + AppliedMonitor { + monitor_id: gen_uuid("cc6cf74e-b25d-4c8c-94a6-914e3f139c14"), + name: "data-snapshot.py".to_string(), + }, ]; repo.save(&alert_config).await.unwrap(); @@ -206,7 +220,7 @@ async fn test_save_with_existing(#[future] infrastructure: Infrastructure) { assert_eq!(alert_config.on_late, read_alert_config.on_late); assert_eq!(alert_config.on_error, read_alert_config.on_error); assert_eq!(alert_config.type_, read_alert_config.type_); - assert_eq!(alert_config.monitor_ids, read_alert_config.monitor_ids); + assert_eq!(alert_config.monitors, read_alert_config.monitors); } #[rstest] diff --git a/api/tests/common/seeds.rs b/api/tests/common/seeds.rs index 155c8a54..1913ab7a 100644 --- a/api/tests/common/seeds.rs +++ b/api/tests/common/seeds.rs @@ -193,22 +193,27 @@ pub fn alert_config_seeds() -> ( MonitorAlertConfigData { monitor_id: gen_uuid("c1bf0515-df39-448b-aa95-686360a33b36"), alert_config_id: gen_uuid("fadd7266-648b-4102-8f85-c768655f4297"), + monitor_name: "db-backup.py".to_string(), }, MonitorAlertConfigData { monitor_id: gen_uuid("f0b291fe-bd41-4787-bc2d-1329903f7a6a"), alert_config_id: gen_uuid("3ba21f52-32c9-41dc-924d-d18d4fc0e81c"), + monitor_name: "generate-orders.sh".to_string(), }, MonitorAlertConfigData { monitor_id: gen_uuid("c1bf0515-df39-448b-aa95-686360a33b36"), alert_config_id: gen_uuid("3ba21f52-32c9-41dc-924d-d18d4fc0e81c"), + monitor_name: "db-backup.py".to_string(), }, MonitorAlertConfigData { monitor_id: gen_uuid("cc6cf74e-b25d-4c8c-94a6-914e3f139c14"), alert_config_id: gen_uuid("3ba21f52-32c9-41dc-924d-d18d4fc0e81c"), + monitor_name: "data-snapshot.py".to_string(), }, MonitorAlertConfigData { monitor_id: gen_uuid("c1bf0515-df39-448b-aa95-686360a33b36"), alert_config_id: gen_uuid("8d307d12-4696-4801-bfb6-628f8f640864"), + monitor_name: "db-backup.py".to_string(), }, ], )