mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 13:36:59 +00:00
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2836,6 +2836,7 @@ dependencies = [
|
||||
"mongodb",
|
||||
"nanoid",
|
||||
"optional_struct",
|
||||
"revolt-result",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
@@ -16,6 +16,9 @@ async-std-runtime = [ "async-std" ]
|
||||
default = [ "mongodb", "async-std-runtime" ]
|
||||
|
||||
[dependencies]
|
||||
# Repo
|
||||
revolt-result = { path = "../result" }
|
||||
|
||||
# Utility
|
||||
log = "*"
|
||||
nanoid = "0.4.0"
|
||||
@@ -37,4 +40,4 @@ async-recursion = "1.0.4"
|
||||
async-std = { version = "1.8.0", features = ["attributes"], optional = true }
|
||||
|
||||
# Authifier
|
||||
authifier = { version = "1.0", default-features = false }
|
||||
authifier = { version = "1.0" }
|
||||
|
||||
@@ -36,7 +36,7 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Insert one document into a collection
|
||||
async fn insert_one<T: Serialize>(
|
||||
pub async fn insert_one<T: Serialize>(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
document: T,
|
||||
@@ -44,8 +44,19 @@ impl MongoDb {
|
||||
self.col::<T>(collection).insert_one(document, None).await
|
||||
}
|
||||
|
||||
/// Count documents by projection
|
||||
pub async fn count_documents(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
projection: Document,
|
||||
) -> Result<u64> {
|
||||
self.col::<Document>(collection)
|
||||
.count_documents(projection, None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Find multiple documents in a collection with options
|
||||
async fn find_with_options<O, T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
pub async fn find_with_options<O, T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
projection: Document,
|
||||
@@ -71,7 +82,7 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Find multiple documents in a collection
|
||||
async fn find<T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
pub async fn find<T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
projection: Document,
|
||||
@@ -80,7 +91,7 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Find one document with options
|
||||
async fn find_one_with_options<O, T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
pub async fn find_one_with_options<O, T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
projection: Document,
|
||||
@@ -95,7 +106,7 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Find one document
|
||||
async fn find_one<T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
pub async fn find_one<T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
projection: Document,
|
||||
@@ -105,7 +116,7 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Find one document by its ID
|
||||
async fn find_one_by_id<T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
pub async fn find_one_by_id<T: DeserializeOwned + Unpin + Send + Sync>(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
id: &str,
|
||||
@@ -120,7 +131,7 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Update one document given a projection, partial document, and list of paths to unset
|
||||
async fn update_one<P, T: Serialize>(
|
||||
pub async fn update_one<P, T: Serialize>(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
projection: Document,
|
||||
@@ -159,7 +170,7 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Update one document given an ID, partial document, and list of paths to unset
|
||||
async fn update_one_by_id<P, T: Serialize>(
|
||||
pub async fn update_one_by_id<P, T: Serialize>(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
id: &str,
|
||||
@@ -183,7 +194,7 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Delete one document by the given projection
|
||||
async fn delete_one(
|
||||
pub async fn delete_one(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
projection: Document,
|
||||
@@ -194,7 +205,11 @@ impl MongoDb {
|
||||
}
|
||||
|
||||
/// Delete one document by the given ID
|
||||
async fn delete_one_by_id(&self, collection: &'static str, id: &str) -> Result<DeleteResult> {
|
||||
pub async fn delete_one_by_id(
|
||||
&self,
|
||||
collection: &'static str,
|
||||
id: &str,
|
||||
) -> Result<DeleteResult> {
|
||||
self.delete_one(
|
||||
collection,
|
||||
doc! {
|
||||
|
||||
@@ -13,6 +13,9 @@ extern crate log;
|
||||
#[macro_use]
|
||||
extern crate optional_struct;
|
||||
|
||||
#[macro_use]
|
||||
extern crate revolt_result;
|
||||
|
||||
#[cfg(feature = "mongodb")]
|
||||
pub use mongodb;
|
||||
|
||||
@@ -28,7 +31,7 @@ macro_rules! database_derived {
|
||||
macro_rules! auto_derived {
|
||||
( $( $item:item )+ ) => {
|
||||
$(
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||
$item
|
||||
)+
|
||||
};
|
||||
@@ -36,8 +39,8 @@ macro_rules! auto_derived {
|
||||
|
||||
macro_rules! auto_derived_partial {
|
||||
( $item:item, $name:expr ) => {
|
||||
#[derive(OptionalStruct, Serialize, Deserialize, Debug, Clone)]
|
||||
#[optional_derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(OptionalStruct, Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
|
||||
#[optional_derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
|
||||
#[optional_name = $name]
|
||||
#[opt_skip_serializing_none]
|
||||
#[opt_some_priority]
|
||||
@@ -48,6 +51,15 @@ macro_rules! auto_derived_partial {
|
||||
mod drivers;
|
||||
pub use drivers::*;
|
||||
|
||||
macro_rules! database_test {
|
||||
( $name: expr ) => {
|
||||
$crate::DatabaseInfo::Reference
|
||||
.connect()
|
||||
.await
|
||||
.expect("Database connection failed.")
|
||||
};
|
||||
}
|
||||
|
||||
mod models;
|
||||
pub use models::*;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
mod model;
|
||||
// mod ops;
|
||||
mod ops;
|
||||
|
||||
pub use model::*;
|
||||
// pub use ops::*;
|
||||
pub use ops::*;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::Database;
|
||||
|
||||
auto_derived_partial!(
|
||||
@@ -66,9 +68,59 @@ impl Bot {
|
||||
}
|
||||
|
||||
/// Delete this bot
|
||||
pub async fn delete(&self, db: &Database) -> Result<(), ()> {
|
||||
pub async fn delete(&self, db: &Database) -> Result<()> {
|
||||
// db.fetch_user(&self.id).await?.mark_deleted(db).await?;
|
||||
// db.delete_bot(&self.id).await
|
||||
Ok(())
|
||||
db.delete_bot(&self.id).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Bot, FieldsBot};
|
||||
|
||||
#[async_std::test]
|
||||
async fn crud() {
|
||||
let db = database_test!("bot_crud");
|
||||
|
||||
let bot_id = "bot";
|
||||
let user_id = "user";
|
||||
let token = "my_token";
|
||||
|
||||
let bot = Bot {
|
||||
id: bot_id.to_string(),
|
||||
owner: user_id.to_string(),
|
||||
token: token.to_string(),
|
||||
interactions_url: Some("some url".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
db.insert_bot(&bot).await.unwrap();
|
||||
db.update_bot(
|
||||
bot_id,
|
||||
&crate::PartialBot {
|
||||
public: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
vec![FieldsBot::Token, FieldsBot::InteractionsURL],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let fetched_bot1 = db.fetch_bot(bot_id).await.unwrap();
|
||||
let fetched_bot2 = db.fetch_bot_by_token(&fetched_bot1.token).await.unwrap();
|
||||
let fetched_bots = db.fetch_bots_by_user(user_id).await.unwrap();
|
||||
|
||||
assert!(!bot.public);
|
||||
assert!(fetched_bot1.public);
|
||||
assert!(bot.interactions_url.is_some());
|
||||
assert!(fetched_bot1.interactions_url.is_none());
|
||||
assert_ne!(bot.token, fetched_bot1.token);
|
||||
assert_eq!(fetched_bot1, fetched_bot2);
|
||||
assert_eq!(fetched_bot1, fetched_bots[0]);
|
||||
assert_eq!(1, db.get_number_of_bots_by_user(user_id).await.unwrap());
|
||||
|
||||
bot.delete(&db).await.unwrap();
|
||||
assert!(db.fetch_bot(bot_id).await.is_err());
|
||||
assert_eq!(0, db.get_number_of_bots_by_user(user_id).await.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
mod dummy;
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::{Bot, FieldsBot, PartialBot};
|
||||
|
||||
mod mongodb;
|
||||
mod reference;
|
||||
|
||||
#[async_trait]
|
||||
pub trait AbstractBots: Sync + Send {
|
||||
@@ -13,7 +17,12 @@ pub trait AbstractBots: Sync + Send {
|
||||
async fn insert_bot(&self, bot: &Bot) -> Result<()>;
|
||||
|
||||
/// Update bot with new information
|
||||
async fn update_bot(&self, id: &str, bot: &PartialBot, remove: Vec<FieldsBot>) -> Result<()>;
|
||||
async fn update_bot(
|
||||
&self,
|
||||
id: &str,
|
||||
partial: &PartialBot,
|
||||
remove: Vec<FieldsBot>,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Delete a bot from the database
|
||||
async fn delete_bot(&self, id: &str) -> Result<()>;
|
||||
|
||||
@@ -1,9 +1,102 @@
|
||||
use revolt_database::MongoDb;
|
||||
use ::mongodb::bson::doc;
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::{Bot, FieldsBot, PartialBot};
|
||||
use crate::{IntoDocumentPath, MongoDb};
|
||||
|
||||
use super::AbstractBots;
|
||||
|
||||
mod init;
|
||||
mod scripts;
|
||||
static COL: &str = "bots";
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractBots for MongoDb {}
|
||||
impl AbstractBots for MongoDb {
|
||||
/// Fetch a bot by its id
|
||||
async fn fetch_bot(&self, id: &str) -> Result<Bot> {
|
||||
self.find_one_by_id(COL, id)
|
||||
.await
|
||||
.map_err(|_| create_database_error!("find_one", COL))?
|
||||
.ok_or_else(|| create_error!(NotFound))
|
||||
}
|
||||
|
||||
/// Fetch a bot by its token
|
||||
async fn fetch_bot_by_token(&self, token: &str) -> Result<Bot> {
|
||||
self.find_one(
|
||||
COL,
|
||||
doc! {
|
||||
"token": token
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(|_| create_database_error!("find_one", COL))?
|
||||
.ok_or_else(|| create_error!(NotFound))
|
||||
}
|
||||
|
||||
/// Insert new bot into the database
|
||||
async fn insert_bot(&self, bot: &Bot) -> Result<()> {
|
||||
self.insert_one(COL, &bot)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| create_database_error!("insert_one", COL))
|
||||
}
|
||||
|
||||
/// Update bot with new information
|
||||
async fn update_bot(
|
||||
&self,
|
||||
id: &str,
|
||||
partial: &PartialBot,
|
||||
remove: Vec<FieldsBot>,
|
||||
) -> Result<()> {
|
||||
self.update_one_by_id(
|
||||
COL,
|
||||
id,
|
||||
partial,
|
||||
remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| create_database_error!("update_one", COL))
|
||||
}
|
||||
|
||||
/// Delete a bot from the database
|
||||
async fn delete_bot(&self, id: &str) -> Result<()> {
|
||||
self.delete_one_by_id(COL, id)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| create_database_error!("delete_one", COL))
|
||||
}
|
||||
|
||||
/// Fetch bots owned by a user
|
||||
async fn fetch_bots_by_user(&self, user_id: &str) -> Result<Vec<Bot>> {
|
||||
self.find(
|
||||
COL,
|
||||
doc! {
|
||||
"owner": user_id
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(|_| create_database_error!("find", COL))
|
||||
}
|
||||
|
||||
/// Get the number of bots owned by a user
|
||||
async fn get_number_of_bots_by_user(&self, user_id: &str) -> Result<usize> {
|
||||
self.count_documents(
|
||||
COL,
|
||||
doc! {
|
||||
"owner": user_id
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map(|v| v as usize)
|
||||
.map_err(|_| create_database_error!("count", COL))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDocumentPath for FieldsBot {
|
||||
fn as_path(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
FieldsBot::InteractionsURL => Some("interactions_url"),
|
||||
FieldsBot::Token => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,81 @@
|
||||
use revolt_database::DummyDb;
|
||||
use revolt_result::Result;
|
||||
|
||||
use crate::ReferenceDb;
|
||||
use crate::{Bot, FieldsBot, PartialBot};
|
||||
|
||||
use super::AbstractBots;
|
||||
|
||||
#[async_trait]
|
||||
impl AbstractBots for DummyDb {}
|
||||
impl AbstractBots for ReferenceDb {
|
||||
/// Fetch a bot by its id
|
||||
async fn fetch_bot(&self, id: &str) -> Result<Bot> {
|
||||
let bots = self.bots.lock().await;
|
||||
bots.get(id).cloned().ok_or_else(|| create_error!(NotFound))
|
||||
}
|
||||
|
||||
/// Fetch a bot by its token
|
||||
async fn fetch_bot_by_token(&self, token: &str) -> Result<Bot> {
|
||||
let bots = self.bots.lock().await;
|
||||
bots.values()
|
||||
.find(|bot| bot.token == token)
|
||||
.cloned()
|
||||
.ok_or_else(|| create_error!(NotFound))
|
||||
}
|
||||
|
||||
/// Insert new bot into the database
|
||||
async fn insert_bot(&self, bot: &Bot) -> Result<()> {
|
||||
let mut bots = self.bots.lock().await;
|
||||
if bots.contains_key(&bot.id) {
|
||||
Err(create_database_error!("insert", "bot"))
|
||||
} else {
|
||||
bots.insert(bot.id.to_string(), bot.clone());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Update bot with new information
|
||||
async fn update_bot(
|
||||
&self,
|
||||
id: &str,
|
||||
partial: &PartialBot,
|
||||
remove: Vec<FieldsBot>,
|
||||
) -> Result<()> {
|
||||
let mut bots = self.bots.lock().await;
|
||||
if let Some(bot) = bots.get_mut(id) {
|
||||
for field in remove {
|
||||
bot.remove(&field);
|
||||
}
|
||||
|
||||
bot.apply_options(partial.clone());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a bot from the database
|
||||
async fn delete_bot(&self, id: &str) -> Result<()> {
|
||||
let mut bots = self.bots.lock().await;
|
||||
if bots.remove(id).is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(create_error!(NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch bots owned by a user
|
||||
async fn fetch_bots_by_user(&self, user_id: &str) -> Result<Vec<Bot>> {
|
||||
let bots = self.bots.lock().await;
|
||||
Ok(bots
|
||||
.values()
|
||||
.filter(|bot| bot.owner == user_id)
|
||||
.cloned()
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Get the number of bots owned by a user
|
||||
async fn get_number_of_bots_by_user(&self, user_id: &str) -> Result<usize> {
|
||||
let bots = self.bots.lock().await;
|
||||
Ok(bots.values().filter(|bot| bot.owner == user_id).count())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,11 @@ pub use bots::*;
|
||||
|
||||
use crate::{Database, MongoDb, ReferenceDb};
|
||||
|
||||
pub trait AbstractDatabase: Sync + Send + admin_migrations::AbstractMigrations {}
|
||||
pub trait AbstractDatabase:
|
||||
Sync + Send + admin_migrations::AbstractMigrations + bots::AbstractBots
|
||||
{
|
||||
}
|
||||
|
||||
impl AbstractDatabase for ReferenceDb {}
|
||||
impl AbstractDatabase for MongoDb {}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ pub enum ErrorType {
|
||||
// ? General errors
|
||||
DatabaseError {
|
||||
operation: String,
|
||||
with: String,
|
||||
collection: String,
|
||||
},
|
||||
InternalError,
|
||||
InvalidOperation,
|
||||
@@ -119,14 +119,24 @@ pub enum ErrorType {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_error {
|
||||
( $error:ident ) => {
|
||||
( $error:ident $( $tt:tt )? ) => {
|
||||
$crate::Error {
|
||||
error_type: $crate::ErrorType::$error,
|
||||
error_type: $crate::ErrorType::$error $( $tt )?,
|
||||
location: format!("{}:{}:{}", file!(), line!(), column!()),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_database_error {
|
||||
( $operation:expr, $collection:expr ) => {
|
||||
create_error!(DatabaseError {
|
||||
operation: $operation.to_string(),
|
||||
collection: $collection.to_string()
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ErrorType;
|
||||
@@ -136,4 +146,10 @@ mod tests {
|
||||
let error = create_error!(LabelMe);
|
||||
assert!(matches!(error.error_type, ErrorType::LabelMe));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_macro_to_construct_complex_error() {
|
||||
let error = create_error!(LabelMe);
|
||||
assert!(matches!(error.error_type, ErrorType::LabelMe));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user