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, } } } 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..8093b63c32 --- /dev/null +++ b/server/repository/src/db_diesel/preference.rs @@ -0,0 +1,97 @@ +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, +} + +#[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 }) + .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..f8a16a87c8 --- /dev/null +++ b/server/repository/src/db_diesel/preference_row.rs @@ -0,0 +1,120 @@ +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 -> Nullable, + } +} + +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: Option, +} + +pub struct PreferenceRowRepository<'a> { + connection: &'a StorageConnection, +} + +impl<'a> PreferenceRowRepository<'a> { + pub fn new(connection: &'a StorageConnection) -> Self { + PreferenceRowRepository { connection } + } + + 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())?; + + 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: row.store_id.clone(), + 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 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> { + 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(Some(change_log_id)) + } +} + +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())) + ) + } +}