[fractal] API, directory: Separate endpoint connection from query build



commit 962143c95d749cb2dbbb9bf57dd6ec210dade448
Author: Alejandro Domínguez <adomu net-c com>
Date:   Fri Feb 1 23:01:06 2019 +0100

    API, directory: Separate endpoint connection from query build

 fractal-gtk/src/appop/directory.rs                 |   2 +-
 fractal-matrix-api/src/backend/directory.rs        | 178 ++++++++++++---------
 fractal-matrix-api/src/backend/mod.rs              |   4 +-
 fractal-matrix-api/src/backend/types.rs            |   2 +-
 fractal-matrix-api/src/lib.rs                      |   2 +
 fractal-matrix-api/src/meson.build                 |   7 +-
 fractal-matrix-api/src/model/mod.rs                |   1 -
 fractal-matrix-api/src/model/room.rs               |  56 +------
 fractal-matrix-api/src/r0.rs                       |   2 +
 fractal-matrix-api/src/r0/directory.rs             |   1 +
 .../src/r0/directory/post_public_rooms.rs          |  80 +++++++++
 fractal-matrix-api/src/r0/thirdparty.rs            |   1 +
 .../thirdparty/get_supported_protocols.rs}         |  22 ++-
 fractal-matrix-api/src/ser.rs                      |  12 ++
 fractal-matrix-api/src/types.rs                    |   6 -
 fractal-matrix-api/src/util.rs                     |   2 +-
 16 files changed, 228 insertions(+), 150 deletions(-)
---
diff --git a/fractal-gtk/src/appop/directory.rs b/fractal-gtk/src/appop/directory.rs
index 733fd2ef..79f982d0 100644
--- a/fractal-gtk/src/appop/directory.rs
+++ b/fractal-gtk/src/appop/directory.rs
@@ -7,8 +7,8 @@ use crate::appop::AppOp;
 use crate::backend::BKCommand;
 use crate::widgets;
 
-use crate::types::ProtocolInstance;
 use crate::types::Room;
+use fractal_api::r0::thirdparty::get_supported_protocols::ProtocolInstance;
 
 impl AppOp {
     pub fn init_protocols(&self) {
diff --git a/fractal-matrix-api/src/backend/directory.rs b/fractal-matrix-api/src/backend/directory.rs
index 99ce826e..b54b8cda 100644
--- a/fractal-matrix-api/src/backend/directory.rs
+++ b/fractal-matrix-api/src/backend/directory.rs
@@ -1,6 +1,4 @@
-use serde_json::json;
-use serde_json::Value as JsonValue;
-use url::Url;
+use url::{Host, Url};
 
 use crate::globals;
 
@@ -10,45 +8,52 @@ use crate::error::Error;
 use std::thread;
 
 use crate::util::cache_path;
-use crate::util::json_q;
 use crate::util::media;
-
-use crate::types::PublicRoomsFilter;
-use crate::types::PublicRoomsRequest;
-use crate::types::PublicRoomsResponse;
+use crate::util::HTTP_CLIENT;
+
+use crate::r0::directory::post_public_rooms::request as post_public_rooms;
+use crate::r0::directory::post_public_rooms::Body as PublicRoomsBody;
+use crate::r0::directory::post_public_rooms::Filter as PublicRoomsFilter;
+use crate::r0::directory::post_public_rooms::Parameters as PublicRoomsParameters;
+use crate::r0::directory::post_public_rooms::Response as PublicRoomsResponse;
+use crate::r0::directory::post_public_rooms::ThirdPartyNetworks;
+use crate::r0::thirdparty::get_supported_protocols::request as get_supported_protocols;
+use crate::r0::thirdparty::get_supported_protocols::Parameters as SupportedProtocolsParameters;
+use crate::r0::thirdparty::get_supported_protocols::Response as SupportedProtocolsResponse;
 use crate::types::Room;
-use crate::types::SupportedProtocols;
-use crate::types::ThirdPartyNetworks;
 
 pub fn protocols(bk: &Backend) {
-    let baseu = bk.get_base_url();
-    let tk = bk.data.lock().unwrap().access_token.clone();
-    let mut url = baseu
-        .join("/_matrix/client/r0/thirdparty/protocols")
-        .expect("Wrong URL in protocols()");
-    url.query_pairs_mut()
-        .clear()
-        .append_pair("access_token", &tk);
-
     let tx = bk.tx.clone();
-    get!(
-        &url,
-        move |r: JsonValue| {
-            let protocols = serde_json::from_value(r)
-                .map(|protocols: SupportedProtocols| {
-                    protocols
-                        .into_iter()
-                        .flat_map(|(_, protocol)| protocol.instances.into_iter())
-                        .collect()
-                })
-                .unwrap_or_default();
-
-            tx.send(BKResponse::DirectoryProtocols(protocols)).unwrap();
-        },
-        |err| {
-            tx.send(BKResponse::DirectoryError(err)).unwrap();
+    let access_token = bk.data.lock().unwrap().access_token.clone();
+
+    let base = bk.get_base_url();
+    let params = SupportedProtocolsParameters { access_token };
+
+    thread::spawn(move || {
+        let query = get_supported_protocols(base, &params)
+            .map_err(Into::into)
+            .and_then(|request| {
+                HTTP_CLIENT
+                    .get_client()?
+                    .execute(request)?
+                    .json::<SupportedProtocolsResponse>()
+                    .map_err(Into::into)
+            });
+
+        match query {
+            Ok(response) => {
+                let protocols = response
+                    .into_iter()
+                    .flat_map(|(_, protocol)| protocol.instances.into_iter())
+                    .collect();
+
+                let _ = tx.send(BKResponse::DirectoryProtocols(protocols));
+            }
+            Err(err) => {
+                let _ = tx.send(BKResponse::DirectoryError(err));
+            }
         }
-    );
+    });
 }
 
 pub fn room_search(
@@ -58,69 +63,84 @@ pub fn room_search(
     third_party: Option<String>,
     more: bool,
 ) -> Result<(), Error> {
-    let mut params: Vec<(&str, String)> = Vec::new();
-
-    if let Some(mut hs) = homeserver {
-        // Extract the hostname if `homeserver` is an URL
-        if let Ok(homeserver_url) = Url::parse(&hs) {
-            hs = homeserver_url.host_str().unwrap_or_default().to_string();
-        }
+    let tx = bk.tx.clone();
+    let data = bk.data.clone();
 
-        params.push(("server", hs));
-    }
+    // TODO: use transpose() when it is stabilized
+    let server = homeserver
+        .map(|hs| {
+            Url::parse(&hs)
+                .ok()
+                .as_ref()
+                .and_then(Url::host)
+                .as_ref()
+                .map(Host::to_owned)
+                .map(Ok)
+                .unwrap_or(Host::parse(&hs))
+                .map(Some)
+        })
+        .unwrap_or(Ok(None))?;
 
-    let url = bk.url("publicRooms", params)?;
     let base = bk.get_base_url();
+    let access_token = data.lock().unwrap().access_token.clone();
 
     let since = if more {
-        Some(bk.data.lock().unwrap().rooms_since.clone())
+        Some(data.lock().unwrap().rooms_since.clone())
     } else {
         None
     };
 
-    let request = PublicRoomsRequest {
+    let params = PublicRoomsParameters {
+        access_token,
+        server,
+    };
+
+    let body = PublicRoomsBody {
         limit: Some(globals::ROOM_DIRECTORY_LIMIT),
         filter: Some(PublicRoomsFilter {
             generic_search_term,
         }),
         since,
         third_party_networks: third_party
-            .map(|tp| ThirdPartyNetworks::Only(tp))
+            .map(ThirdPartyNetworks::Only)
             .unwrap_or_default(),
     };
 
-    let attrs = serde_json::to_value(request).expect("Failed to serialize the search request");
-
-    let tx = bk.tx.clone();
-    let data = bk.data.clone();
-    post!(
-        &url,
-        &attrs,
-        move |r: JsonValue| {
-            let rooms = serde_json::from_value(r)
-                .map(|pr: PublicRoomsResponse| {
-                    data.lock().unwrap().rooms_since = pr.next_batch.unwrap_or_default();
-
-                    pr.chunk
-                        .into_iter()
-                        .map(Into::into)
-                        .inspect(|r: &Room| {
-                            if let Some(avatar) = r.avatar.clone() {
-                                if let Ok(dest) = cache_path(&r.id) {
-                                    media(&base.clone(), &avatar, Some(&dest)).unwrap_or_default();
-                                }
+    thread::spawn(move || {
+        let query = post_public_rooms(base.clone(), &params, &body)
+            .map_err(Into::into)
+            .and_then(|request| {
+                HTTP_CLIENT
+                    .get_client()?
+                    .execute(request)?
+                    .json::<PublicRoomsResponse>()
+                    .map_err(Into::into)
+            });
+
+        match query {
+            Ok(response) => {
+                data.lock().unwrap().rooms_since = response.next_batch.unwrap_or_default();
+
+                let rooms = response
+                    .chunk
+                    .into_iter()
+                    .map(Into::into)
+                    .inspect(|r: &Room| {
+                        if let Some(avatar) = r.avatar.clone() {
+                            if let Ok(dest) = cache_path(&r.id) {
+                                let _ = media(&base, &avatar, Some(&dest));
                             }
-                        })
-                        .collect()
-                })
-                .unwrap_or_default();
-
-            tx.send(BKResponse::DirectorySearch(rooms)).unwrap();
-        },
-        |err| {
-            tx.send(BKResponse::DirectoryError(err)).unwrap();
+                        }
+                    })
+                    .collect();
+
+                let _ = tx.send(BKResponse::DirectorySearch(rooms));
+            }
+            Err(err) => {
+                let _ = tx.send(BKResponse::DirectoryError(err));
+            }
         }
-    );
+    });
 
     Ok(())
 }
diff --git a/fractal-matrix-api/src/backend/mod.rs b/fractal-matrix-api/src/backend/mod.rs
index 78137ae7..39c6c80d 100644
--- a/fractal-matrix-api/src/backend/mod.rs
+++ b/fractal-matrix-api/src/backend/mod.rs
@@ -303,9 +303,7 @@ impl Backend {
             }
 
             // Directory module
-            Ok(BKCommand::DirectoryProtocols) => {
-                directory::protocols(self);
-            }
+            Ok(BKCommand::DirectoryProtocols) => directory::protocols(self),
             Ok(BKCommand::DirectorySearch(dhs, dq, dtp, more)) => {
                 let hs = match dhs {
                     ref a if a.is_empty() => None,
diff --git a/fractal-matrix-api/src/backend/types.rs b/fractal-matrix-api/src/backend/types.rs
index 76855c36..2b7f6704 100644
--- a/fractal-matrix-api/src/backend/types.rs
+++ b/fractal-matrix-api/src/backend/types.rs
@@ -4,11 +4,11 @@ use std::sync::{Arc, Condvar, Mutex};
 
 use crate::error::Error;
 
+use crate::r0::thirdparty::get_supported_protocols::ProtocolInstance;
 use crate::types::Event;
 use crate::types::Medium;
 use crate::types::Member;
 use crate::types::Message;
-use crate::types::ProtocolInstance;
 use crate::types::Room;
 use crate::types::Sticker;
 use crate::types::StickerGroup;
diff --git a/fractal-matrix-api/src/lib.rs b/fractal-matrix-api/src/lib.rs
index a302385b..8f600d45 100644
--- a/fractal-matrix-api/src/lib.rs
+++ b/fractal-matrix-api/src/lib.rs
@@ -7,6 +7,8 @@ pub mod backend;
 pub mod cache;
 mod client;
 mod model;
+pub mod r0;
+mod ser;
 pub mod types;
 
 #[cfg(test)]
diff --git a/fractal-matrix-api/src/meson.build b/fractal-matrix-api/src/meson.build
index 8e6eea47..958b39b7 100644
--- a/fractal-matrix-api/src/meson.build
+++ b/fractal-matrix-api/src/meson.build
@@ -14,17 +14,22 @@ api_sources = files(
   'model/member.rs',
   'model/message.rs',
   'model/mod.rs',
-  'model/protocol.rs',
   'model/register.rs',
   'model/room.rs',
   'model/stickers.rs',
   'model/sync.rs',
   'model/user.rs',
+  'r0/directory/post_public_rooms.rs',
+  'r0/thirdparty/get_supported_protocols.rs',
+  'r0/directory.rs',
+  'r0/thirdparty.rs',
   'cache.rs',
   'client.rs',
   'error.rs',
   'globals.rs',
   'lib.rs',
+  'r0.rs',
+  'ser.rs',
   'types.rs',
   'util.rs'
 )
diff --git a/fractal-matrix-api/src/model/mod.rs b/fractal-matrix-api/src/model/mod.rs
index 758a0e27..0a005e21 100644
--- a/fractal-matrix-api/src/model/mod.rs
+++ b/fractal-matrix-api/src/model/mod.rs
@@ -3,7 +3,6 @@ pub mod fileinfo;
 pub mod filter;
 pub mod member;
 pub mod message;
-pub mod protocol;
 pub mod register;
 pub mod room;
 pub mod stickers;
diff --git a/fractal-matrix-api/src/model/room.rs b/fractal-matrix-api/src/model/room.rs
index 6c30afaf..6e52517f 100644
--- a/fractal-matrix-api/src/model/room.rs
+++ b/fractal-matrix-api/src/model/room.rs
@@ -3,6 +3,7 @@ use serde_json::Value as JsonValue;
 use crate::model::member::Member;
 use crate::model::member::MemberList;
 use crate::model::message::Message;
+use crate::r0::directory::post_public_rooms::Chunk as PublicRoomsChunk;
 use crate::types::SyncResponse;
 use crate::util::get_user_avatar;
 use crate::util::parse_m_direct;
@@ -297,61 +298,6 @@ impl PartialEq for Room {
 
 pub type RoomList = HashMap<String, Room>;
 
-#[derive(Clone, Debug, Serialize)]
-pub struct PublicRoomsRequest {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub limit: Option<i32>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub since: Option<String>,
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub filter: Option<PublicRoomsFilter>,
-    #[serde(flatten)]
-    pub third_party_networks: ThirdPartyNetworks,
-}
-
-#[derive(Clone, Debug, Serialize)]
-pub struct PublicRoomsFilter {
-    pub generic_search_term: Option<String>,
-}
-
-#[derive(Clone, Debug, Serialize)]
-#[serde(tag = "include_all_networks", content = "third_party_instance_id")]
-pub enum ThirdPartyNetworks {
-    #[serde(rename = "false")]
-    None,
-    #[serde(rename = "false")]
-    Only(String),
-    #[serde(rename = "true")]
-    All,
-}
-
-impl Default for ThirdPartyNetworks {
-    fn default() -> Self {
-        ThirdPartyNetworks::None
-    }
-}
-
-#[derive(Clone, Debug, Deserialize)]
-pub struct PublicRoomsResponse {
-    pub chunk: Vec<PublicRoomsChunk>,
-    pub next_batch: Option<String>,
-    pub prev_batch: Option<String>,
-    pub total_room_count_estimate: Option<u64>,
-}
-
-#[derive(Clone, Debug, Deserialize)]
-pub struct PublicRoomsChunk {
-    pub aliases: Option<Vec<String>>,
-    pub avatar_url: Option<String>,
-    pub canonical_alias: Option<String>,
-    pub guest_can_join: bool,
-    pub name: Option<String>,
-    pub num_joined_members: i32,
-    pub room_id: String,
-    pub topic: Option<String>,
-    pub world_readable: bool,
-}
-
 fn evc(events: &Vec<JsonValue>, t: &str, field: &str) -> Option<String> {
     events
         .iter()
diff --git a/fractal-matrix-api/src/r0.rs b/fractal-matrix-api/src/r0.rs
new file mode 100644
index 00000000..d87d88df
--- /dev/null
+++ b/fractal-matrix-api/src/r0.rs
@@ -0,0 +1,2 @@
+pub mod directory;
+pub mod thirdparty;
diff --git a/fractal-matrix-api/src/r0/directory.rs b/fractal-matrix-api/src/r0/directory.rs
new file mode 100644
index 00000000..b7b38fa4
--- /dev/null
+++ b/fractal-matrix-api/src/r0/directory.rs
@@ -0,0 +1 @@
+pub mod post_public_rooms;
diff --git a/fractal-matrix-api/src/r0/directory/post_public_rooms.rs 
b/fractal-matrix-api/src/r0/directory/post_public_rooms.rs
new file mode 100644
index 00000000..145b7acb
--- /dev/null
+++ b/fractal-matrix-api/src/r0/directory/post_public_rooms.rs
@@ -0,0 +1,80 @@
+use crate::ser::serialize_option_host;
+use reqwest::Client;
+use reqwest::Error;
+use reqwest::Request;
+use serde::{Deserialize, Serialize};
+use url::Host;
+use url::Url;
+
+#[derive(Clone, Debug, Serialize)]
+pub struct Parameters {
+    pub access_token: String,
+    #[serde(serialize_with = "serialize_option_host")]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub server: Option<Host<String>>,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct Body {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub limit: Option<i32>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub since: Option<String>,
+    // This field doesn't follow the spec but for some reason
+    // it fails with matrix.org if it's not set this way
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub filter: Option<Filter>,
+    #[serde(flatten)]
+    pub third_party_networks: ThirdPartyNetworks,
+}
+
+#[derive(Clone, Debug, Serialize)]
+#[serde(tag = "include_all_networks", content = "third_party_instance_id")]
+pub enum ThirdPartyNetworks {
+    #[serde(rename = "false")]
+    None,
+    #[serde(rename = "false")]
+    Only(String),
+    #[serde(rename = "true")]
+    All,
+}
+
+impl Default for ThirdPartyNetworks {
+    fn default() -> Self {
+        ThirdPartyNetworks::None
+    }
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct Filter {
+    pub generic_search_term: Option<String>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct Response {
+    pub chunk: Vec<Chunk>,
+    pub next_batch: Option<String>,
+    pub prev_batch: Option<String>,
+    pub total_room_count_estimate: Option<u64>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct Chunk {
+    pub aliases: Option<Vec<String>>,
+    pub avatar_url: Option<String>,
+    pub canonical_alias: Option<String>,
+    pub guest_can_join: bool,
+    pub name: Option<String>,
+    pub num_joined_members: i32,
+    pub room_id: String,
+    pub topic: Option<String>,
+    pub world_readable: bool,
+}
+
+pub fn request(base: Url, params: &Parameters, body: &Body) -> Result<Request, Error> {
+    let url = base
+        .join("/_matrix/client/r0/publicRooms")
+        .expect("Malformed URL in post_public_rooms");
+
+    Client::new().post(url).query(params).json(body).build()
+}
diff --git a/fractal-matrix-api/src/r0/thirdparty.rs b/fractal-matrix-api/src/r0/thirdparty.rs
new file mode 100644
index 00000000..b2338027
--- /dev/null
+++ b/fractal-matrix-api/src/r0/thirdparty.rs
@@ -0,0 +1 @@
+pub mod get_supported_protocols;
diff --git a/fractal-matrix-api/src/model/protocol.rs 
b/fractal-matrix-api/src/r0/thirdparty/get_supported_protocols.rs
similarity index 57%
rename from fractal-matrix-api/src/model/protocol.rs
rename to fractal-matrix-api/src/r0/thirdparty/get_supported_protocols.rs
index 040ed1d8..65100d34 100644
--- a/fractal-matrix-api/src/model/protocol.rs
+++ b/fractal-matrix-api/src/r0/thirdparty/get_supported_protocols.rs
@@ -1,8 +1,17 @@
-use serde::Deserialize;
+use reqwest::Client;
+use reqwest::Error;
+use reqwest::Request;
+use serde::{Deserialize, Serialize};
 use serde_json::Value as JsonValue;
 use std::collections::BTreeMap;
+use url::Url;
 
-pub type SupportedProtocols = BTreeMap<String, Protocol>;
+#[derive(Debug, Clone, Serialize)]
+pub struct Parameters {
+    pub access_token: String,
+}
+
+pub type Response = BTreeMap<String, Protocol>;
 
 #[derive(Debug, Clone, Deserialize)]
 pub struct Protocol {
@@ -23,9 +32,18 @@ pub struct FieldType {
 
 #[derive(Debug, Clone, Deserialize)]
 pub struct ProtocolInstance {
+    // TODO: Avoid this rename
     #[serde(rename = "network_id")]
     pub id: String,
     pub desc: String,
     pub icon: Option<String>,
     pub fields: JsonValue,
 }
+
+pub fn request(base: Url, params: &Parameters) -> Result<Request, Error> {
+    let url = base
+        .join("/_matrix/client/r0/thirdparty/protocols")
+        .expect("Wrong URL in get_supported_protocols");
+
+    Client::new().get(url).query(params).build()
+}
diff --git a/fractal-matrix-api/src/ser.rs b/fractal-matrix-api/src/ser.rs
new file mode 100644
index 00000000..2029a499
--- /dev/null
+++ b/fractal-matrix-api/src/ser.rs
@@ -0,0 +1,12 @@
+use serde::Serializer;
+use url::Host;
+
+pub fn serialize_option_host<S>(host: &Option<Host>, ser: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    match host {
+        Some(h) => ser.serialize_str(&h.to_string()),
+        None => ser.serialize_none(),
+    }
+}
diff --git a/fractal-matrix-api/src/types.rs b/fractal-matrix-api/src/types.rs
index eb451f2c..3f6d76f5 100644
--- a/fractal-matrix-api/src/types.rs
+++ b/fractal-matrix-api/src/types.rs
@@ -8,18 +8,12 @@ pub use crate::model::filter::RoomFilter;
 pub use crate::model::member::Member;
 pub use crate::model::member::MemberList;
 pub use crate::model::message::Message;
-pub use crate::model::protocol::ProtocolInstance;
-pub use crate::model::protocol::SupportedProtocols;
 pub use crate::model::register::*;
-pub use crate::model::room::PublicRoomsFilter;
-pub use crate::model::room::PublicRoomsRequest;
-pub use crate::model::room::PublicRoomsResponse;
 pub use crate::model::room::Reason;
 pub use crate::model::room::Room;
 pub use crate::model::room::RoomList;
 pub use crate::model::room::RoomMembership;
 pub use crate::model::room::RoomTag;
-pub use crate::model::room::ThirdPartyNetworks;
 pub use crate::model::stickers::Sticker;
 pub use crate::model::stickers::StickerGroup;
 pub use crate::model::sync::JoinedRoom;
diff --git a/fractal-matrix-api/src/util.rs b/fractal-matrix-api/src/util.rs
index f4664fd2..bba8f3a3 100644
--- a/fractal-matrix-api/src/util.rs
+++ b/fractal-matrix-api/src/util.rs
@@ -29,7 +29,7 @@ use reqwest::header::CONTENT_TYPE;
 use crate::globals;
 
 lazy_static! {
-    static ref HTTP_CLIENT: Client = Client::new();
+    pub static ref HTTP_CLIENT: Client = Client::new();
 }
 
 pub fn semaphore<F>(thread_count: Arc<(Mutex<u8>, Condvar)>, func: F)


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