From c0892ce183c3aa1478005a12f42593ee46746695 Mon Sep 17 00:00:00 2001 From: noel-yeldos Date: Mon, 10 Mar 2025 14:48:34 +1300 Subject: [PATCH 1/8] Update changelog tablename --- server/repository/src/db_diesel/changelog/changelog.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/repository/src/db_diesel/changelog/changelog.rs b/server/repository/src/db_diesel/changelog/changelog.rs index e4923bbe82..6f7299f464 100644 --- a/server/repository/src/db_diesel/changelog/changelog.rs +++ b/server/repository/src/db_diesel/changelog/changelog.rs @@ -121,11 +121,12 @@ pub enum ChangelogTableName { Report, FormSchema, PluginData, + Preference, } pub(crate) enum ChangeLogSyncStyle { - Legacy, - Central, + Legacy, // Everything that goes to Legacy mSupply server + Central, // Data created on Open-mSupply central server Remote, File, RemoteAndCentral, // These records will sync like remote record if store_id exist, otherwise they will sync like central records @@ -197,6 +198,7 @@ impl ChangelogTableName { ChangelogTableName::Report => ChangeLogSyncStyle::Central, ChangelogTableName::FormSchema => ChangeLogSyncStyle::Central, ChangelogTableName::PluginData => ChangeLogSyncStyle::RemoteAndCentral, + ChangelogTableName::Preference => ChangeLogSyncStyle::Central, } } } From 836411a73656feb4215f2a4fb13d0cb7de6c9ce6 Mon Sep 17 00:00:00 2001 From: noel-yeldos Date: Mon, 10 Mar 2025 17:15:05 +1300 Subject: [PATCH 2/8] Add repo layer for prefs --- server/repository/src/db_diesel/mod.rs | 4 + server/repository/src/db_diesel/preference.rs | 101 +++++++++++++++++ .../src/db_diesel/preference_row.rs | 105 ++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 server/repository/src/db_diesel/preference.rs create mode 100644 server/repository/src/db_diesel/preference_row.rs diff --git a/server/repository/src/db_diesel/mod.rs b/server/repository/src/db_diesel/mod.rs index 1c2099bf87..8f2d8a4e2b 100644 --- a/server/repository/src/db_diesel/mod.rs +++ b/server/repository/src/db_diesel/mod.rs @@ -94,6 +94,8 @@ mod patient; pub mod period; pub mod plugin_data; mod plugin_data_row; +pub mod preference; +mod preference_row; pub mod printer; pub mod printer_row; pub mod program_enrolment; @@ -225,6 +227,8 @@ pub use patient::*; pub use period::*; pub use plugin_data::*; pub use plugin_data_row::*; +pub use preference::*; +pub use preference_row::*; pub use printer_row::*; pub use program_enrolment::*; pub use program_enrolment_row::*; diff --git a/server/repository/src/db_diesel/preference.rs b/server/repository/src/db_diesel/preference.rs new file mode 100644 index 0000000000..47b9c412a8 --- /dev/null +++ b/server/repository/src/db_diesel/preference.rs @@ -0,0 +1,101 @@ +use diesel::{ + dsl::{InnerJoin, IntoBoxed}, + prelude::*, +}; + +use crate::diesel_macros::apply_equal_filter; + +use super::{ + preference_row::{preference, PreferenceRow}, + store_row::store, + DBType, EqualFilter, Pagination, RepositoryError, StorageConnection, StoreRow, +}; + +#[derive(PartialEq, Debug, Clone, Default)] +pub struct Preference { + pub preference_row: PreferenceRow, + pub store_row: StoreRow, +} + +#[derive(Clone, Default)] +pub struct PreferenceFilter { + pub id: Option>, +} + +impl PreferenceFilter { + pub fn new() -> PreferenceFilter { + PreferenceFilter::default() + } + + pub fn id(mut self, filter: EqualFilter) -> Self { + self.id = Some(filter); + self + } +} + +pub type PreferenceJoin = (PreferenceRow, StoreRow); + +pub struct PreferenceRepository<'a> { + connection: &'a StorageConnection, +} + +impl<'a> PreferenceRepository<'a> { + pub fn new(connection: &'a StorageConnection) -> Self { + PreferenceRepository { connection } + } + + pub fn query_one( + &self, + filter: PreferenceFilter, + ) -> Result, RepositoryError> { + Ok(self.query_by_filter(filter)?.pop()) + } + + pub fn query_by_filter( + &self, + filter: PreferenceFilter, + ) -> Result, RepositoryError> { + self.query(Pagination::new(), Some(filter)) + } + + pub fn query( + &self, + pagination: Pagination, + filter: Option, + ) -> Result, RepositoryError> { + let query = create_filtered_query(filter); + + let final_query = query + .offset(pagination.offset as i64) + .limit(pagination.limit as i64); + + // Debug diesel query + //println!( + // "{}", + // diesel::debug_query::(&final_query).to_string() + //); + let result = final_query + .load::(self.connection.lock().connection())? + .into_iter() + .map(|(preference_row, store_row)| Preference { + preference_row, + store_row, + }) + .collect(); + + Ok(result) + } +} + +type BoxedPreferenceQuery = IntoBoxed<'static, InnerJoin, DBType>; + +fn create_filtered_query(filter: Option) -> BoxedPreferenceQuery { + let mut query = preference::table.inner_join(store::table).into_boxed(); + + if let Some(f) = filter { + let PreferenceFilter { id } = f; + + apply_equal_filter!(query, id, preference::id); + } + query +} diff --git a/server/repository/src/db_diesel/preference_row.rs b/server/repository/src/db_diesel/preference_row.rs new file mode 100644 index 0000000000..042d4dc7ed --- /dev/null +++ b/server/repository/src/db_diesel/preference_row.rs @@ -0,0 +1,105 @@ +use crate::{ + ChangeLogInsertRow, ChangelogRepository, ChangelogTableName, RepositoryError, RowActionType, + StorageConnection, Upsert, +}; + +use super::{preference_row::preference::dsl::*, store_row::store}; +use serde::{Deserialize, Serialize}; + +use diesel::prelude::*; + +table! { + preference (id) { + id -> Text, + key -> Text, + value -> Text, + store_id -> Text, + } +} + +joinable!(preference -> store (store_id)); + +allow_tables_to_appear_in_same_query!(preference, store); + +#[derive( + Clone, Insertable, Queryable, Debug, PartialEq, AsChangeset, Eq, Serialize, Deserialize, Default, +)] +#[diesel(table_name = preference)] +pub struct PreferenceRow { + pub id: String, + pub key: String, + pub value: String, + pub store_id: String, +} + +pub struct PreferenceRowRepository<'a> { + connection: &'a StorageConnection, +} + +impl<'a> PreferenceRowRepository<'a> { + pub fn new(connection: &'a StorageConnection) -> Self { + PreferenceRowRepository { connection } + } + + pub fn _upsert_one(&self, preference_row: &PreferenceRow) -> Result<(), RepositoryError> { + diesel::insert_into(preference::table) + .values(preference_row) + .on_conflict(id) + .do_update() + .set(preference_row) + .execute(self.connection.lock().connection())?; + Ok(()) + } + + pub fn upsert_one(&self, preference_row: &PreferenceRow) -> Result { + self._upsert_one(preference_row)?; + self.insert_changelog(preference_row.to_owned(), RowActionType::Upsert) + } + + fn insert_changelog( + &self, + row: PreferenceRow, + action: RowActionType, + ) -> Result { + let row = ChangeLogInsertRow { + table_name: ChangelogTableName::Preference, + record_id: row.id, + row_action: action, + store_id: Some(row.store_id), + name_link_id: None, + }; + ChangelogRepository::new(self.connection).insert(&row) + } + + pub fn find_one_by_key( + &self, + preference_key: &str, + ) -> Result, RepositoryError> { + let result = preference + .filter(key.eq(preference_key)) + .first(self.connection.lock().connection()) + .optional()?; + Ok(result) + } + + pub fn delete(&self, preference_id: &str) -> Result<(), RepositoryError> { + diesel::delete(preference.filter(preference::id.eq(preference_id))) + .execute(self.connection.lock().connection())?; + Ok(()) + } +} + +impl Upsert for PreferenceRow { + fn upsert(&self, con: &StorageConnection) -> Result, RepositoryError> { + let cursor_id = PreferenceRowRepository::new(con).upsert_one(self)?; + Ok(Some(cursor_id)) + } + + // Test only + fn assert_upserted(&self, con: &StorageConnection) { + assert_eq!( + PreferenceRowRepository::new(con).find_one_by_key(&self.key), + Ok(Some(self.clone())) + ) + } +} From fdcd7d07337c3fa5b0a11250fb38d386eb5cc119 Mon Sep 17 00:00:00 2001 From: noel-yeldos Date: Tue, 11 Mar 2025 15:26:40 +1300 Subject: [PATCH 3/8] Update store id to be nullable --- server/repository/src/db_diesel/preference_row.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/repository/src/db_diesel/preference_row.rs b/server/repository/src/db_diesel/preference_row.rs index 042d4dc7ed..9a1ab0c0bf 100644 --- a/server/repository/src/db_diesel/preference_row.rs +++ b/server/repository/src/db_diesel/preference_row.rs @@ -13,7 +13,7 @@ table! { id -> Text, key -> Text, value -> Text, - store_id -> Text, + store_id -> Nullable, } } @@ -29,7 +29,7 @@ pub struct PreferenceRow { pub id: String, pub key: String, pub value: String, - pub store_id: String, + pub store_id: Option, } pub struct PreferenceRowRepository<'a> { From e82119a0a24b0c803bf0b29f13711c68e8b6b1d7 Mon Sep 17 00:00:00 2001 From: noel-yeldos Date: Tue, 11 Mar 2025 15:45:33 +1300 Subject: [PATCH 4/8] fix type mismatch --- server/repository/src/db_diesel/preference_row.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/repository/src/db_diesel/preference_row.rs b/server/repository/src/db_diesel/preference_row.rs index 9a1ab0c0bf..34b7e8bf0c 100644 --- a/server/repository/src/db_diesel/preference_row.rs +++ b/server/repository/src/db_diesel/preference_row.rs @@ -65,7 +65,7 @@ impl<'a> PreferenceRowRepository<'a> { table_name: ChangelogTableName::Preference, record_id: row.id, row_action: action, - store_id: Some(row.store_id), + store_id: row.store_id.clone(), name_link_id: None, }; ChangelogRepository::new(self.connection).insert(&row) From 266b3c8d9d1c13d9600d7d4035d29a7e0ee40a94 Mon Sep 17 00:00:00 2001 From: noel-yeldos Date: Tue, 11 Mar 2025 16:06:19 +1300 Subject: [PATCH 5/8] Refactor upsert_one function --- server/repository/src/db_diesel/preference_row.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/server/repository/src/db_diesel/preference_row.rs b/server/repository/src/db_diesel/preference_row.rs index 34b7e8bf0c..70d60365d3 100644 --- a/server/repository/src/db_diesel/preference_row.rs +++ b/server/repository/src/db_diesel/preference_row.rs @@ -41,18 +41,14 @@ impl<'a> PreferenceRowRepository<'a> { PreferenceRowRepository { connection } } - pub fn _upsert_one(&self, preference_row: &PreferenceRow) -> Result<(), RepositoryError> { + fn upsert_one(&self, preference_row: &PreferenceRow) -> Result { diesel::insert_into(preference::table) .values(preference_row) .on_conflict(id) .do_update() .set(preference_row) .execute(self.connection.lock().connection())?; - Ok(()) - } - pub fn upsert_one(&self, preference_row: &PreferenceRow) -> Result { - self._upsert_one(preference_row)?; self.insert_changelog(preference_row.to_owned(), RowActionType::Upsert) } From ddfd65ec1bd8c881f9c7b0b9cd008d28006d14d7 Mon Sep 17 00:00:00 2001 From: noel-yeldos Date: Tue, 11 Mar 2025 16:45:23 +1300 Subject: [PATCH 6/8] Add find_one_by_id function --- server/repository/src/db_diesel/preference_row.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/server/repository/src/db_diesel/preference_row.rs b/server/repository/src/db_diesel/preference_row.rs index 70d60365d3..435c47b12c 100644 --- a/server/repository/src/db_diesel/preference_row.rs +++ b/server/repository/src/db_diesel/preference_row.rs @@ -78,6 +78,17 @@ impl<'a> PreferenceRowRepository<'a> { Ok(result) } + pub fn find_one_by_id( + &self, + preference_id: &str, + ) -> Result, RepositoryError> { + let result = preference::table + .filter(preference::id.eq(preference_id)) + .first(self.connection.lock().connection()) + .optional()?; + Ok(result) + } + pub fn delete(&self, preference_id: &str) -> Result<(), RepositoryError> { diesel::delete(preference.filter(preference::id.eq(preference_id))) .execute(self.connection.lock().connection())?; From 01634a0ec20b1e13b1b7d9ced6a4e331df217e5b Mon Sep 17 00:00:00 2001 From: noel-yeldos Date: Tue, 11 Mar 2025 17:04:37 +1300 Subject: [PATCH 7/8] Update changelog on delete --- server/repository/src/db_diesel/preference_row.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/repository/src/db_diesel/preference_row.rs b/server/repository/src/db_diesel/preference_row.rs index 435c47b12c..f8a16a87c8 100644 --- a/server/repository/src/db_diesel/preference_row.rs +++ b/server/repository/src/db_diesel/preference_row.rs @@ -89,10 +89,18 @@ impl<'a> PreferenceRowRepository<'a> { Ok(result) } - pub fn delete(&self, preference_id: &str) -> Result<(), RepositoryError> { + pub fn delete(&self, preference_id: &str) -> Result, RepositoryError> { + let old_row = self.find_one_by_id(preference_id)?; + let change_log_id = match old_row { + Some(old_row) => self.insert_changelog(old_row, RowActionType::Delete)?, + None => { + return Ok(None); + } + }; + diesel::delete(preference.filter(preference::id.eq(preference_id))) .execute(self.connection.lock().connection())?; - Ok(()) + Ok(Some(change_log_id)) } } From 5bccdaa25a2feca9e1f0088471aa44806fccc4ae Mon Sep 17 00:00:00 2001 From: noel-yeldos Date: Tue, 11 Mar 2025 17:17:53 +1300 Subject: [PATCH 8/8] Remove storeRow from Preference sruct --- server/repository/src/db_diesel/preference.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/server/repository/src/db_diesel/preference.rs b/server/repository/src/db_diesel/preference.rs index 47b9c412a8..8093b63c32 100644 --- a/server/repository/src/db_diesel/preference.rs +++ b/server/repository/src/db_diesel/preference.rs @@ -14,7 +14,6 @@ use super::{ #[derive(PartialEq, Debug, Clone, Default)] pub struct Preference { pub preference_row: PreferenceRow, - pub store_row: StoreRow, } #[derive(Clone, Default)] @@ -77,10 +76,7 @@ impl<'a> PreferenceRepository<'a> { let result = final_query .load::(self.connection.lock().connection())? .into_iter() - .map(|(preference_row, store_row)| Preference { - preference_row, - store_row, - }) + .map(|(preference_row, _store_row)| Preference { preference_row }) .collect(); Ok(result)