1
0
mirror of https://gitlab.com/comunic/comunicapiv3 synced 2024-11-22 21:39:21 +00:00

Start conversations system migration

This commit is contained in:
Pierre HUBERT 2021-03-02 18:57:34 +01:00
parent cd5419edea
commit 60733f2d04
7 changed files with 262 additions and 30 deletions

View File

@ -5,31 +5,86 @@
//! @author Pierre HUBERT //! @author Pierre HUBERT
use serde::Serialize; use serde::Serialize;
use serde_json::Value;
use crate::data::conversation_message::ConversationMessage; use crate::data::conversation_message::{ConversationMessage, ConversationServerMessageType};
use crate::utils::user_data_utils::user_data_url; use crate::utils::user_data_utils::user_data_url;
#[derive(Serialize)] #[derive(Serialize)]
#[allow(non_snake_case)] struct ConversationMessageFileAPI {
url: String,
size: u64,
name: String,
thumbnail: Option<String>,
r#type: String,
}
#[derive(Serialize)]
pub struct ConversationMessageAPI { pub struct ConversationMessageAPI {
ID: u64, id: u64,
convID: u64, conv_id: u64,
ID_user: u64, user_id: Option<u64>,
time_insert: u64, time_insert: u64,
message: String, message: Option<String>,
image_path: Option<String>, file: Option<ConversationMessageFileAPI>,
server_message: Option<serde_json::Value>,
} }
impl ConversationMessageAPI { impl ConversationMessageAPI {
/// Turn a conversation message into an API entry /// Turn a conversation message into an API entry
pub fn new(msg: &ConversationMessage) -> ConversationMessageAPI { pub fn new(msg: &ConversationMessage) -> ConversationMessageAPI {
let file = match &msg.file {
None => None,
Some(file) => Some(ConversationMessageFileAPI {
url: user_data_url(&file.path.clone()),
size: file.size,
name: file.name.clone(),
thumbnail: file.thumbnail.clone(),
r#type: file.r#type.clone(),
})
};
let server_message = match &msg.server_message {
None => None,
Some(msg) => {
let mut value = serde_json::Map::new();
match msg {
ConversationServerMessageType::UserCreatedConversation(user) => {
value.insert("type".to_string(), Value::from("user_created_conv"));
value.insert("user_id".to_string(), Value::from(user.id()));
}
ConversationServerMessageType::UserAddedAnotherUserToConversation(msg) => {
value.insert("type".to_string(), Value::from("user_added_another"));
value.insert("user_who_added".to_string(), Value::from(msg.user_who_added.id()));
value.insert("user_added".to_string(), Value::from(msg.user_added.id()));
}
ConversationServerMessageType::UserLeftConversation(u) => {
value.insert("type".to_string(), Value::from("user_left"));
value.insert("user_id".to_string(), Value::from(u.id()));
}
ConversationServerMessageType::UserRemovedFromConversation(msg) => {
value.insert("type".to_string(), Value::from("user_removed_another"));
value.insert("user_who_removed".to_string(), Value::from(msg.user_who_removed.id()));
value.insert("user_removed".to_string(), Value::from(msg.user_removed.id()));
}
}
Some(serde_json::Value::Object(value))
}
};
ConversationMessageAPI { ConversationMessageAPI {
ID: msg.id, id: msg.id,
convID: msg.conv_id, conv_id: msg.conv_id,
ID_user: msg.user_id.id(), user_id: msg.user_id.clone().map(|u| u.id()),
time_insert: msg.time_sent, time_insert: msg.time_sent,
message: msg.message.clone().unwrap_or(String::new()), message: msg.message.clone(),
image_path: msg.image_path.as_ref().map(|f| user_data_url(f)), file,
server_message,
} }
} }

View File

@ -61,7 +61,7 @@ impl AccountExport {
// Conversation messages // Conversation messages
for (_, conv_messages) in &self.conversation_messages { for (_, conv_messages) in &self.conversation_messages {
conv_messages.iter().for_each(|f| { set.insert(f.user_id.clone()); }) conv_messages.iter().for_each(|f| { set.extend(f.referenced_users_id()); })
} }
Ok(set) Ok(set)

View File

@ -2,15 +2,141 @@
//! //!
//! Information about a single conversation message //! Information about a single conversation message
use std::collections::HashSet;
use crate::data::error::{ExecError, Res};
use crate::data::user::UserID; use crate::data::user::UserID;
pub type ConvMessageID = u64;
#[derive(Debug)]
pub struct ConversationMessageFile {
pub path: String,
pub size: u64,
pub name: String,
pub thumbnail: Option<String>,
pub r#type: String,
}
#[derive(Debug)]
pub struct UserAddedAnotherUserToConversation {
pub user_who_added: UserID,
pub user_added: UserID,
}
#[derive(Debug)]
pub struct UserRemovedAnotherUserToConversation {
pub user_who_removed: UserID,
pub user_removed: UserID,
}
#[derive(Debug)]
pub enum ConversationServerMessageType {
UserCreatedConversation(UserID),
UserAddedAnotherUserToConversation(UserAddedAnotherUserToConversation),
UserLeftConversation(UserID),
UserRemovedFromConversation(UserRemovedAnotherUserToConversation),
}
impl ConversationServerMessageType {
pub fn to_db(&self) -> String {
let info = match self {
ConversationServerMessageType::UserCreatedConversation(u) => ("user_created_conv", Some(u.clone()), None),
ConversationServerMessageType::UserAddedAnotherUserToConversation(msg) => ("user_added_another_user", Some(msg.user_who_added.clone()), Some(msg.user_added.clone())),
ConversationServerMessageType::UserLeftConversation(u) => ("user_left", Some(u.clone()), None),
ConversationServerMessageType::UserRemovedFromConversation(msg) => ("user_removed", Some(msg.user_who_removed.clone()), Some(msg.user_removed.clone())),
};
format!(
"{}-{}-{}",
info.0,
info.1.map(|u| u.id()).unwrap_or(0),
info.2.map(|u| u.id()).unwrap_or(0)
)
}
pub fn from_db(str: &str) -> Res<Self> {
let split: Vec<&str> = str.split("-").collect();
if split.len() != 3 {
return Err(ExecError::boxed_new("Invalid ConversationSerMessageType"));
}
let id_1 = split[1].parse::<u64>()?;
let id_2 = split[2].parse::<u64>()?;
let info = (
split[0],
match id_1 {
0 => None,
id => Some(UserID::new(id))
},
match id_2 {
0 => None,
id => Some(UserID::new(id))
},
);
match info {
("user_created_conv", Some(user_id), _) => Ok(Self::UserCreatedConversation(user_id)),
("user_added_another_user", Some(user_id), Some(user_2)) => Ok(Self::UserAddedAnotherUserToConversation(UserAddedAnotherUserToConversation {
user_who_added: user_id,
user_added: user_2,
})),
("user_left", Some(user_id), _) => Ok(Self::UserLeftConversation(user_id)),
("user_removed", Some(user_id), Some(user_2)) => Ok(Self::UserRemovedFromConversation(UserRemovedAnotherUserToConversation {
user_who_removed: user_id,
user_removed: user_2,
})),
_ => Err(ExecError::boxed_new("Unknown server message type!"))
}
}
}
/// Information about a single conversation message /// Information about a single conversation message
#[derive(Debug)] #[derive(Debug)]
pub struct ConversationMessage { pub struct ConversationMessage {
pub id: u64, pub id: ConvMessageID,
pub time_sent: u64, pub time_sent: u64,
pub conv_id: u64, pub conv_id: u64,
pub user_id: UserID, pub user_id: Option<UserID>,
pub message: Option<String>, pub message: Option<String>,
pub image_path: Option<String>, pub server_message: Option<ConversationServerMessageType>,
pub file: Option<ConversationMessageFile>,
}
impl ConversationMessage {
/// Get the entire list of referenced users in a conversation message
pub fn referenced_users_id(&self) -> HashSet<UserID> {
let mut users = HashSet::new();
if let Some(user_id) = &self.user_id {
users.insert(user_id.clone());
}
if let Some(srv_msg) = &self.server_message {
match srv_msg {
ConversationServerMessageType::UserCreatedConversation(user) => {
users.insert(user.clone());
}
ConversationServerMessageType::UserAddedAnotherUserToConversation(msg) => {
users.insert(msg.user_who_added.clone());
users.insert(msg.user_added.clone());
}
ConversationServerMessageType::UserLeftConversation(user) => {
users.insert(user.clone());
}
ConversationServerMessageType::UserRemovedFromConversation(msg) => {
users.insert(msg.user_who_removed.clone());
users.insert(msg.user_removed.clone());
}
}
}
users
}
} }

View File

@ -2,9 +2,9 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::constants::database_tables_names::{CONV_LIST_TABLE, CONV_MESSAGES_TABLE, CONV_MEMBERS_TABLE}; use crate::constants::database_tables_names::{CONV_LIST_TABLE, CONV_MEMBERS_TABLE, CONV_MESSAGES_TABLE};
use crate::data::conversation::Conversation; use crate::data::conversation::Conversation;
use crate::data::conversation_message::ConversationMessage; use crate::data::conversation_message::{ConversationMessage, ConversationMessageFile, ConversationServerMessageType};
use crate::data::error::{ExecError, Res, ResultBoxError}; use crate::data::error::{ExecError, Res, ResultBoxError};
use crate::data::new_conversation::NewConversation; use crate::data::new_conversation::NewConversation;
use crate::data::new_conversation_message::NewConversationMessage; use crate::data::new_conversation_message::NewConversationMessage;
@ -14,7 +14,7 @@ use crate::helpers::{database, events_helper};
use crate::helpers::database::InsertQuery; use crate::helpers::database::InsertQuery;
use crate::helpers::events_helper::Event; use crate::helpers::events_helper::Event;
use crate::utils::date_utils::time; use crate::utils::date_utils::time;
use crate::utils::user_data_utils::user_data_path; use crate::utils::user_data_utils::delete_user_data_file_if_exists;
/// Create a new conversation. This method returns the ID of the created conversation /// Create a new conversation. This method returns the ID of the created conversation
pub fn create(conv: &NewConversation) -> ResultBoxError<u64> { pub fn create(conv: &NewConversation) -> ResultBoxError<u64> {
@ -377,11 +377,12 @@ pub fn update_message_content(msg_id: u64, new_content: &str) -> ResultBoxError<
/// Remove a message from a conversation /// Remove a message from a conversation
pub fn delete_message(msg: &ConversationMessage) -> ResultBoxError<()> { pub fn delete_message(msg: &ConversationMessage) -> ResultBoxError<()> {
// Delete associated image (if any) // Delete associated files
if let Some(img) = &msg.image_path { if let Some(file) = &msg.file {
let path = user_data_path(img.as_ref()); delete_user_data_file_if_exists(&file.path)?;
if path.exists() {
std::fs::remove_file(path)?; if let Some(thumb) = &file.thumbnail {
delete_user_data_file_if_exists(thumb)?;
} }
} }
@ -523,12 +524,39 @@ fn db_to_conversation_info(row: &database::RowResult) -> ResultBoxError<Conversa
/// Turn a database entry into a ConversationMessgae object /// Turn a database entry into a ConversationMessgae object
fn db_to_conversation_message(row: &database::RowResult) -> ResultBoxError<ConversationMessage> { fn db_to_conversation_message(row: &database::RowResult) -> ResultBoxError<ConversationMessage> {
let user_id = match row.is_null("user_id")? {
true => None,
false => Some(row.get_user_id("user_id")?)
};
let file = match row.is_null_or_empty("filepath")? {
true => None,
false => Some(ConversationMessageFile {
path: row.get_str("filepath")?,
size: row.get_u64("file_size")?,
name: row.get_str("file_name")?,
thumbnail: row.get_optional_str("file_thumbnail")?,
r#type: row.get_str("file_type")?,
})
};
let server_message = match &user_id {
Some(_) => None,
None => Some(ConversationServerMessageType::from_db(&row.get_str("message")?)?)
};
let message = match server_message {
None => row.get_optional_str("message")?,
Some(_) => None,
};
Ok(ConversationMessage { Ok(ConversationMessage {
id: row.get_u64("id")?, id: row.get_u64("id")?,
time_sent: row.get_u64("time_insert")?, time_sent: row.get_u64("time_sent")?,
conv_id: row.get_u64("conv_id")?, conv_id: row.get_u64("conv_id")?,
user_id: row.get_user_id("user_id")?, user_id,
message: row.get_optional_str("message")?, message,
image_path: row.get_optional_str("image_path")?, server_message,
file,
}) })
} }

View File

@ -380,6 +380,15 @@ impl<'a> RowResult<'a> {
} }
} }
/// Check out whether a given value is null or empty or not
pub fn is_null_or_empty(&self, name: &str) -> ResultBoxError<bool> {
if self.is_null(name)? {
return Ok(true);
}
Ok(self.get_str(name)?.is_empty())
}
/// Get an optional string => Set to None if string is null / empty /// Get an optional string => Set to None if string is null / empty
pub fn get_optional_str(&self, name: &str) -> ResultBoxError<Option<String>> { pub fn get_optional_str(&self, name: &str) -> ResultBoxError<Option<String>> {
match self.is_null(name)? { match self.is_null(name)? {

View File

@ -1,6 +1,7 @@
use comunic_server::{cleanup_thread, server}; use comunic_server::{cleanup_thread, server};
use comunic_server::api_data::conversation_message_api::ConversationMessageAPI;
use comunic_server::data::config::{conf, Config}; use comunic_server::data::config::{conf, Config};
use comunic_server::helpers::database; use comunic_server::helpers::{conversations_helper, database};
#[actix_rt::main] #[actix_rt::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
@ -18,6 +19,10 @@ async fn main() -> std::io::Result<()> {
// Start cleanup thread // Start cleanup thread
cleanup_thread::start().expect("Failed to start cleanup thread!"); cleanup_thread::start().expect("Failed to start cleanup thread!");
let msg = conversations_helper::get_last_messages(120, 100).unwrap();
println!("{:#?}", msg);
println!("{:#?}", serde_json::to_string(&ConversationMessageAPI::for_list(&msg)));
// Start the server // Start the server
server::start_server(conf()).await server::start_server(conf()).await
} }

View File

@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
use serde_json::to_string; use serde_json::to_string;
use crate::data::config::conf; use crate::data::config::conf;
use crate::data::error::{ExecError, ResultBoxError}; use crate::data::error::{ExecError, Res, ResultBoxError};
use crate::data::user::UserID; use crate::data::user::UserID;
use crate::utils::crypt_utils::rand_str; use crate::utils::crypt_utils::rand_str;
@ -62,3 +62,12 @@ pub fn generate_new_user_data_file_name(dir: &Path, ext: &str) -> ResultBoxError
} }
} }
} }
/// Delete a file from user data, if it exists
pub fn delete_user_data_file_if_exists(path: &str) -> Res {
let path = user_data_path(path.as_ref());
if path.exists() {
std::fs::remove_file(path)?;
}
Ok(())
}