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

Implement presence mechanismes

This commit is contained in:
Pierre HUBERT 2021-04-21 15:21:29 +02:00
parent 82bb23640a
commit 2f545574fa
11 changed files with 269 additions and 10 deletions

View File

@ -261,4 +261,13 @@ CREATE TABLE `comunic_custom_emojis` (
`user_id` INT NULL, `user_id` INT NULL,
`shortcut` VARCHAR(45) NULL, `shortcut` VARCHAR(45) NULL,
`path` VARCHAR(255) NULL, `path` VARCHAR(255) NULL,
PRIMARY KEY (`id`)); PRIMARY KEY (`id`));
CREATE TABLE `forez_presence` (
`id` INT NOT NULL AUTO_INCREMENT,
`user_id` INT NULL,
`group_id` INT NULL,
`year` INT NULL,
`month` INT NULL,
`day` INT NULL,
PRIMARY KEY (`id`));

View File

@ -1,4 +1,12 @@
-- Nothing yet
ALTER TABLE `utilisateurs` ALTER TABLE `utilisateurs`
ADD COLUMN `is_email_public` INT NULL DEFAULT 0 AFTER `allow_notif_sound`, ADD COLUMN `is_email_public` INT NULL DEFAULT 0 AFTER `allow_notif_sound`,
ADD COLUMN `location` VARCHAR(45) NULL AFTER `is_email_visible`; ADD COLUMN `location` VARCHAR(45) NULL AFTER `is_email_visible`;
CREATE TABLE `comunic`.`forez_presence` (
`id` INT NOT NULL AUTO_INCREMENT,
`user_id` INT NULL,
`group_id` INT NULL,
`year` INT NULL,
`month` INT NULL,
`day` INT NULL,
PRIMARY KEY (`id`));

View File

@ -57,6 +57,9 @@ pub mod database_tables_names {
/// Notifications table /// Notifications table
pub const NOTIFICATIONS_TABLE: &str = "comunic_notifications"; pub const NOTIFICATIONS_TABLE: &str = "comunic_notifications";
/// Forez presence table
pub const FOREZ_PRESENCE_TABLE: &str = "forez_presence";
} }
/// Push Notifications Database prefix /// Push Notifications Database prefix

View File

@ -5,12 +5,14 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::data::http_request_handler::HttpRequestHandler;
use crate::routes::RequestResult;
use crate::data::config::conf;
use crate::data::base_request_handler::BaseRequestHandler;
use crate::api_data::group_api::GroupApi; use crate::api_data::group_api::GroupApi;
use crate::helpers::groups_helper; use crate::data::base_request_handler::BaseRequestHandler;
use crate::data::config::conf;
use crate::data::http_request_handler::HttpRequestHandler;
use crate::data::presence::Presence;
use crate::data::user_ws_request_handler::UserWsRequestHandler;
use crate::helpers::{forez_presence_helper, groups_helper};
use crate::routes::RequestResult;
/// Get the list of declared Forez groups in the application /// Get the list of declared Forez groups in the application
pub fn get_list_groups(r: &mut HttpRequestHandler) -> RequestResult { pub fn get_list_groups(r: &mut HttpRequestHandler) -> RequestResult {
@ -21,4 +23,115 @@ pub fn get_list_groups(r: &mut HttpRequestHandler) -> RequestResult {
} }
r.set_response(list) r.set_response(list)
}
/// Set presence
///
/// Presences format: YYYY,MM,DD;YYYY,MM,DD;...
pub fn set_presence(r: &mut UserWsRequestHandler) -> RequestResult {
let group = r.post_forez_group("group")?;
let presences = r.post_string_opt("presence", 0, false)?;
let mut list = vec![];
for p in presences.split(";") {
if p == "" {
continue;
}
let info: Vec<&str> = p.split(",").collect();
if info.len() != 3 {
r.bad_request("Invalid presence information!".to_string())?;
}
let year = info[0].parse::<u32>()?;
let month = info[1].parse::<u16>()?;
let day = info[2].parse::<u16>()?;
if year < 2020 || year > 2100 {
r.bad_request("Invalid year specified!".to_string())?;
}
if month < 1 || month > 12 {
r.bad_request("Invalid month specified!".to_string())?;
}
if day < 1 || day > 31 {
r.bad_request("Invalid day specified!".to_string())?;
}
let presence = Presence {
id: 0,
user_id: r.user_id()?,
year,
month,
day,
};
if !list.contains(&presence) {
list.push(presence);
}
}
forez_presence_helper::update(&group, &r.user_id()?, list)?;
r.success("Presences updated.")
}
/// Get the list of presences of all the members of a group
///
/// Format: USER_ID,YYYY,MM,DD
pub fn get_list<H: BaseRequestHandler>(r: &mut H) -> RequestResult {
let group = r.post_forez_group("group")?;
let list = forez_presence_helper::get_list(&group)?;
let list = list
.iter()
.map(|p| format!("{},{},{},{}", p.user_id.id(), p.year, p.month, p.day))
.collect::<Vec<String>>();
r.set_response(list)
}
/// Add a new day of presence
pub fn add_day<H: BaseRequestHandler>(r: &mut H) -> RequestResult {
let group = r.post_forez_group("group")?;
let mut list = forez_presence_helper::get_user_presences(&group, &r.user_id()?)?;
let presence = Presence {
id: 0,
user_id: r.user_id()?,
year: r.post_u32("year")?,
month: r.post_u16("month")?,
day: r.post_u16("day")?,
};
if !list.contains(&presence) {
list.push(presence);
}
forez_presence_helper::update(&group, &r.user_id()?, list)?;
r.success("Updated presences.")
}
/// Remove a day of presence
pub fn del_day<H: BaseRequestHandler>(r: &mut H) -> RequestResult {
let group = r.post_forez_group("group")?;
let mut list = forez_presence_helper::get_user_presences(&group, &r.user_id()?)?;
let presence = Presence {
id: 0,
user_id: r.user_id()?,
year: r.post_u32("year")?,
month: r.post_u16("month")?,
day: r.post_u16("day")?,
};
list.retain(|el| el != &presence);
forez_presence_helper::update(&group, &r.user_id()?, list)?;
r.success("Updated presences.")
} }

View File

@ -14,6 +14,7 @@ use serde::Serialize;
use crate::api_data::http_error::HttpError; use crate::api_data::http_error::HttpError;
use crate::constants::PASSWORD_MIN_LENGTH; use crate::constants::PASSWORD_MIN_LENGTH;
use crate::data::comment::Comment; use crate::data::comment::Comment;
use crate::data::config::conf;
use crate::data::conversation::{ConversationMember, ConvID}; use crate::data::conversation::{ConversationMember, ConvID};
use crate::data::custom_emoji::CustomEmoji; use crate::data::custom_emoji::CustomEmoji;
use crate::data::error::{ExecError, Res, ResultBoxError}; use crate::data::error::{ExecError, Res, ResultBoxError};
@ -484,6 +485,14 @@ pub trait BaseRequestHandler {
Ok(self.post_string(name)?.parse::<u64>()?) Ok(self.post_string(name)?.parse::<u64>()?)
} }
fn post_u32(&mut self, name: &str) -> Res<u32> {
Ok(self.post_u64(name)? as u32)
}
fn post_u16(&mut self, name: &str) -> Res<u16> {
Ok(self.post_u64(name)? as u16)
}
fn post_positive_u64_opt(&mut self, name: &str) -> Res<Option<u64>> { fn post_positive_u64_opt(&mut self, name: &str) -> Res<Option<u64>> {
match self.post_u64_opt(name, 0)? { match self.post_u64_opt(name, 0)? {
0 => Ok(None), 0 => Ok(None),
@ -796,4 +805,15 @@ pub trait BaseRequestHandler {
} }
} }
} }
/// Get a membership to a Forez group
fn post_forez_group(&mut self, name: &str) -> Res<GroupID> {
let group = self.post_group_id_with_access(name, GroupAccessLevel::MEMBER_ACCESS)?;
if !conf().forez_groups.contains(&group) {
self.bad_request(format!("The group {} is not a Forez group!", group.id()))?;
}
Ok(group)
}
} }

View File

@ -39,4 +39,5 @@ pub mod user_ws_message;
pub mod user_ws_connection; pub mod user_ws_connection;
pub mod call_signal; pub mod call_signal;
pub mod new_notifications_settings; pub mod new_notifications_settings;
pub mod push_notification; pub mod push_notification;
pub mod presence;

22
src/data/presence.rs Normal file
View File

@ -0,0 +1,22 @@
//! # Presence information
//!
//! @author Pierre Hubert
use crate::data::user::UserID;
pub struct Presence {
pub id: u64,
pub user_id: UserID,
pub year: u32,
pub month: u16,
pub day: u16,
}
impl PartialEq for Presence {
fn eq(&self, other: &Self) -> bool {
self.user_id == other.user_id &&
self.year == other.year &&
self.month == other.month &&
self.day == other.day
}
}

View File

@ -350,6 +350,16 @@ impl<'a> RowResult<'a> {
} }
} }
pub fn get_u16(&self, name: &str) -> ResultBoxError<u16> {
let value = self.row.get_opt(self.find_col(name)?);
match value {
None => Err(ExecError::boxed_string(
format!("Could not extract integer field {} !", name))),
Some(s) => Ok(s?)
}
}
/// Find an integer included in the request /// Find an integer included in the request
pub fn get_usize(&self, name: &str) -> Result<usize, Box<dyn Error>> { pub fn get_usize(&self, name: &str) -> Result<usize, Box<dyn Error>> {
let value = self.row.get_opt(self.find_col(name)?); let value = self.row.get_opt(self.find_col(name)?);
@ -661,6 +671,11 @@ impl InsertQuery {
self self
} }
pub fn add_u16(mut self, key: &str, value: u16) -> InsertQuery {
self.values.insert(key.to_string(), Value::from(value));
self
}
pub fn add_user_id(mut self, key: &str, value: &UserID) -> InsertQuery { pub fn add_user_id(mut self, key: &str, value: &UserID) -> InsertQuery {
self.values.insert(key.to_string(), Value::from(value.id())); self.values.insert(key.to_string(), Value::from(value.id()));
self self

View File

@ -0,0 +1,62 @@
//! # Forez Presence helper
//!
//! @author Pierre Hubert
use crate::constants::database_tables_names::FOREZ_PRESENCE_TABLE;
use crate::data::error::Res;
use crate::data::group_id::GroupID;
use crate::data::presence::Presence;
use crate::data::user::UserID;
use crate::helpers::database;
/// Get the list of presence contained in the database
pub fn get_list(group_id: &GroupID) -> Res<Vec<Presence>> {
database::QueryInfo::new(FOREZ_PRESENCE_TABLE)
.cond_group_id("group_id", group_id)
.exec(db_to_presence)
}
/// Get the list of presences of a specific user
pub fn get_user_presences(group_id: &GroupID, user_id: &UserID) -> Res<Vec<Presence>> {
database::QueryInfo::new(FOREZ_PRESENCE_TABLE)
.cond_user_id("user_id", user_id)
.cond_group_id("group_id", group_id)
.exec(db_to_presence)
}
/// Update the presences of a user
pub fn update(group_id: &GroupID, user_id: &UserID, list: Vec<Presence>) -> Res {
let previous_presences = get_user_presences(group_id, user_id)?;
for presence in &list {
if !previous_presences.contains(presence) {
database::InsertQuery::new(FOREZ_PRESENCE_TABLE)
.add_user_id("user_id", user_id)
.add_group_id("group_id", group_id)
.add_u32("year", presence.year)
.add_u16("month", presence.month)
.add_u16("day", presence.day)
.insert()?;
}
}
for prev in &previous_presences {
if !list.contains(prev) {
database::DeleteQuery::new(FOREZ_PRESENCE_TABLE)
.cond_u64("id", prev.id)
.exec()?;
}
}
Ok(())
}
/// Turn a database entry into a presence entry
fn db_to_presence(row: &database::RowResult) -> Res<Presence> {
Ok(Presence {
id: row.get_u64("id")?,
user_id: row.get_user_id("user_id")?,
year: row.get_u32("year")?,
month: row.get_u16("month")?,
day: row.get_u16("day")?,
})
}

View File

@ -20,4 +20,5 @@ pub mod events_helper;
pub mod calls_helper; pub mod calls_helper;
pub mod push_notifications_helper; pub mod push_notifications_helper;
pub mod independent_push_notifications_service_helper; pub mod independent_push_notifications_service_helper;
pub mod firebase_notifications_helper; pub mod firebase_notifications_helper;
pub mod forez_presence_helper;

View File

@ -2,7 +2,7 @@
//! //!
//! @author Pierre Hubert //! @author Pierre Hubert
use crate::controllers::{calls_controller, conversations_controller, likes_controller, user_ws_actions}; use crate::controllers::{calls_controller, conversations_controller, forez_controller, likes_controller, user_ws_actions};
use crate::data::error::Res; use crate::data::error::Res;
use crate::data::user_ws_request_handler::UserWsRequestHandler; use crate::data::user_ws_request_handler::UserWsRequestHandler;
@ -49,6 +49,11 @@ pub fn get_user_ws_routes() -> Vec<UserWsRoute> {
UserWsRoute::new("calls/mark_ready", calls_controller::mark_user_ready), UserWsRoute::new("calls/mark_ready", calls_controller::mark_user_ready),
UserWsRoute::new("calls/request_offer", calls_controller::request_offer), UserWsRoute::new("calls/request_offer", calls_controller::request_offer),
UserWsRoute::new("calls/stop_streaming", calls_controller::stop_streaming), UserWsRoute::new("calls/stop_streaming", calls_controller::stop_streaming),
// Presence controller
UserWsRoute::new("forez_presence/list", forez_controller::get_list),
UserWsRoute::new("forez_presence/add_day", forez_controller::add_day),
UserWsRoute::new("forez_presence/del_day", forez_controller::del_day),
] ]
} }