[fractal/backend: 7/9] backend: Member model and relation with room
- From: Daniel Garcia Moreno <danigm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/backend: 7/9] backend: Member model and relation with room
- Date: Wed, 30 Jan 2019 11:53:52 +0000 (UTC)
commit e2c64f23108b4c5d87c80963ae773cdc74bdfdea
Author: Daniel GarcĂa Moreno <danigm wadobo com>
Date: Sun Jan 6 21:09:11 2019 +0100
backend: Member model and relation with room
fractal-backend/src/model/member.rs | 197 ++++++++++++++++++++++++++++++++++++
fractal-backend/src/model/mod.rs | 8 ++
fractal-backend/src/model/room.rs | 1 -
fractal-backend/tests/models.rs | 100 ++++++++++++++++++
4 files changed, 305 insertions(+), 1 deletion(-)
---
diff --git a/fractal-backend/src/model/member.rs b/fractal-backend/src/model/member.rs
new file mode 100644
index 00000000..e929df98
--- /dev/null
+++ b/fractal-backend/src/model/member.rs
@@ -0,0 +1,197 @@
+pub use api::types::Member;
+use failure::err_msg;
+use failure::Error;
+
+use rusqlite::types::ToSql;
+use rusqlite::Row;
+use rusqlite::NO_PARAMS;
+
+use super::conn;
+use super::Model;
+
+impl Model for Member {
+ fn table_name() -> &'static str {
+ "member"
+ }
+
+ fn get_id(&self) -> &str {
+ &self.uid
+ }
+
+ fn fields() -> Vec<&'static str> {
+ vec!["id", "alias", "avatar"]
+ }
+
+ fn create_sql() -> String {
+ format!(
+ "
+ CREATE TABLE if not exists {table} (
+ id TEXT PRIMARY KEY,
+ alias TEXT,
+ avatar TEXT
+ )
+ ",
+ table = Self::table_name()
+ )
+ }
+
+ fn store(&self) -> Result<(), Error> {
+ let fields = Self::fields().join(",");
+ let questions = Self::fields()
+ .iter()
+ .map(|_| "?")
+ .collect::<Vec<&str>>()
+ .join(",");
+ let query = format!(
+ "INSERT INTO {} ({}) VALUES ({})",
+ Self::table_name(),
+ fields,
+ questions
+ );
+
+ conn(
+ move |c| {
+ c.execute(&query, &[&self.uid, &self.alias as &ToSql, &self.avatar])
+ .map(|_| ())
+ .map_err(|e| err_msg(e.to_string()))
+ },
+ Err(err_msg("Connection not init")),
+ )
+ }
+
+ fn map_row(row: &Row) -> Self {
+ Self {
+ uid: row.get(0),
+ alias: row.get(1),
+ avatar: row.get(2),
+ }
+ }
+}
+
+pub trait MemberModel: Sized {
+ fn store_relation(&self, room: &str) -> Result<(), Error>;
+ fn delete_relation(&self, room: &str) -> Result<usize, Error>;
+ fn update_relation(&self, room: &str) -> Result<(), Error>;
+ fn get_range(room: &str, limit: Option<u32>, offset: Option<u32>) -> Result<Vec<Self>, Error>;
+ fn delete_relations(room: &str) -> Result<usize, Error>;
+ fn create_relation_table() -> Result<(), Error>;
+}
+
+impl MemberModel for Member {
+ fn create_relation_table() -> Result<(), Error> {
+ let query = format!(
+ "
+ CREATE TABLE if not exists {table}_room (
+ uid TEXT NOT NULL,
+ room TEXT NOT NULL,
+
+ FOREIGN KEY(room) REFERENCES room(id),
+ FOREIGN KEY(uid) REFERENCES {table}(id)
+ )
+ ",
+ table = Self::table_name()
+ );
+
+ conn(
+ move |c| {
+ c.execute(&query, NO_PARAMS)
+ .map(|_| ())
+ .map_err(|e| err_msg(e.to_string()))
+ },
+ Err(err_msg("Connection not init")),
+ )
+ }
+
+ fn store_relation(&self, room: &str) -> Result<(), Error> {
+ let query = format!(
+ "INSERT INTO {table}_room (uid, room) VALUES (?, ?)",
+ table = Self::table_name(),
+ );
+
+ conn(
+ move |c| {
+ c.execute(&query, &[&self.uid, room])
+ .map(|_| ())
+ .map_err(|e| err_msg(e.to_string()))
+ },
+ Err(err_msg("Connection not init")),
+ )
+ }
+
+ fn delete_relation(&self, room: &str) -> Result<usize, Error> {
+ let query = format!(
+ "DELETE from {table}_room WHERE uid = ? and room = ?",
+ table = Self::table_name()
+ );
+
+ conn(
+ move |c| {
+ c.execute(&query, &[self.get_id(), room])
+ .map_err(|e| err_msg(e.to_string()))
+ },
+ Err(err_msg("Connection not init")),
+ )
+ }
+
+ fn delete_relations(room: &str) -> Result<usize, Error> {
+ let query = format!(
+ "DELETE from {table}_room WHERE room = ?",
+ table = Self::table_name()
+ );
+
+ conn(
+ move |c| {
+ c.execute(&query, &[room])
+ .map_err(|e| err_msg(e.to_string()))
+ },
+ Err(err_msg("Connection not init")),
+ )
+ }
+
+ fn update_relation(&self, room: &str) -> Result<(), Error> {
+ self.delete_relation(room)?;
+ self.store_relation(room)
+ }
+
+ /// Returns a list of Members from filtering by `room` roomid ordered by
+ /// date
+ ///
+ /// The param `limit` defines the number of members to return, if it's
+ /// None, all members will be returned
+ ///
+ /// The param `offset` is used to ignore that number of members and start
+ /// to return from that. if it's None, the return will be done from the end
+ /// of the list.
+ fn get_range(room: &str, limit: Option<u32>, offset: Option<u32>) -> Result<Vec<Self>, Error> {
+ let fields = Self::fields().join(",");
+ let mut query = format!(
+ "SELECT {fields} FROM {table} INNER JOIN
+ {table}_room ON uid=id
+ WHERE room = ? ORDER BY uid desc",
+ fields = fields,
+ table = Self::table_name()
+ );
+
+ if let Some(l) = limit {
+ query = query + &format!(" LIMIT {}", l);
+ }
+
+ if let Some(o) = offset {
+ query = query + &format!(" OFFSET {}", o);
+ }
+
+ conn(
+ move |c| {
+ let mut stmt = c.prepare(&query)?;
+ let iter = stmt.query_map(&[room], Self::map_row)?;
+
+ let array = iter
+ .filter(|r| r.is_ok())
+ .map(|r| r.unwrap())
+ .collect::<Vec<Self>>();
+ Ok(array)
+ },
+ Err(err_msg("Connection not init")),
+ )
+ }
+}
diff --git a/fractal-backend/src/model/mod.rs b/fractal-backend/src/model/mod.rs
index 2864fd87..f1c6bef7 100644
--- a/fractal-backend/src/model/mod.rs
+++ b/fractal-backend/src/model/mod.rs
@@ -4,9 +4,12 @@ use failure::Error;
use rusqlite::Row;
use rusqlite::NO_PARAMS;
+pub mod member;
pub mod message;
pub mod room;
+pub use self::member::Member;
+pub use self::member::MemberModel;
pub use self::message::Message;
pub use self::message::MessageModel;
pub use self::room::Room;
@@ -66,6 +69,11 @@ pub trait Model: Sized {
)
}
+ fn update(&self) -> Result<(), Error> {
+ self.delete()?;
+ self.store()
+ }
+
fn create_table() -> Result<usize, Error> {
conn(
move |c| {
diff --git a/fractal-backend/src/model/room.rs b/fractal-backend/src/model/room.rs
index 77171596..4b389bd5 100644
--- a/fractal-backend/src/model/room.rs
+++ b/fractal-backend/src/model/room.rs
@@ -44,7 +44,6 @@ impl Model for Room {
fn create_sql() -> String {
//TODO: implements relations for:
- // members: MemberList,
// inv_sender: Option<Member>,
format!(
"
diff --git a/fractal-backend/tests/models.rs b/fractal-backend/tests/models.rs
index 4289f088..684b41f9 100644
--- a/fractal-backend/tests/models.rs
+++ b/fractal-backend/tests/models.rs
@@ -2,6 +2,8 @@ extern crate chrono;
extern crate fractal_backend;
use fractal_backend::init_local as init;
+use fractal_backend::model::Member;
+use fractal_backend::model::MemberModel;
use fractal_backend::model::Message;
use fractal_backend::model::MessageModel;
use fractal_backend::model::Model;
@@ -107,3 +109,101 @@ fn message_room_relation() {
let items = Message::get_range(&r.id, Some(10), Some(100)).unwrap();
assert_eq!(items.len(), 0);
}
+
+#[test]
+fn member_model() {
+ let _ = init("").unwrap();
+
+ assert!(Room::create_table().is_ok());
+ let created = Member::create_table();
+ assert!(created.is_ok());
+
+ let m1 = Member {
+ uid: String::from("UID"),
+ alias: None,
+ avatar: None,
+ };
+ let m2 = Member {
+ uid: String::from("UID2"),
+ alias: None,
+ avatar: None,
+ };
+ let m3 = Member {
+ uid: String::from("UID3"),
+ alias: None,
+ avatar: None,
+ };
+ assert!(m1.store().is_ok());
+ assert!(m2.store().is_ok());
+ assert!(m3.store().is_ok());
+
+ let newm = Member::get("UID").unwrap();
+ assert_eq!(m1, newm);
+
+ let deleted = m1.delete();
+ assert!(deleted.is_ok());
+
+ let really_deleted = Member::get("UID");
+ assert!(really_deleted.is_err());
+}
+
+#[test]
+fn member_room_relation() {
+ let _ = init("").unwrap();
+
+ let created = Room::create_table();
+ assert!(created.is_ok());
+ let created = Member::create_table();
+ assert!(created.is_ok());
+ assert!(Member::create_relation_table().is_ok());
+
+ let r = Room::new("ROOM ID".to_string(), Some("ROOM NAME".to_string()));
+ let stored = r.store();
+ assert!(stored.is_ok());
+
+ let mut m = Member {
+ uid: String::from("UID"),
+ alias: None,
+ avatar: None,
+ };
+
+ for i in 0..100 {
+ m.uid = format!("USER {:04}", i);
+ assert!(m.store().is_ok());
+ assert!(m.store_relation("ROOM ID").is_ok());
+ }
+
+ for i in 0..100 {
+ m.uid = format!("USER ROOM2 {:04}", i);
+ assert!(m.store().is_ok());
+ assert!(m.store_relation("ROOM ID 2").is_ok());
+ }
+
+ for i in 0..10 {
+ let items = Member::get_range(&r.id, Some(10), Some(i * 10)).unwrap();
+ for (j, m) in items.iter().enumerate() {
+ let idx = 99 - (10 * i as usize + j);
+ assert_eq!(m.uid, format!("USER {:04}", idx));
+ }
+ }
+
+ let items = Member::get_range(&r.id, Some(10), Some(95)).unwrap();
+ assert_eq!(items.len(), 5);
+
+ let items = Member::get_range(&r.id, Some(10), Some(100)).unwrap();
+ assert_eq!(items.len(), 0);
+
+ let items = Member::get_range("ROOM ID 2", Some(10), None).unwrap();
+ assert_eq!(items.len(), 10);
+ for m in items {
+ assert!(m.delete_relation("ROOM ID 2").is_ok());
+ }
+
+ assert_eq!(
+ 90,
+ Member::get_range("ROOM ID 2", None, None).unwrap().len()
+ );
+ assert!(Member::delete_relations("ROOM ID 2").is_ok());
+ assert_eq!(0, Member::get_range("ROOM ID 2", None, None).unwrap().len());
+ assert_eq!(100, Member::get_range("ROOM ID", None, None).unwrap().len());
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]