[fractal/backend: 3/9] backend: Message initial db model



commit d81c434bb2b73945bf69290f89996f3100ff8267
Author: Daniel García Moreno <danigm wadobo com>
Date:   Thu Dec 13 16:49:56 2018 +0100

    backend: Message initial db model

 Cargo.lock                           |   2 +
 fractal-backend/Cargo.toml           |   2 +
 fractal-backend/src/lib.rs           |   2 +
 fractal-backend/src/model/message.rs | 153 +++++++++++++++++++++++++++++++++++
 fractal-backend/src/model/mod.rs     |   3 +
 fractal-backend/tests/models.rs      |  23 ++++++
 6 files changed, 185 insertions(+)
---
diff --git a/Cargo.lock b/Cargo.lock
index 9d16ba1e..99cf195b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -464,10 +464,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index";
 name = "fractal-backend"
 version = "0.1.0"
 dependencies = [
+ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "fractal-matrix-api 4.0.0",
  "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rusqlite 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
diff --git a/fractal-backend/Cargo.toml b/fractal-backend/Cargo.toml
index 7d7d502c..9f5e8bc0 100644
--- a/fractal-backend/Cargo.toml
+++ b/fractal-backend/Cargo.toml
@@ -7,6 +7,8 @@ authors = ["Daniel García Moreno <danigm wadobo com>"]
 rusqlite = "0.15.0"
 failure = "0.1.3"
 lazy_static = "1.2.0"
+serde_json = "1.0.33"
+chrono = "0.4.6"
 
 [dependencies.fractal-matrix-api]
 path = "../fractal-matrix-api"
diff --git a/fractal-backend/src/lib.rs b/fractal-backend/src/lib.rs
index 75d86d2a..41673c8e 100644
--- a/fractal-backend/src/lib.rs
+++ b/fractal-backend/src/lib.rs
@@ -1,6 +1,8 @@
+extern crate chrono;
 extern crate failure;
 extern crate fractal_matrix_api as api;
 extern crate rusqlite;
+extern crate serde_json;
 
 #[macro_use]
 extern crate lazy_static;
diff --git a/fractal-backend/src/model/message.rs b/fractal-backend/src/model/message.rs
new file mode 100644
index 00000000..1ece037e
--- /dev/null
+++ b/fractal-backend/src/model/message.rs
@@ -0,0 +1,153 @@
+use std::collections::HashMap;
+
+pub use api::types::Message;
+use failure::err_msg;
+use failure::Error;
+
+use rusqlite::types::ToSql;
+use rusqlite::Row;
+
+use chrono::DateTime;
+use chrono::Local;
+
+use serde_json;
+use serde_json::Value;
+
+use super::conn;
+use super::Model;
+
+impl Model for Message {
+    fn table_name() -> &'static str {
+        "message"
+    }
+
+    // TODO: we need a non optional id here
+    fn get_id(&self) -> &str {
+        match self.id.as_ref() {
+            Some(r) => r,
+            None => "",
+        }
+    }
+
+    fn fields() -> Vec<&'static str> {
+        vec![
+            "sender",
+            "mtype",
+            "body",
+            "date",
+            "room",
+            "thumb",
+            "url",
+            "id",
+            "formatted_body",
+            "format",
+            "source",
+            "receipt",
+            "redacted",
+            "in_reply_to",
+            "extra_content",
+        ]
+    }
+
+    fn create_sql() -> String {
+        //TODO: implements relation to room as ForeignKey
+        format!(
+            "
+        CREATE TABLE if not exists {} (
+            sender TEXT NOT NULL,
+            mtype TEXT NOT NULL,
+            body TEXT NOT NULL,
+            date TEXT NOT NULL,
+            room TEXT NOT NULL,
+            thumb TEXT,
+            url TEXT,
+            id TEXT PRIMARY KEY,
+            formatted_body TEXT,
+            format TEXT,
+            source TEXT,
+            receipt TEXT NOT NULL,
+            redacted BOOLEAN NOT NULL,
+            in_reply_to TEXT,
+            extra_content TEXT
+        )
+        ",
+            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
+        );
+
+        //TODO: maybe we should add a new table for this?
+        let receipt_serialized = serde_json::to_string(&self.receipt)?;
+        let date_serialized = serde_json::to_string(&self.date)?;
+        let extra_content = serde_json::to_string(&self.extra_content)?;
+
+        conn(
+            move |c| {
+                c.execute(
+                    &query,
+                    &[
+                        &self.sender,
+                        &self.mtype,
+                        &self.body,
+                        &date_serialized,
+                        &self.room,
+                        &self.thumb as &ToSql,
+                        &self.url,
+                        &self.id,
+                        &self.formatted_body,
+                        &self.format,
+                        &self.source,
+                        &receipt_serialized,
+                        &self.redacted,
+                        &self.in_reply_to,
+                        &extra_content,
+                    ],
+                )
+                .map(|_| ())
+                .map_err(|e| err_msg(e.to_string()))
+            },
+            Err(err_msg("Connection not init")),
+        )
+    }
+
+    fn map_row(row: &Row) -> Self {
+        let dstr: String = row.get(3);
+        let rstr: String = row.get(11);
+        let ecstr: String = row.get(14);
+
+        let date: DateTime<Local> = serde_json::from_str(&dstr).unwrap();
+        let receipt: HashMap<String, i64> = serde_json::from_str(&rstr).unwrap();
+        let extra_content: Option<Value> = serde_json::from_str(&ecstr).unwrap();
+
+        Self {
+            sender: row.get(0),
+            mtype: row.get(1),
+            body: row.get(2),
+            date: date,
+            room: row.get(4),
+            thumb: row.get(5),
+            url: row.get(6),
+            id: row.get(7),
+            formatted_body: row.get(8),
+            format: row.get(9),
+            source: row.get(10),
+            receipt: receipt,
+            redacted: row.get(12),
+            in_reply_to: row.get(13),
+            extra_content: extra_content,
+        }
+    }
+}
diff --git a/fractal-backend/src/model/mod.rs b/fractal-backend/src/model/mod.rs
index d3137fdd..22ba84a4 100644
--- a/fractal-backend/src/model/mod.rs
+++ b/fractal-backend/src/model/mod.rs
@@ -4,7 +4,10 @@ use failure::Error;
 use rusqlite::Row;
 use rusqlite::NO_PARAMS;
 
+pub mod message;
 pub mod room;
+
+pub use self::message::Message;
 pub use self::room::Room;
 
 pub trait Model: Sized {
diff --git a/fractal-backend/tests/models.rs b/fractal-backend/tests/models.rs
index 325e15ee..462eb2a3 100644
--- a/fractal-backend/tests/models.rs
+++ b/fractal-backend/tests/models.rs
@@ -1,6 +1,7 @@
 extern crate fractal_backend;
 
 use fractal_backend::init;
+use fractal_backend::model::Message;
 use fractal_backend::model::Model;
 use fractal_backend::model::Room;
 use std::fs::remove_file;
@@ -37,3 +38,25 @@ fn room_model() {
         assert_eq!(r.id, format!("ROOM {}", i));
     }
 }
+
+#[test]
+fn message_model() {
+    let _ = remove_file("/tmp/db.sqlite3");
+    let _ = init("/tmp/db.sqlite3").unwrap();
+
+    let mut msg = Message::default();
+    msg.id = Some("MSGID".to_string());
+    let created = Message::create_table();
+    assert!(created.is_ok());
+    let stored = msg.store();
+    assert!(stored.is_ok());
+
+    let newm = Message::get("MSGID").unwrap();
+    assert_eq!(msg, newm);
+
+    let deleted = msg.delete();
+    assert!(deleted.is_ok());
+
+    let really_deleted = Message::get("MSGID");
+    assert!(really_deleted.is_err());
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]