chore(monorepo): delta, january, quark
This commit is contained in:
68
crates/quark/src/impl/mongo/users/bot.rs
Normal file
68
crates/quark/src/impl/mongo/users/bot.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use crate::models::bot::{Bot, FieldsBot, PartialBot};
|
||||
use crate::r#impl::mongo::IntoDocumentPath;
|
||||
use crate::{AbstractBot, Result};
|
||||
|
||||
use super::super::MongoDb;
|
||||
|
||||
static COL: &str = "bots";
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractBot for MongoDb {
|
||||
async fn fetch_bot(&self, id: &str) -> Result<Bot> {
|
||||
self.find_one_by_id(COL, id).await
|
||||
}
|
||||
|
||||
async fn fetch_bot_by_token(&self, token: &str) -> Result<Bot> {
|
||||
self.find_one(
|
||||
COL,
|
||||
doc! {
|
||||
"token": token
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn insert_bot(&self, bot: &Bot) -> Result<()> {
|
||||
self.insert_one(COL, &bot).await.map(|_| ())
|
||||
}
|
||||
|
||||
async fn update_bot(&self, id: &str, bot: &PartialBot, remove: Vec<FieldsBot>) -> Result<()> {
|
||||
self.update_one_by_id(
|
||||
COL,
|
||||
id,
|
||||
bot,
|
||||
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
async fn delete_bot(&self, id: &str) -> Result<()> {
|
||||
self.delete_one_by_id(COL, id).await.map(|_| ())
|
||||
}
|
||||
|
||||
async fn fetch_bots_by_user(&self, user_id: &str) -> Result<Vec<Bot>> {
|
||||
self.find(
|
||||
COL,
|
||||
doc! {
|
||||
"owner": user_id
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_number_of_bots_by_user(&self, user_id: &str) -> Result<usize> {
|
||||
// ! FIXME: move this to generic?
|
||||
self.fetch_bots_by_user(user_id).await.map(|x| x.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDocumentPath for FieldsBot {
|
||||
fn as_path(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
FieldsBot::InteractionsURL => Some("interactions_url"),
|
||||
FieldsBot::Token => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
319
crates/quark/src/impl/mongo/users/user.rs
Normal file
319
crates/quark/src/impl/mongo/users/user.rs
Normal file
@@ -0,0 +1,319 @@
|
||||
use bson::Document;
|
||||
use futures::StreamExt;
|
||||
use mongodb::options::{Collation, CollationStrength, FindOneOptions, FindOptions};
|
||||
|
||||
use crate::models::user::{FieldsUser, PartialUser, RelationshipStatus, User};
|
||||
use crate::r#impl::mongo::IntoDocumentPath;
|
||||
use crate::{AbstractUser, Error, Result};
|
||||
|
||||
use super::super::MongoDb;
|
||||
|
||||
lazy_static! {
|
||||
static ref FIND_USERNAME_OPTIONS: FindOneOptions = FindOneOptions::builder()
|
||||
.collation(
|
||||
Collation::builder()
|
||||
.locale("en")
|
||||
.strength(CollationStrength::Secondary)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
static COL: &str = "users";
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractUser for MongoDb {
|
||||
async fn fetch_user(&self, id: &str) -> Result<User> {
|
||||
self.find_one_by_id(COL, id).await
|
||||
}
|
||||
|
||||
async fn fetch_user_by_username(&self, username: &str) -> Result<User> {
|
||||
self.find_one_with_options(
|
||||
COL,
|
||||
doc! {
|
||||
"username": username
|
||||
},
|
||||
FIND_USERNAME_OPTIONS.clone(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_user_by_token(&self, token: &str) -> Result<User> {
|
||||
let session = self
|
||||
.col::<Document>("sessions")
|
||||
.find_one(
|
||||
doc! {
|
||||
"token": token
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find_one",
|
||||
with: "sessions",
|
||||
})?
|
||||
.ok_or(Error::InvalidSession)?;
|
||||
|
||||
self.fetch_user(session.get_str("user_id").unwrap()).await
|
||||
}
|
||||
|
||||
async fn insert_user(&self, user: &User) -> Result<()> {
|
||||
self.insert_one(COL, user).await.map(|_| ())
|
||||
}
|
||||
|
||||
async fn update_user(
|
||||
&self,
|
||||
id: &str,
|
||||
user: &PartialUser,
|
||||
remove: Vec<FieldsUser>,
|
||||
) -> Result<()> {
|
||||
self.update_one_by_id(
|
||||
COL,
|
||||
id,
|
||||
user,
|
||||
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
async fn delete_user(&self, id: &str) -> Result<()> {
|
||||
self.delete_one_by_id(COL, id).await.map(|_| ())
|
||||
}
|
||||
|
||||
async fn fetch_users<'a>(&self, ids: &'a [String]) -> Result<Vec<User>> {
|
||||
let mut cursor = self
|
||||
.col::<User>(COL)
|
||||
.find(
|
||||
doc! {
|
||||
"_id": {
|
||||
"$in": ids
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find",
|
||||
with: "users",
|
||||
})?;
|
||||
|
||||
let mut users = vec![];
|
||||
while let Some(Ok(user)) = cursor.next().await {
|
||||
users.push(user);
|
||||
}
|
||||
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
async fn is_username_taken(&self, username: &str) -> Result<bool> {
|
||||
// ! FIXME: move this up to generic
|
||||
match self.fetch_user_by_username(username).await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(Error::NotFound) => Ok(false),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_mutual_user_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>> {
|
||||
Ok(self
|
||||
.col::<Document>(COL)
|
||||
.find(
|
||||
doc! {
|
||||
"$and": [
|
||||
{ "relations": { "$elemMatch": { "_id": &user_a, "status": "Friend" } } },
|
||||
{ "relations": { "$elemMatch": { "_id": &user_b, "status": "Friend" } } }
|
||||
]
|
||||
},
|
||||
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find",
|
||||
with: "users",
|
||||
})?
|
||||
.filter_map(|s| async { s.ok() })
|
||||
.collect::<Vec<Document>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|x| x.get_str("_id").ok().map(|x| x.to_string()))
|
||||
.collect::<Vec<String>>())
|
||||
}
|
||||
|
||||
async fn fetch_mutual_channel_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>> {
|
||||
Ok(self
|
||||
.col::<Document>("channels")
|
||||
.find(
|
||||
doc! {
|
||||
"channel_type": {
|
||||
"$in": ["Group", "DirectMessage"]
|
||||
},
|
||||
"recipients": {
|
||||
"$all": [ user_a, user_b ]
|
||||
}
|
||||
},
|
||||
FindOptions::builder().projection(doc! { "_id": 1 }).build(),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "find",
|
||||
with: "channels",
|
||||
})?
|
||||
.filter_map(|s| async { s.ok() })
|
||||
.collect::<Vec<Document>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|x| x.get_str("_id").ok().map(|x| x.to_string()))
|
||||
.collect::<Vec<String>>())
|
||||
}
|
||||
|
||||
async fn fetch_mutual_server_ids(&self, user_a: &str, user_b: &str) -> Result<Vec<String>> {
|
||||
Ok(self
|
||||
.col::<Document>("server_members")
|
||||
.aggregate(
|
||||
vec![
|
||||
doc! {
|
||||
"$match": {
|
||||
"_id.user": user_a
|
||||
}
|
||||
},
|
||||
doc! {
|
||||
"$lookup": {
|
||||
"from": "server_members",
|
||||
"as": "members",
|
||||
"let": {
|
||||
"server": "$_id.server"
|
||||
},
|
||||
"pipeline": [
|
||||
{
|
||||
"$match": {
|
||||
"$expr": {
|
||||
"$and": [
|
||||
{ "$eq": [ "$_id.user", user_b ] },
|
||||
{ "$eq": [ "$_id.server", "$$server" ] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
doc! {
|
||||
"$match": {
|
||||
"members": {
|
||||
"$size": 1_i32
|
||||
}
|
||||
}
|
||||
},
|
||||
doc! {
|
||||
"$project": {
|
||||
"_id": "$_id.server"
|
||||
}
|
||||
},
|
||||
],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "aggregate",
|
||||
with: "server_members",
|
||||
})?
|
||||
.filter_map(|s| async { s.ok() })
|
||||
.collect::<Vec<Document>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|x| x.get_str("_id").ok().map(|i| i.to_string()))
|
||||
.collect::<Vec<String>>())
|
||||
}
|
||||
|
||||
async fn set_relationship(
|
||||
&self,
|
||||
user_id: &str,
|
||||
target_id: &str,
|
||||
relationship: &RelationshipStatus,
|
||||
) -> Result<()> {
|
||||
if let RelationshipStatus::None = relationship {
|
||||
return self.pull_relationship(user_id, target_id).await;
|
||||
}
|
||||
|
||||
self.col::<Document>(COL)
|
||||
.update_one(
|
||||
doc! {
|
||||
"_id": user_id
|
||||
},
|
||||
vec![doc! {
|
||||
"$set": {
|
||||
"relations": {
|
||||
"$concatArrays": [
|
||||
{
|
||||
"$ifNull": [
|
||||
{
|
||||
"$filter": {
|
||||
"input": "$relations",
|
||||
"cond": {
|
||||
"$ne": [
|
||||
"$$this._id",
|
||||
target_id
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
[]
|
||||
]
|
||||
},
|
||||
[
|
||||
{
|
||||
"_id": target_id,
|
||||
"status": format!("{:?}", relationship)
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "update_one",
|
||||
with: "user",
|
||||
})
|
||||
}
|
||||
|
||||
async fn pull_relationship(&self, user_id: &str, target_id: &str) -> Result<()> {
|
||||
self.col::<Document>(COL)
|
||||
.update_one(
|
||||
doc! {
|
||||
"_id": user_id
|
||||
},
|
||||
doc! {
|
||||
"$pull": {
|
||||
"relations": {
|
||||
"_id": target_id
|
||||
}
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "update_one",
|
||||
with: "user",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDocumentPath for FieldsUser {
|
||||
fn as_path(&self) -> Option<&'static str> {
|
||||
Some(match self {
|
||||
FieldsUser::Avatar => "avatar",
|
||||
FieldsUser::ProfileBackground => "profile.background",
|
||||
FieldsUser::ProfileContent => "profile.content",
|
||||
FieldsUser::StatusPresence => "status.presence",
|
||||
FieldsUser::StatusText => "status.text",
|
||||
})
|
||||
}
|
||||
}
|
||||
62
crates/quark/src/impl/mongo/users/user_settings.rs
Normal file
62
crates/quark/src/impl/mongo/users/user_settings.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use bson::{to_bson, Document};
|
||||
use mongodb::options::{FindOneOptions, UpdateOptions};
|
||||
|
||||
use crate::models::UserSettings;
|
||||
use crate::{AbstractUserSettings, Error, Result};
|
||||
|
||||
use super::super::MongoDb;
|
||||
|
||||
static COL: &str = "user_settings";
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractUserSettings for MongoDb {
|
||||
async fn fetch_user_settings(&'_ self, id: &str, filter: &'_ [String]) -> Result<UserSettings> {
|
||||
let mut projection = doc! {
|
||||
"_id": 0,
|
||||
};
|
||||
|
||||
for key in filter {
|
||||
projection.insert(key, 1);
|
||||
}
|
||||
|
||||
self.find_one_with_options(
|
||||
COL,
|
||||
doc! {
|
||||
"_id": id
|
||||
},
|
||||
FindOneOptions::builder().projection(projection).build(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
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::<Document>(COL)
|
||||
.update_one(
|
||||
doc! {
|
||||
"_id": id
|
||||
},
|
||||
doc! {
|
||||
"$set": set
|
||||
},
|
||||
UpdateOptions::builder().upsert(true).build(),
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| Error::DatabaseError {
|
||||
operation: "update_one",
|
||||
with: "user_settings",
|
||||
})
|
||||
}
|
||||
|
||||
async fn delete_user_settings(&self, id: &str) -> Result<()> {
|
||||
self.delete_one_by_id(COL, id).await.map(|_| ())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user