From b5863c0104903b19fca071a6efea7e07f5913311 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Wed, 26 Apr 2023 17:53:46 +0100 Subject: [PATCH] feat(core/database): implement user settings model --- crates/core/database/src/drivers/reference.rs | 14 +++- crates/core/database/src/models/mod.rs | 3 + .../database/src/models/user_settings/mod.rs | 5 ++ .../src/models/user_settings/model.rs | 28 ++++++++ .../database/src/models/user_settings/ops.rs | 18 +++++ .../src/models/user_settings/ops/mongodb.rs | 67 +++++++++++++++++++ .../src/models/user_settings/ops/reference.rs | 46 +++++++++++++ 7 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 crates/core/database/src/models/user_settings/mod.rs create mode 100644 crates/core/database/src/models/user_settings/model.rs create mode 100644 crates/core/database/src/models/user_settings/ops.rs create mode 100644 crates/core/database/src/models/user_settings/ops/mongodb.rs create mode 100644 crates/core/database/src/models/user_settings/ops/reference.rs diff --git a/crates/core/database/src/drivers/reference.rs b/crates/core/database/src/drivers/reference.rs index f59dda16..4749dc44 100644 --- a/crates/core/database/src/drivers/reference.rs +++ b/crates/core/database/src/drivers/reference.rs @@ -2,14 +2,26 @@ use std::{collections::HashMap, sync::Arc}; use futures::lock::Mutex; -use crate::{Bot, File, User}; +use crate::{Bot, File, User, UserSettings}; database_derived!( /// Reference implementation #[derive(Default)] pub struct ReferenceDb { pub bots: Arc>>, + pub user_settings: Arc>>, pub users: Arc>>, pub files: Arc>>, + + pub servers: Arc>>, + pub server_bans: Arc>>, + pub server_members: Arc>>, + pub safety_reports: Arc>>, + pub safety_snapshots: Arc>>, + pub emoji: Arc>>, + pub messages: Arc>>, + pub channels: Arc>>, + pub channel_invites: Arc>>, + pub channel_unreads: Arc>>, } ); diff --git a/crates/core/database/src/models/mod.rs b/crates/core/database/src/models/mod.rs index 98fcb952..67c8e35d 100644 --- a/crates/core/database/src/models/mod.rs +++ b/crates/core/database/src/models/mod.rs @@ -1,11 +1,13 @@ mod admin_migrations; mod bots; mod files; +mod user_settings; mod users; pub use admin_migrations::*; pub use bots::*; pub use files::*; +pub use user_settings::*; pub use users::*; use crate::{Database, MongoDb, ReferenceDb}; @@ -16,6 +18,7 @@ pub trait AbstractDatabase: + admin_migrations::AbstractMigrations + bots::AbstractBots + files::AbstractAttachments + + user_settings::AbstractUserSettings + users::AbstractUsers { } diff --git a/crates/core/database/src/models/user_settings/mod.rs b/crates/core/database/src/models/user_settings/mod.rs new file mode 100644 index 00000000..4d801b73 --- /dev/null +++ b/crates/core/database/src/models/user_settings/mod.rs @@ -0,0 +1,5 @@ +mod model; +mod ops; + +pub use model::*; +pub use ops::*; diff --git a/crates/core/database/src/models/user_settings/model.rs b/crates/core/database/src/models/user_settings/model.rs new file mode 100644 index 00000000..1780f6a2 --- /dev/null +++ b/crates/core/database/src/models/user_settings/model.rs @@ -0,0 +1,28 @@ +use std::collections::HashMap; + +use crate::Database; + +use revolt_result::Result; + +pub type UserSettings = HashMap; + +#[async_trait] +pub trait UserSettingsImpl { + async fn set(self, db: &Database, user: &str) -> Result<()>; +} + +#[async_trait] +impl UserSettingsImpl for UserSettings { + async fn set(self, db: &Database, user: &str) -> Result<()> { + db.set_user_settings(user, &self).await?; + + /* // TODO: EventV1::UserSettingsUpdate { + id: user.to_string(), + update: self, + } + .private(user.to_string()) + .await; */ + + Ok(()) + } +} diff --git a/crates/core/database/src/models/user_settings/ops.rs b/crates/core/database/src/models/user_settings/ops.rs new file mode 100644 index 00000000..2dd6bb69 --- /dev/null +++ b/crates/core/database/src/models/user_settings/ops.rs @@ -0,0 +1,18 @@ +use revolt_result::Result; + +use crate::UserSettings; + +mod mongodb; +mod reference; + +#[async_trait] +pub trait AbstractUserSettings: Sync + Send { + /// Fetch a subset of user settings + async fn fetch_user_settings(&'_ self, id: &str, filter: &'_ [String]) -> Result; + + /// Update a subset of user settings + async fn set_user_settings(&self, id: &str, settings: &UserSettings) -> Result<()>; + + /// Delete all user settings + async fn delete_user_settings(&self, id: &str) -> Result<()>; +} diff --git a/crates/core/database/src/models/user_settings/ops/mongodb.rs b/crates/core/database/src/models/user_settings/ops/mongodb.rs new file mode 100644 index 00000000..5de98832 --- /dev/null +++ b/crates/core/database/src/models/user_settings/ops/mongodb.rs @@ -0,0 +1,67 @@ +use ::mongodb::options::FindOneOptions; +use bson::to_bson; +use bson::Document; +use mongodb::options::UpdateOptions; +use revolt_result::Result; + +use crate::MongoDb; +use crate::UserSettings; + +use super::AbstractUserSettings; + +static COL: &str = "user_settings"; + +#[async_trait] +impl AbstractUserSettings for MongoDb { + /// Fetch a subset of user settings + async fn fetch_user_settings(&'_ self, id: &str, filter: &'_ [String]) -> Result { + let mut projection = doc! { + "_id": 0, + }; + + for key in filter { + projection.insert(key, 1); + } + + Ok(query!( + self, + find_one_with_options, + COL, + doc! { + "_id": id + }, + FindOneOptions::builder().projection(projection).build() + )? + .unwrap_or_default()) + } + + /// Update a subset of user settings + async fn set_user_settings(&self, id: &str, settings: &UserSettings) -> Result<()> { + let mut set = doc! {}; + for (key, data) in settings { + set.insert( + key, + vec![to_bson(&data.0).unwrap(), to_bson(&data.1).unwrap()], + ); + } + + self.col::(COL) + .update_one( + doc! { + "_id": id + }, + doc! { + "$set": set + }, + UpdateOptions::builder().upsert(true).build(), + ) + .await + .map(|_| ()) + .map_err(|_| create_database_error!("update_one", "user_settings")) + } + + /// Delete all user settings + async fn delete_user_settings(&self, id: &str) -> Result<()> { + query!(self, delete_one_by_id, COL, id).map(|_| ()) + } +} diff --git a/crates/core/database/src/models/user_settings/ops/reference.rs b/crates/core/database/src/models/user_settings/ops/reference.rs new file mode 100644 index 00000000..17d8445b --- /dev/null +++ b/crates/core/database/src/models/user_settings/ops/reference.rs @@ -0,0 +1,46 @@ +use revolt_result::Result; + +use crate::ReferenceDb; +use crate::UserSettings; + +use super::AbstractUserSettings; + +#[async_trait] +impl AbstractUserSettings for ReferenceDb { + /// Fetch a subset of user settings + async fn fetch_user_settings(&'_ self, id: &str, filter: &'_ [String]) -> Result { + let user_settings = self.user_settings.lock().await; + user_settings + .get(id) + .cloned() + .map(|settings| { + settings + .into_iter() + .filter(|(key, _)| filter.contains(key)) + .collect() + }) + .ok_or_else(|| create_error!(NotFound)) + } + + /// Update a subset of user settings + async fn set_user_settings(&self, id: &str, settings: &UserSettings) -> Result<()> { + let mut user_settings = self.user_settings.lock().await; + if let Some(settings) = user_settings.get_mut(id) { + settings.extend(settings.clone()); + } else { + user_settings.insert(id.to_string(), settings.clone()); + } + + Ok(()) + } + + /// Delete all user settings + async fn delete_user_settings(&self, id: &str) -> Result<()> { + let mut user_settings = self.user_settings.lock().await; + if user_settings.remove(id).is_some() { + Ok(()) + } else { + Err(create_error!(NotFound)) + } + } +}