mirror of
				https://gitlab.com/comunic/comunicapiv3
				synced 2025-11-03 17:14:03 +00:00 
			
		
		
		
	Get the list of conversations of the user
This commit is contained in:
		
							
								
								
									
										57
									
								
								src/api_data/conversation_api.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/api_data/conversation_api.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
//! # Conversation API object
 | 
			
		||||
//!
 | 
			
		||||
//! @author Pierre Hubert
 | 
			
		||||
use serde::{Serialize, Serializer};
 | 
			
		||||
use crate::api_data::legacy_api_bool::LegacyBool;
 | 
			
		||||
use crate::data::conversation::Conversation;
 | 
			
		||||
 | 
			
		||||
/// Special implementation of conversation name (false if none / the name otherwise)
 | 
			
		||||
struct ConvName(Option<String>);
 | 
			
		||||
 | 
			
		||||
impl Serialize for ConvName {
 | 
			
		||||
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
 | 
			
		||||
        S: Serializer {
 | 
			
		||||
        match &self.0 {
 | 
			
		||||
            None => serializer.serialize_bool(false),
 | 
			
		||||
            Some(n) => serializer.serialize_str(n)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
#[allow(non_snake_case)]
 | 
			
		||||
pub struct ConversationAPI {
 | 
			
		||||
    ID: u64,
 | 
			
		||||
    ID_owner: u64,
 | 
			
		||||
    last_active: u64,
 | 
			
		||||
    name: ConvName,
 | 
			
		||||
    following: LegacyBool,
 | 
			
		||||
    saw_last_message: LegacyBool,
 | 
			
		||||
    members: Vec<u64>,
 | 
			
		||||
    canEveryoneAddMembers: bool,
 | 
			
		||||
    can_have_call: bool,
 | 
			
		||||
    can_have_video_call: bool,
 | 
			
		||||
    has_call_now: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ConversationAPI {
 | 
			
		||||
 | 
			
		||||
    /// Construct a new Conversation instance
 | 
			
		||||
    pub fn new(conv: &Conversation) -> ConversationAPI {
 | 
			
		||||
        ConversationAPI {
 | 
			
		||||
            ID: conv.id,
 | 
			
		||||
            ID_owner: conv.owner_id as u64,
 | 
			
		||||
            last_active: conv.last_active,
 | 
			
		||||
            name: ConvName(conv.name.clone()),
 | 
			
		||||
            following: LegacyBool(conv.following),
 | 
			
		||||
            saw_last_message: LegacyBool(conv.saw_last_message),
 | 
			
		||||
            members: conv.members.iter().map(|x| x.clone() as u64).collect(),
 | 
			
		||||
            canEveryoneAddMembers: conv.can_everyone_add_members,
 | 
			
		||||
 | 
			
		||||
            // TODO : update when call system is implemented
 | 
			
		||||
            can_have_call: false,
 | 
			
		||||
            can_have_video_call: false,
 | 
			
		||||
            has_call_now: false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/api_data/legacy_api_bool.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/api_data/legacy_api_bool.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
//! # Legacy API boolean
 | 
			
		||||
//!
 | 
			
		||||
//! true => 1
 | 
			
		||||
//! false => 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
use serde::{Serialize, Serializer};
 | 
			
		||||
 | 
			
		||||
/// Special implementation of conversation name (false if none / the name otherwise)
 | 
			
		||||
pub struct LegacyBool(pub bool);
 | 
			
		||||
 | 
			
		||||
impl Serialize for LegacyBool {
 | 
			
		||||
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
 | 
			
		||||
        S: Serializer {
 | 
			
		||||
        match &self.0 {
 | 
			
		||||
            true => serializer.serialize_i8(1),
 | 
			
		||||
            false => serializer.serialize_i8(0),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -15,3 +15,5 @@ pub mod custom_emoji;
 | 
			
		||||
pub mod res_find_user_by_virtual_directory;
 | 
			
		||||
pub mod res_find_virtual_directory;
 | 
			
		||||
pub mod res_create_conversation;
 | 
			
		||||
pub mod conversation_api;
 | 
			
		||||
mod legacy_api_bool;
 | 
			
		||||
@@ -7,6 +7,7 @@ use crate::controllers::routes::RequestResult;
 | 
			
		||||
use crate::helpers::{user_helper, conversations_helper};
 | 
			
		||||
use crate::data::new_conversation::NewConversation;
 | 
			
		||||
use crate::api_data::res_create_conversation::ResCreateConversation;
 | 
			
		||||
use crate::api_data::conversation_api::ConversationAPI;
 | 
			
		||||
 | 
			
		||||
/// Create a new conversation
 | 
			
		||||
pub fn create(r: &mut HttpRequestHandler) -> RequestResult {
 | 
			
		||||
@@ -47,5 +48,8 @@ pub fn create(r: &mut HttpRequestHandler) -> RequestResult {
 | 
			
		||||
 | 
			
		||||
/// Get the list of conversations of a user
 | 
			
		||||
pub fn get_list(r: &mut HttpRequestHandler) -> RequestResult {
 | 
			
		||||
    r.success("Get the list of conversations")
 | 
			
		||||
 | 
			
		||||
    let list = conversations_helper::get_list_user(r.user_id()?)?;
 | 
			
		||||
 | 
			
		||||
    r.set_response(list.iter().map(|c| ConversationAPI::new(c)).collect::<Vec<ConversationAPI>>())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								src/data/conversation.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/data/conversation.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
//! # Conversation information
 | 
			
		||||
//!
 | 
			
		||||
//! @author Pierre Hubert
 | 
			
		||||
 | 
			
		||||
use crate::data::user::UserID;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct Conversation {
 | 
			
		||||
    pub id: u64,
 | 
			
		||||
    pub owner_id: UserID,
 | 
			
		||||
    pub name: Option<String>,
 | 
			
		||||
    pub members: Vec<UserID>,
 | 
			
		||||
    pub can_everyone_add_members: bool,
 | 
			
		||||
    pub last_active: u64,
 | 
			
		||||
    pub time_create: u64,
 | 
			
		||||
 | 
			
		||||
    pub following: bool,
 | 
			
		||||
    pub saw_last_message: bool,
 | 
			
		||||
}
 | 
			
		||||
@@ -8,3 +8,4 @@ pub mod user;
 | 
			
		||||
pub mod user_token;
 | 
			
		||||
pub mod custom_emoji;
 | 
			
		||||
pub mod new_conversation;
 | 
			
		||||
pub mod conversation;
 | 
			
		||||
@@ -4,10 +4,12 @@
 | 
			
		||||
 | 
			
		||||
use crate::data::new_conversation::NewConversation;
 | 
			
		||||
use crate::data::error::{ResultBoxError, ExecError};
 | 
			
		||||
use crate::helpers::database::InsertQuery;
 | 
			
		||||
use crate::helpers::database::{InsertQuery};
 | 
			
		||||
use crate::constants::database_tables_names::{CONV_LIST_TABLE, CONV_USERS_TABLE};
 | 
			
		||||
use crate::utils::date_utils::time;
 | 
			
		||||
use crate::data::user::UserID;
 | 
			
		||||
use crate::data::conversation::Conversation;
 | 
			
		||||
use crate::helpers::database;
 | 
			
		||||
 | 
			
		||||
/// Create a new conversation. This method returns the ID of the created conversation
 | 
			
		||||
pub fn create(conv: &NewConversation) -> ResultBoxError<u64> {
 | 
			
		||||
@@ -46,3 +48,50 @@ pub fn add_member(conv_id: u64, user_id: UserID, following: bool) -> ResultBoxEr
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the list of conversations of a specific user
 | 
			
		||||
pub fn get_list_user(user_id: UserID) -> ResultBoxError<Vec<Conversation>> {
 | 
			
		||||
    database::QueryInfo::new(CONV_LIST_TABLE)
 | 
			
		||||
        .alias("l")
 | 
			
		||||
 | 
			
		||||
        // Join with conversation members table
 | 
			
		||||
        .join(CONV_USERS_TABLE, "u", "l.id = u.conv_id")
 | 
			
		||||
 | 
			
		||||
        // Specify selected fields
 | 
			
		||||
        .add_field("*")
 | 
			
		||||
        .add_field("l.id as id")
 | 
			
		||||
        .add_field("l.user_id as owner_id")
 | 
			
		||||
 | 
			
		||||
        // Filter query
 | 
			
		||||
        .cond_user_id("u.user_id", user_id)
 | 
			
		||||
 | 
			
		||||
        // Sort results
 | 
			
		||||
        .set_order("l.last_active DESC")
 | 
			
		||||
 | 
			
		||||
        // Execute query
 | 
			
		||||
        .exec(db_to_conversation_info)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get the list of members of a conversation
 | 
			
		||||
pub fn get_list_members(conv_id: u64) -> ResultBoxError<Vec<UserID>> {
 | 
			
		||||
    database::QueryInfo::new(CONV_USERS_TABLE)
 | 
			
		||||
        .cond_u64("conv_id", conv_id)
 | 
			
		||||
        .add_field("user_id")
 | 
			
		||||
        .exec(|res|res.get_user_id("user_id"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Turn a database entry into a ConversationInfo object
 | 
			
		||||
fn db_to_conversation_info(row: &database::RowResult) -> ResultBoxError<Conversation> {
 | 
			
		||||
    let conv_id = row.get_u64("id")?;
 | 
			
		||||
    Ok(Conversation {
 | 
			
		||||
        id: conv_id,
 | 
			
		||||
        owner_id: row.get_user_id("owner_id")?,
 | 
			
		||||
        name: row.get_optional_str("name")?,
 | 
			
		||||
        members: get_list_members(conv_id)?,
 | 
			
		||||
        can_everyone_add_members: row.get_legacy_bool("can_everyone_add_members")?,
 | 
			
		||||
        last_active: row.get_u64("last_active")?,
 | 
			
		||||
        time_create: row.get_u64("time_add")?,
 | 
			
		||||
        following: row.get_legacy_bool("following")?,
 | 
			
		||||
        saw_last_message: row.get_legacy_bool("saw_last_message")?,
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
@@ -50,10 +50,20 @@ pub fn get_connection() -> Result<mysql::PooledConn, Box<dyn Error>> {
 | 
			
		||||
    Ok(pool.get_conn()?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Structure used to implement JOIN on queries
 | 
			
		||||
struct QueryJoin {
 | 
			
		||||
    table: String,
 | 
			
		||||
    table_alias: String,
 | 
			
		||||
    condition: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct QueryInfo {
 | 
			
		||||
    /// Fetched table
 | 
			
		||||
    pub table: String,
 | 
			
		||||
    pub table_alias: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Joins
 | 
			
		||||
    joins: Vec<QueryJoin>,
 | 
			
		||||
 | 
			
		||||
    /// Query limits
 | 
			
		||||
    pub conditions: collections::HashMap<String, String>,
 | 
			
		||||
@@ -61,6 +71,9 @@ pub struct QueryInfo {
 | 
			
		||||
    /// Limit of the query (0 = no limit)
 | 
			
		||||
    pub limit: u64,
 | 
			
		||||
 | 
			
		||||
    /// Order of the query
 | 
			
		||||
    pub order: Option<String>,
 | 
			
		||||
 | 
			
		||||
    /// Queried arguments
 | 
			
		||||
    ///
 | 
			
		||||
    /// If this attribute is empty, all the columns of the table set are fetched
 | 
			
		||||
@@ -72,12 +85,30 @@ impl QueryInfo {
 | 
			
		||||
    pub fn new(table: &str) -> QueryInfo {
 | 
			
		||||
        QueryInfo {
 | 
			
		||||
            table: table.to_string(),
 | 
			
		||||
            table_alias: None,
 | 
			
		||||
            joins: Vec::new(),
 | 
			
		||||
            conditions: collections::HashMap::new(),
 | 
			
		||||
            limit: 0,
 | 
			
		||||
            order: None,
 | 
			
		||||
            fields: Vec::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Main fetched table alias
 | 
			
		||||
    pub fn alias(mut self, table_alias: &str) -> QueryInfo {
 | 
			
		||||
        self.table_alias = Some(table_alias.to_string());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn join(mut self, table: &str, table_alias: &str, cond: &str) -> QueryInfo {
 | 
			
		||||
        self.joins.push(QueryJoin {
 | 
			
		||||
            table: table.to_string(),
 | 
			
		||||
            table_alias: table_alias.to_string(),
 | 
			
		||||
            condition: cond.to_string(),
 | 
			
		||||
        });
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn cond(mut self, key: &str, val: &str) -> QueryInfo {
 | 
			
		||||
        self.conditions.insert(key.to_string(), val.to_string());
 | 
			
		||||
        self
 | 
			
		||||
@@ -109,6 +140,12 @@ impl QueryInfo {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set results ordering
 | 
			
		||||
    pub fn set_order(mut self, order: &str) -> QueryInfo {
 | 
			
		||||
        self.order = Some(order.to_string());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Execute query
 | 
			
		||||
    pub fn exec<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(self, process_function: F)
 | 
			
		||||
                                                             -> Result<Vec<E>, Box<dyn Error>> {
 | 
			
		||||
@@ -187,6 +224,11 @@ impl<'a> RowResult<'a> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the ID of a user included in the request
 | 
			
		||||
    pub fn get_user_id(&self, name: &str) -> ResultBoxError<UserID> {
 | 
			
		||||
        self.get_int64(name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Find a string included in the request
 | 
			
		||||
    pub fn get_str(&self, name: &str) -> Result<String, Box<dyn Error>> {
 | 
			
		||||
        let value = self.row.get_opt(self.find_col(name)?);
 | 
			
		||||
@@ -273,6 +315,17 @@ pub fn query<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(info: QueryInfo, proce
 | 
			
		||||
    // Build query
 | 
			
		||||
    let mut query = format!("SELECT {} FROM {} ", select_elements, info.table);
 | 
			
		||||
 | 
			
		||||
    // Table alias (if any)
 | 
			
		||||
    if let Some(alias) = info.table_alias {
 | 
			
		||||
        query = query.add(format!(" {} ", alias).as_str());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Join conditions
 | 
			
		||||
    for j in info.joins {
 | 
			
		||||
        query = query.add(
 | 
			
		||||
            format!(" JOIN {} {} ON {} ", j.table, j.table_alias, j.condition).as_ref());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // WHERE clause
 | 
			
		||||
    if !info.conditions.is_empty() {
 | 
			
		||||
@@ -287,6 +340,11 @@ pub fn query<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(info: QueryInfo, proce
 | 
			
		||||
        query = query.add(&where_args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ORDER clause
 | 
			
		||||
    if let Some(order) = info.order {
 | 
			
		||||
        query = query.add(format!(" ORDER BY {} ", order).as_str());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // LIMIT clause
 | 
			
		||||
    if info.limit > 0 {
 | 
			
		||||
        query = query.add(&format!(" LIMIT {}", info.limit));
 | 
			
		||||
@@ -294,7 +352,10 @@ pub fn query<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(info: QueryInfo, proce
 | 
			
		||||
 | 
			
		||||
    // Execute query
 | 
			
		||||
    let mut con = get_connection()?;
 | 
			
		||||
    let stmt = con.prep(&query)?;
 | 
			
		||||
    let stmt = con.prep(&query).or_else(|err| {
 | 
			
		||||
        println!("Error in SQL query: {}", &query);
 | 
			
		||||
        Err(err)
 | 
			
		||||
    })?;
 | 
			
		||||
 | 
			
		||||
    let mut res = con.exec_iter(stmt, params)?;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user