From dd850766fd2f2f29ec4b9ed11a0978bcf5161241 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Wed, 24 Apr 2024 01:29:47 +0200
Subject: [PATCH] Add notification to controllers on schedule change

---
 Cargo.lock                                    | 12 ++++++-
 emgauwa-core/Cargo.toml                       |  1 +
 emgauwa-core/src/handlers/v1/schedules.rs     | 32 +++++++++++++++++--
 emgauwa-lib/src/db/junction_relay_schedule.rs | 17 ++++++++++
 4 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 8b6ca2a..013c284 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -729,6 +729,7 @@ dependencies = [
  "chrono",
  "emgauwa-lib",
  "futures",
+ "itertools 0.12.1",
  "log",
  "serde",
  "serde_derive",
@@ -1181,6 +1182,15 @@ dependencies = [
  "either",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.9"
@@ -1998,7 +2008,7 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6b7b278788e7be4d0d29c0f39497a0eef3fba6bbc8e70d8bf7fde46edeaa9e85"
 dependencies = [
- "itertools",
+ "itertools 0.11.0",
  "nom",
  "unicode_categories",
 ]
diff --git a/emgauwa-core/Cargo.toml b/emgauwa-core/Cargo.toml
index 1bb4ef4..a3dd89a 100644
--- a/emgauwa-core/Cargo.toml
+++ b/emgauwa-core/Cargo.toml
@@ -19,6 +19,7 @@ log = "0.4"
 
 chrono = { version = "0.4", features = ["serde"] }
 uuid = { version = "1.5", features = ["serde", "v4"] }
+itertools = "0.12"
 
 serde = "1.0"
 serde_json = "1.0"
diff --git a/emgauwa-core/src/handlers/v1/schedules.rs b/emgauwa-core/src/handlers/v1/schedules.rs
index 1130e6b..21bef5a 100644
--- a/emgauwa-core/src/handlers/v1/schedules.rs
+++ b/emgauwa-core/src/handlers/v1/schedules.rs
@@ -1,11 +1,18 @@
+use actix::Addr;
 use actix_web::{delete, get, post, put, web, HttpResponse};
-use emgauwa_lib::db::{DbSchedule, DbTag};
+use emgauwa_lib::db::{DbController, DbJunctionRelaySchedule, DbSchedule, DbTag};
 use emgauwa_lib::errors::{ApiError, DatabaseError, EmgauwaError};
 use emgauwa_lib::models::{convert_db_list, FromDbModel, Schedule};
-use emgauwa_lib::types::{RequestCreateSchedule, RequestUpdateSchedule, ScheduleUid};
+use emgauwa_lib::types::{
+	ControllerWsAction, RequestCreateSchedule, RequestUpdateSchedule, ScheduleUid,
+};
+use itertools::Itertools;
 use sqlx::pool::PoolConnection;
 use sqlx::{Pool, Sqlite};
 
+use crate::app_state;
+use crate::app_state::AppState;
+
 #[get("/schedules")]
 pub async fn index(pool: web::Data<Pool<Sqlite>>) -> Result<HttpResponse, EmgauwaError> {
 	let mut pool_conn = pool.acquire().await?;
@@ -116,6 +123,7 @@ pub async fn add_list(
 #[put("/schedules/{schedule_id}")]
 pub async fn update(
 	pool: web::Data<Pool<Sqlite>>,
+	app_state: web::Data<Addr<AppState>>,
 	path: web::Path<(String,)>,
 	data: web::Json<RequestUpdateSchedule>,
 ) -> Result<HttpResponse, EmgauwaError> {
@@ -144,6 +152,26 @@ pub async fn update(
 		schedule.set_tags(&mut pool_conn, tags.as_slice()).await?;
 	}
 
+	let controller_ids: Vec<i64> = DbJunctionRelaySchedule::get_relays(&mut pool_conn, &schedule)
+		.await?
+		.into_iter()
+		.map(|r| r.controller_id)
+		.unique()
+		.collect();
+
+	for controller_id in controller_ids {
+		let controller = DbController::get(&mut pool_conn, controller_id)
+			.await?
+			.ok_or(DatabaseError::NotFound)?;
+		app_state
+			.send(app_state::Action {
+				controller_uid: controller.uid,
+				action: ControllerWsAction::Schedules(vec![schedule.clone()]),
+			})
+			.await??;
+	}
+
+
 	let return_schedule = Schedule::from_db_model(&mut pool_conn, schedule)?;
 	Ok(HttpResponse::Ok().json(return_schedule))
 }
diff --git a/emgauwa-lib/src/db/junction_relay_schedule.rs b/emgauwa-lib/src/db/junction_relay_schedule.rs
index 128490b..f081a54 100644
--- a/emgauwa-lib/src/db/junction_relay_schedule.rs
+++ b/emgauwa-lib/src/db/junction_relay_schedule.rs
@@ -45,6 +45,23 @@ impl DbJunctionRelaySchedule {
 		.map_err(DatabaseError::from)
 	}
 
+	pub async fn get_relays(
+		conn: &mut PoolConnection<Sqlite>,
+		schedule: &DbSchedule,
+	) -> Result<Vec<DbRelay>, DatabaseError> {
+		sqlx::query_as!(
+			DbRelay,
+			r#"SELECT relays.* FROM relays INNER JOIN junction_relay_schedule
+			ON junction_relay_schedule.relay_id = relays.id
+			WHERE junction_relay_schedule.schedule_id = ?
+			ORDER BY junction_relay_schedule.weekday"#,
+			schedule.id
+		)
+		.fetch_all(conn.deref_mut())
+		.await
+		.map_err(DatabaseError::from)
+	}
+
 	pub async fn get_schedule(
 		conn: &mut PoolConnection<Sqlite>,
 		relay: &DbRelay,