[fractal] API: Implement and use user request and response types



commit dc924b1be08cdbd9bb8589a71892e77a738e89b0
Author: Alejandro Domínguez <adomu net-c com>
Date:   Mon Jan 28 18:38:26 2019 +0100

    API: Implement and use user request and response types

 fractal-gtk/src/appop/account.rs         |  64 +++----
 fractal-gtk/src/widgets/address.rs       |  28 +--
 fractal-matrix-api/src/backend/mod.rs    |  16 +-
 fractal-matrix-api/src/backend/types.rs  |   7 +-
 fractal-matrix-api/src/backend/user.rs   | 286 ++++++++++++++++---------------
 fractal-matrix-api/src/model/member.rs   |  11 ++
 fractal-matrix-api/src/model/mod.rs      |   2 +-
 fractal-matrix-api/src/model/user.rs     | 126 ++++++++++++++
 fractal-matrix-api/src/model/userinfo.rs |   9 -
 fractal-matrix-api/src/types.rs          |   3 +-
 10 files changed, 351 insertions(+), 201 deletions(-)
---
diff --git a/fractal-gtk/src/appop/account.rs b/fractal-gtk/src/appop/account.rs
index d0a84f8d..5cf316cc 100644
--- a/fractal-gtk/src/appop/account.rs
+++ b/fractal-gtk/src/appop/account.rs
@@ -12,10 +12,11 @@ use crate::widgets;
 use crate::widgets::AvatarExt;
 
 use crate::cache::download_to_cache;
-use fractal_api::types::UserInfo;
+use fractal_api::types::Medium;
+use fractal_api::types::ThirdPartyIdentifier;
 
 impl AppOp {
-    pub fn set_three_pid(&self, data: Option<Vec<UserInfo>>) {
+    pub fn set_three_pid(&self, data: Option<Vec<ThirdPartyIdentifier>>) {
         self.update_address(data);
     }
 
@@ -312,7 +313,7 @@ impl AppOp {
         self.set_state(AppState::AccountSettings);
     }
 
-    pub fn update_address(&self, data: Option<Vec<UserInfo>>) {
+    pub fn update_address(&self, data: Option<Vec<ThirdPartyIdentifier>>) {
         let grid = self
             .ui
             .builder
@@ -368,34 +369,37 @@ impl AppOp {
         phone.pack_start(&empty_phone.create(None), true, true, 0);
         if let Some(data) = data {
             for item in data {
-                if item.medium == "email" {
-                    if first_email {
-                        empty_email.update(Some(item.address));
-                        let entry =
-                            widgets::Address::new(widgets::AddressType::Email, &self).create(None);
-                        grid.insert_next_to(&email, gtk::PositionType::Bottom);
-                        grid.attach_next_to(&entry, &email, gtk::PositionType::Bottom, 1, 1);
-                        first_email = false;
-                    } else {
-                        let entry = widgets::Address::new(widgets::AddressType::Email, &self)
-                            .create(Some(item.address));
-                        grid.insert_next_to(&email, gtk::PositionType::Bottom);
-                        grid.attach_next_to(&entry, &email, gtk::PositionType::Bottom, 1, 1);
+                match item.medium {
+                    Medium::Email => {
+                        if first_email {
+                            empty_email.update(Some(item.address));
+                            let entry = widgets::Address::new(widgets::AddressType::Email, &self)
+                                .create(None);
+                            grid.insert_next_to(&email, gtk::PositionType::Bottom);
+                            grid.attach_next_to(&entry, &email, gtk::PositionType::Bottom, 1, 1);
+                            first_email = false;
+                        } else {
+                            let entry = widgets::Address::new(widgets::AddressType::Email, &self)
+                                .create(Some(item.address));
+                            grid.insert_next_to(&email, gtk::PositionType::Bottom);
+                            grid.attach_next_to(&entry, &email, gtk::PositionType::Bottom, 1, 1);
+                        }
                     }
-                } else if item.medium == "msisdn" {
-                    if first_phone {
-                        empty_phone.update(Some(item.address));
-                        let entry =
-                            widgets::Address::new(widgets::AddressType::Phone, &self).create(None);
-                        grid.insert_next_to(&phone, gtk::PositionType::Bottom);
-                        grid.attach_next_to(&entry, &phone, gtk::PositionType::Bottom, 1, 1);
-                        first_phone = false;
-                    } else {
-                        let s = String::from("+") + &String::from(item.address);
-                        let entry = widgets::Address::new(widgets::AddressType::Phone, &self)
-                            .create(Some(s));
-                        grid.insert_next_to(&phone, gtk::PositionType::Bottom);
-                        grid.attach_next_to(&entry, &phone, gtk::PositionType::Bottom, 1, 1);
+                    Medium::MsIsdn => {
+                        if first_phone {
+                            empty_phone.update(Some(item.address));
+                            let entry = widgets::Address::new(widgets::AddressType::Phone, &self)
+                                .create(None);
+                            grid.insert_next_to(&phone, gtk::PositionType::Bottom);
+                            grid.attach_next_to(&entry, &phone, gtk::PositionType::Bottom, 1, 1);
+                            first_phone = false;
+                        } else {
+                            let s = String::from("+") + &item.address;
+                            let entry = widgets::Address::new(widgets::AddressType::Phone, &self)
+                                .create(Some(s));
+                            grid.insert_next_to(&phone, gtk::PositionType::Bottom);
+                            grid.attach_next_to(&entry, &phone, gtk::PositionType::Bottom, 1, 1);
+                        }
                     }
                 }
             }
diff --git a/fractal-gtk/src/widgets/address.rs b/fractal-gtk/src/widgets/address.rs
index 3a9d9228..dea21bfe 100644
--- a/fractal-gtk/src/widgets/address.rs
+++ b/fractal-gtk/src/widgets/address.rs
@@ -1,3 +1,4 @@
+use fractal_api::types::Medium;
 use glib::signal;
 use gtk;
 use gtk::prelude::*;
@@ -170,8 +171,8 @@ impl<'a> Address<'a> {
             entry.set_editable(false);
 
             let medium = match medium {
-                AddressType::Email => String::from("email"),
-                AddressType::Phone => String::from("msisdn"),
+                AddressType::Email => Medium::Email,
+                AddressType::Phone => Medium::MsIsdn,
             };
 
             match action {
@@ -189,7 +190,7 @@ impl<'a> Address<'a> {
 
 fn delete_address(
     backend: &Sender<BKCommand>,
-    medium: String,
+    medium: Medium,
     address: Option<String>,
 ) -> Option<String> {
     backend
@@ -200,19 +201,22 @@ fn delete_address(
 
 fn add_address(
     backend: &Sender<BKCommand>,
-    medium: String,
+    medium: Medium,
     id_server: String,
     address: Option<String>,
 ) -> Option<String> {
     let secret: String = thread_rng().sample_iter(&Alphanumeric).take(36).collect();
-    if medium == "msisdn" {
-        backend
-            .send(BKCommand::GetTokenPhone(id_server, address?, secret))
-            .unwrap();
-    } else {
-        backend
-            .send(BKCommand::GetTokenEmail(id_server, address?, secret))
-            .unwrap();
+    match medium {
+        Medium::MsIsdn => {
+            backend
+                .send(BKCommand::GetTokenPhone(id_server, address?, secret))
+                .unwrap();
+        }
+        Medium::Email => {
+            backend
+                .send(BKCommand::GetTokenEmail(id_server, address?, secret))
+                .unwrap();
+        }
     }
     None
 }
diff --git a/fractal-matrix-api/src/backend/mod.rs b/fractal-matrix-api/src/backend/mod.rs
index 8dfca561..7487c7d4 100644
--- a/fractal-matrix-api/src/backend/mod.rs
+++ b/fractal-matrix-api/src/backend/mod.rs
@@ -121,11 +121,11 @@ impl Backend {
                 bkerror!(r, tx, BKResponse::GetThreePIDError);
             }
             Ok(BKCommand::GetTokenEmail(identity, email, client_secret)) => {
-                let r = user::get_email_token(self, &identity, &email, client_secret);
+                let r = user::get_email_token(self, identity, email, client_secret);
                 bkerror!(r, tx, BKResponse::GetTokenEmailError);
             }
             Ok(BKCommand::GetTokenPhone(identity, phone, client_secret)) => {
-                let r = user::get_phone_token(self, &identity, &phone, client_secret);
+                let r = user::get_phone_token(self, identity, phone, client_secret);
                 bkerror!(r, tx, BKResponse::GetTokenEmailError);
             }
             Ok(BKCommand::SubmitPhoneToken(identity, client_secret, sid, token)) => {
@@ -133,18 +133,18 @@ impl Backend {
                 bkerror!(r, tx, BKResponse::SubmitPhoneTokenError);
             }
             Ok(BKCommand::AddThreePID(identity, client_secret, sid)) => {
-                let r = user::add_threepid(self, &identity, &client_secret, sid);
+                let r = user::add_threepid(self, identity, client_secret, sid);
                 bkerror!(r, tx, BKResponse::AddThreePIDError);
             }
             Ok(BKCommand::DeleteThreePID(medium, address)) => {
-                user::delete_three_pid(self, &medium, &address);
+                user::delete_three_pid(self, medium, address);
             }
             Ok(BKCommand::ChangePassword(username, old_password, new_password)) => {
-                let r = user::change_password(self, &username, &old_password, &new_password);
+                let r = user::change_password(self, username, old_password, new_password);
                 bkerror!(r, tx, BKResponse::ChangePasswordError);
             }
-            Ok(BKCommand::AccountDestruction(username, password, flag)) => {
-                let r = user::account_destruction(self, &username, &password, flag);
+            Ok(BKCommand::AccountDestruction(username, password, _)) => {
+                let r = user::account_destruction(self, username, password);
                 bkerror!(r, tx, BKResponse::AccountDestructionError);
             }
             Ok(BKCommand::GetAvatar) => {
@@ -168,7 +168,7 @@ impl Backend {
                 bkerror!(r, tx, BKResponse::CommandError);
             }
             Ok(BKCommand::UserSearch(term)) => {
-                let r = user::search(self, &term);
+                let r = user::search(self, term);
                 bkerror!(r, tx, BKResponse::CommandError);
             }
 
diff --git a/fractal-matrix-api/src/backend/types.rs b/fractal-matrix-api/src/backend/types.rs
index a18f1c4e..9f72a41d 100644
--- a/fractal-matrix-api/src/backend/types.rs
+++ b/fractal-matrix-api/src/backend/types.rs
@@ -5,13 +5,14 @@ use std::sync::{Arc, Condvar, Mutex};
 use crate::error::Error;
 
 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;
-use crate::types::UserInfo;
+use crate::types::ThirdPartyIdentifier;
 
 use crate::cache::CacheMap;
 use url::Url;
@@ -32,7 +33,7 @@ pub enum BKCommand {
     GetTokenPhone(String, String, String),
     SubmitPhoneToken(String, String, String, String),
     AddThreePID(String, String, String),
-    DeleteThreePID(String, String),
+    DeleteThreePID(Medium, String),
     ChangePassword(String, String, String),
     AccountDestruction(String, String, bool),
     GetAvatar,
@@ -90,7 +91,7 @@ pub enum BKResponse {
     Logout,
     Name(String),
     SetUserName(String),
-    GetThreePID(Vec<UserInfo>),
+    GetThreePID(Vec<ThirdPartyIdentifier>),
     GetTokenEmail(String, String),
     GetTokenPhone(String, String),
     SubmitPhoneToken(Option<String>, String),
diff --git a/fractal-matrix-api/src/backend/user.rs b/fractal-matrix-api/src/backend/user.rs
index fe02fe01..4d95ad57 100644
--- a/fractal-matrix-api/src/backend/user.rs
+++ b/fractal-matrix-api/src/backend/user.rs
@@ -19,8 +19,26 @@ use std::sync::{Arc, Mutex};
 use std::thread;
 use url::Url;
 
+use crate::types::AddThreePIDRequest;
+use crate::types::AuthenticationData;
+use crate::types::ChangePasswordRequest;
+use crate::types::DeactivateAccountRequest;
+use crate::types::DeleteThreePIDRequest;
+use crate::types::EmailTokenRequest;
+use crate::types::GetDisplayNameResponse;
+use crate::types::Identifier;
+use crate::types::Medium;
 use crate::types::Member;
-use crate::types::UserInfo;
+use crate::types::PhoneTokenRequest;
+use crate::types::PutDisplayNameRequest;
+use crate::types::SearchUserRequest;
+use crate::types::SearchUserResponse;
+use crate::types::SubmitPhoneTokenRequest;
+use crate::types::SubmitPhoneTokenResponse;
+use crate::types::ThirdPartyIDResponse;
+use crate::types::ThirdPartyTokenResponse;
+use crate::types::ThreePIDCredentials;
+use crate::types::UserIdentifier;
 
 use serde_json;
 use serde_json::Value as JsonValue;
@@ -31,9 +49,12 @@ pub fn get_username(bk: &Backend) -> Result<(), Error> {
     let tx = bk.tx.clone();
     get!(
         &url,
-        |r: JsonValue| {
-            let name = String::from(r["displayname"].as_str().unwrap_or(&id));
+        |r: JsonValue| if let Ok(response) = serde_json::from_value::<GetDisplayNameResponse>(r) {
+            let name = response.displayname.unwrap_or(id);
             tx.send(BKResponse::Name(name)).unwrap();
+        } else {
+            tx.send(BKResponse::UserNameError(Error::BackendError))
+                .unwrap();
         },
         |err| tx.send(BKResponse::UserNameError(err)).unwrap()
     );
@@ -45,15 +66,17 @@ pub fn set_username(bk: &Backend, name: String) -> Result<(), Error> {
     let id = bk.data.lock().unwrap().user_id.clone();
     let url = bk.url(&format!("profile/{}/displayname", encode_uid(&id)), vec![])?;
 
-    let attrs = json!({
-        "displayname": name,
-    });
+    let attrs = PutDisplayNameRequest {
+        displayname: Some(name.clone()),
+    };
+    let attrs_json =
+        serde_json::to_value(attrs).expect("Failed to serialize display name setting request");
 
     let tx = bk.tx.clone();
     query!(
         "put",
         &url,
-        &attrs,
+        &attrs_json,
         |_| {
             tx.send(BKResponse::SetUserName(name)).unwrap();
         },
@@ -70,36 +93,12 @@ pub fn get_threepid(bk: &Backend) -> Result<(), Error> {
     let tx = bk.tx.clone();
     get!(
         &url,
-        |r: JsonValue| {
-            let mut result: Vec<UserInfo> = vec![];
-            if let Some(arr) = r["threepids"].as_array() {
-                for pid in arr.iter() {
-                    let address = match pid["address"].as_str() {
-                        None => String::new(),
-                        Some(a) => a.to_string(),
-                    };
-                    let add = match pid["added_at"].as_u64() {
-                        None => 0,
-                        Some(a) => a,
-                    };
-                    let medium = match pid["medium"].as_str() {
-                        None => String::new(),
-                        Some(a) => a.to_string(),
-                    };
-                    let val = match pid["validated_at"].as_u64() {
-                        None => 0,
-                        Some(a) => a,
-                    };
-
-                    result.push(UserInfo {
-                        address: address,
-                        added_at: add,
-                        validated_at: val,
-                        medium: medium,
-                    });
-                }
-            }
-            tx.send(BKResponse::GetThreePID(result)).unwrap();
+        |r: JsonValue| if let Ok(response) = serde_json::from_value::<ThirdPartyIDResponse>(r) {
+            tx.send(BKResponse::GetThreePID(response.threepids))
+                .unwrap();
+        } else {
+            tx.send(BKResponse::GetThreePIDError(Error::BackendError))
+                .unwrap();
         },
         |err| tx.send(BKResponse::GetThreePIDError(err)).unwrap()
     );
@@ -109,26 +108,31 @@ pub fn get_threepid(bk: &Backend) -> Result<(), Error> {
 
 pub fn get_email_token(
     bk: &Backend,
-    identity: &str,
-    email: &str,
+    identity: String,
+    email: String,
     client_secret: String,
 ) -> Result<(), Error> {
     let url = bk.url("account/3pid/email/requestToken", vec![])?;
 
-    let attrs = json!({
-        "id_server": identity[8..],
-        "client_secret": client_secret.clone(),
-        "email": email,
-        "send_attempt": "1",
-    });
+    let attrs = EmailTokenRequest {
+        id_server: identity[8..].into(),
+        client_secret: client_secret.clone(),
+        email: email,
+        send_attempt: 1,
+        next_link: None,
+    };
+
+    let attrs_json = serde_json::to_value(attrs).expect("Failed to serialize email token request");
 
     let tx = bk.tx.clone();
     post!(
         &url,
-        &attrs,
-        |r: JsonValue| {
-            let sid = String::from(r["sid"].as_str().unwrap_or_default());
-            tx.send(BKResponse::GetTokenEmail(sid, client_secret))
+        &attrs_json,
+        |r: JsonValue| if let Ok(response) = serde_json::from_value::<ThirdPartyTokenResponse>(r) {
+            tx.send(BKResponse::GetTokenEmail(response.sid, client_secret))
+                .unwrap();
+        } else {
+            tx.send(BKResponse::GetTokenEmailError(Error::BackendError))
                 .unwrap();
         },
         |err| match err {
@@ -148,27 +152,32 @@ pub fn get_email_token(
 
 pub fn get_phone_token(
     bk: &Backend,
-    identity: &str,
-    phone: &str,
+    identity: String,
+    phone: String,
     client_secret: String,
 ) -> Result<(), Error> {
     let url = bk.url(&format!("account/3pid/msisdn/requestToken"), vec![])?;
 
-    let attrs = json!({
-        "id_server": identity[8..],
-        "client_secret": client_secret,
-        "phone_number": phone,
-        "country": "",
-        "send_attempt": "1",
-    });
+    let attrs = PhoneTokenRequest {
+        id_server: identity[8..].into(),
+        client_secret: client_secret.clone(),
+        phone_number: phone,
+        country: String::new(),
+        send_attempt: 1,
+        next_link: None,
+    };
+
+    let attrs_json = serde_json::to_value(attrs).expect("Failed to serialize phone token request");
 
     let tx = bk.tx.clone();
     post!(
         &url,
-        &attrs,
-        |r: JsonValue| {
-            let sid = String::from(r["sid"].as_str().unwrap_or_default());
-            tx.send(BKResponse::GetTokenPhone(sid, client_secret))
+        &attrs_json,
+        |r: JsonValue| if let Ok(response) = serde_json::from_value::<ThirdPartyTokenResponse>(r) {
+            tx.send(BKResponse::GetTokenPhone(response.sid, client_secret))
+                .unwrap();
+        } else {
+            tx.send(BKResponse::GetTokenPhoneError(Error::BackendError))
                 .unwrap();
         },
         |err| match err {
@@ -188,25 +197,28 @@ pub fn get_phone_token(
 
 pub fn add_threepid(
     bk: &Backend,
-    identity: &str,
-    client_secret: &str,
+    identity: String,
+    client_secret: String,
     sid: String,
 ) -> Result<(), Error> {
     let url = bk.url(&format!("account/3pid"), vec![])?;
-    let attrs = json!({
-        "three_pid_creds": {
-            "id_server": identity[8..],
-            "sid": sid,
-            "client_secret": client_secret.clone()
+    let attrs = AddThreePIDRequest {
+        three_pid_creds: ThreePIDCredentials {
+            id_server: identity[8..].into(),
+            sid: sid.clone(),
+            client_secret,
         },
-        "bind": true
-    });
+        bind: true,
+    };
+
+    let attrs_json = serde_json::to_value(attrs)
+        .expect("Failed to serialize add third party information request");
 
     let tx = bk.tx.clone();
     post!(
         &url,
-        &attrs,
-        |_r: JsonValue| {
+        &attrs_json,
+        |_| {
             tx.send(BKResponse::AddThreePID(sid)).unwrap();
         },
         |err| {
@@ -224,25 +236,28 @@ pub fn submit_phone_token(
     sid: String,
     token: String,
 ) -> Result<(), Error> {
-    let params = &[
-        ("sid", sid.clone()),
-        ("client_secret", client_secret.clone()),
-        ("token", token),
-    ];
     let path = "/_matrix/identity/api/v1/validate/msisdn/submitToken";
-    let url = build_url(&Url::parse(&url)?, path, params)?;
+    let url = build_url(&Url::parse(url)?, path, &[])?;
+
+    let attrs = SubmitPhoneTokenRequest {
+        sid: sid.clone(),
+        client_secret: client_secret.clone(),
+        token,
+    };
 
+    let attrs_json =
+        serde_json::to_value(attrs).expect("Failed to serialize phone token submit request");
     let tx = bk.tx.clone();
     post!(
         &url,
-        |r: JsonValue| {
-            let result = if r["success"] == true {
-                Some(sid)
-            } else {
-                None
-            };
+        &attrs_json,
+        |r: JsonValue| if let Ok(response) = serde_json::from_value::<SubmitPhoneTokenResponse>(r) {
+            let result = Some(sid).filter(|_| response.success);
             tx.send(BKResponse::SubmitPhoneToken(result, client_secret))
                 .unwrap();
+        } else {
+            tx.send(BKResponse::SubmitPhoneTokenError(Error::BackendError))
+                .unwrap();
         },
         |err| {
             tx.send(BKResponse::SubmitPhoneTokenError(err)).unwrap();
@@ -252,7 +267,7 @@ pub fn submit_phone_token(
     Ok(())
 }
 
-pub fn delete_three_pid(bk: &Backend, medium: &str, address: &str) {
+pub fn delete_three_pid(bk: &Backend, medium: Medium, address: String) {
     let baseu = bk.get_base_url();
     let tk = bk.data.lock().unwrap().access_token.clone();
     let mut url = baseu
@@ -261,15 +276,14 @@ pub fn delete_three_pid(bk: &Backend, medium: &str, address: &str) {
     url.query_pairs_mut()
         .clear()
         .append_pair("access_token", &tk);
-    let attrs = json!({
-        "medium": medium,
-        "address": address,
-    });
+    let attrs = DeleteThreePIDRequest { medium, address };
 
+    let attrs_json =
+        serde_json::to_value(attrs).expect("Failed to serialize third party ID delete request");
     let tx = bk.tx.clone();
     post!(
         &url,
-        &attrs,
+        &attrs_json,
         |_r: JsonValue| {
             tx.send(BKResponse::DeleteThreePID).unwrap();
         },
@@ -281,25 +295,27 @@ pub fn delete_three_pid(bk: &Backend, medium: &str, address: &str) {
 
 pub fn change_password(
     bk: &Backend,
-    username: &str,
-    old_password: &str,
-    new_password: &str,
+    user: String,
+    old_password: String,
+    new_password: String,
 ) -> Result<(), Error> {
     let url = bk.url(&format!("account/password"), vec![])?;
 
-    let attrs = json!({
-        "new_password": new_password,
-        "auth": {
-            "type": "m.login.password",
-            "user": username,
-            "password": old_password,
-        }
-    });
-
+    let attrs = ChangePasswordRequest {
+        new_password,
+        auth: Some(AuthenticationData::Password {
+            identifier: Identifier::new(UserIdentifier::User { user }),
+            password: old_password,
+            session: None,
+        }),
+    };
+
+    let attrs_json =
+        serde_json::to_value(attrs).expect("Failed to serialize password change request");
     let tx = bk.tx.clone();
     post!(
         &url,
-        &attrs,
+        &attrs_json,
         |r: JsonValue| {
             info!("{}", r);
             tx.send(BKResponse::ChangePassword).unwrap();
@@ -312,27 +328,23 @@ pub fn change_password(
     Ok(())
 }
 
-pub fn account_destruction(
-    bk: &Backend,
-    username: &str,
-    password: &str,
-    flag: bool,
-) -> Result<(), Error> {
+pub fn account_destruction(bk: &Backend, user: String, password: String) -> Result<(), Error> {
     let url = bk.url(&format!("account/deactivate"), vec![])?;
 
-    let attrs = json!({
-        "erase": flag,
-        "auth": {
-            "type": "m.login.password",
-            "user": username,
-            "password": password,
-        }
-    });
+    let attrs = DeactivateAccountRequest {
+        auth: Some(AuthenticationData::Password {
+            identifier: Identifier::new(UserIdentifier::User { user }),
+            password,
+            session: None,
+        }),
+    };
 
+    let attrs_json =
+        serde_json::to_value(attrs).expect("Failed to serialize account deactivation request");
     let tx = bk.tx.clone();
     post!(
         &url,
-        &attrs,
+        &attrs_json,
         |r: JsonValue| {
             info!("{}", r);
             tx.send(BKResponse::AccountDestruction).unwrap();
@@ -414,11 +426,13 @@ pub fn get_username_async(bk: &Backend, uid: String, tx: Sender<String>) -> Resu
     let url = bk.url(&format!("profile/{}/displayname", encode_uid(&uid)), vec![])?;
     get!(
         &url,
-        |r: JsonValue| {
-            let name = String::from(r["displayname"].as_str().unwrap_or(&uid));
+        |r: JsonValue| if let Ok(response) = serde_json::from_value::<GetDisplayNameResponse>(r) {
+            let name = response.displayname.unwrap_or(uid);
             tx.send(name).unwrap();
+        } else {
+            tx.send(uid.to_string()).unwrap();
         },
-        |_err| tx.send(uid.to_string()).unwrap()
+        |_| tx.send(uid.to_string()).unwrap()
     );
 
     Ok(())
@@ -492,28 +506,26 @@ pub fn set_user_avatar(bk: &Backend, avatar: String) -> Result<(), Error> {
     Ok(())
 }
 
-pub fn search(bk: &Backend, term: &str) -> Result<(), Error> {
+pub fn search(bk: &Backend, search_term: String) -> Result<(), Error> {
     let url = bk.url(&format!("user_directory/search"), vec![])?;
 
-    let attrs = json!({
-        "search_term": term,
-    });
+    let attrs = SearchUserRequest {
+        search_term,
+        ..Default::default()
+    };
 
+    let attrs_json =
+        serde_json::to_value(attrs).expect("Failed to serialize user directory search request");
     let tx = bk.tx.clone();
     post!(
         &url,
-        &attrs,
-        |js: JsonValue| {
-            let mut users: Vec<Member> = vec![];
-            if let Some(arr) = js["results"].as_array() {
-                for member in arr.iter() {
-                    let mut member_s: Member = serde_json::from_value(member.clone()).unwrap();
-                    member_s.uid = member["user_id"].as_str().unwrap_or_default().to_string();
-
-                    users.push(member_s);
-                }
-            }
+        &attrs_json,
+        |r: JsonValue| if let Ok(response) = serde_json::from_value::<SearchUserResponse>(r) {
+            let users = response.results.into_iter().map(Into::into).collect();
             tx.send(BKResponse::UserSearch(users)).unwrap();
+        } else {
+            tx.send(BKResponse::CommandError(Error::BackendError))
+                .unwrap();
         },
         |err| {
             tx.send(BKResponse::CommandError(err)).unwrap();
diff --git a/fractal-matrix-api/src/model/member.rs b/fractal-matrix-api/src/model/member.rs
index 0e8dfcdb..a2ff4b1b 100644
--- a/fractal-matrix-api/src/model/member.rs
+++ b/fractal-matrix-api/src/model/member.rs
@@ -1,3 +1,4 @@
+use crate::types::User;
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
 
@@ -30,5 +31,15 @@ impl PartialEq for Member {
     }
 }
 
+impl From<User> for Member {
+    fn from(user: User) -> Self {
+        Self {
+            uid: user.user_id,
+            alias: user.display_name,
+            avatar: user.avatar_url,
+        }
+    }
+}
+
 // hashmap userid -> Member
 pub type MemberList = HashMap<String, Member>;
diff --git a/fractal-matrix-api/src/model/mod.rs b/fractal-matrix-api/src/model/mod.rs
index 1e05f375..93ed1579 100644
--- a/fractal-matrix-api/src/model/mod.rs
+++ b/fractal-matrix-api/src/model/mod.rs
@@ -7,7 +7,7 @@ pub mod register;
 pub mod room;
 pub mod stickers;
 pub mod sync;
-pub mod userinfo;
+pub mod user;
 
 use serde::{Deserialize, Serialize};
 
diff --git a/fractal-matrix-api/src/model/user.rs b/fractal-matrix-api/src/model/user.rs
new file mode 100644
index 00000000..a6a5e6da
--- /dev/null
+++ b/fractal-matrix-api/src/model/user.rs
@@ -0,0 +1,126 @@
+use super::{AuthenticationData, Medium, ThreePIDCredentials};
+use serde::{Deserialize, Serialize};
+use std::ops::Not;
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct GetDisplayNameResponse {
+    pub displayname: Option<String>,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct PutDisplayNameRequest {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub displayname: Option<String>,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct ThirdPartyIDResponse {
+    pub threepids: Vec<ThirdPartyIdentifier>,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct ThirdPartyIdentifier {
+    pub added_at: u64,
+    pub medium: Medium,
+    pub validated_at: u64,
+    pub address: String,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct EmailTokenRequest {
+    pub client_secret: String,
+    pub email: String,
+    pub id_server: String,
+    pub send_attempt: u64,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub next_link: Option<String>,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct PhoneTokenRequest {
+    pub client_secret: String,
+    pub phone_number: String,
+    pub country: String,
+    pub id_server: String,
+    pub send_attempt: u64,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub next_link: Option<String>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct ThirdPartyTokenResponse {
+    pub sid: String,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct AddThreePIDRequest {
+    pub three_pid_creds: ThreePIDCredentials,
+    #[serde(skip_serializing_if = "Not::not")]
+    pub bind: bool,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct SubmitPhoneTokenRequest {
+    pub sid: String,
+    pub client_secret: String,
+    pub token: String,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct SubmitPhoneTokenResponse {
+    pub success: bool,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct DeleteThreePIDRequest {
+    pub medium: Medium,
+    pub address: String,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct ChangePasswordRequest {
+    pub new_password: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub auth: Option<AuthenticationData>,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct DeactivateAccountRequest {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub auth: Option<AuthenticationData>,
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct SearchUserRequest {
+    pub search_term: String,
+    #[serde(skip_serializing_if = "u64_is_10")]
+    pub limit: u64,
+}
+
+impl Default for SearchUserRequest {
+    fn default() -> Self {
+        Self {
+            search_term: Default::default(),
+            limit: 10,
+        }
+    }
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct SearchUserResponse {
+    pub results: Vec<User>,
+    pub limited: bool,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct User {
+    pub user_id: String,
+    #[serde(default)]
+    pub display_name: Option<String>,
+    #[serde(default)]
+    pub avatar_url: Option<String>,
+}
+
+fn u64_is_10(number: &u64) -> bool {
+    number == &10
+}
diff --git a/fractal-matrix-api/src/types.rs b/fractal-matrix-api/src/types.rs
index 96aa9d1f..fce0a4a4 100644
--- a/fractal-matrix-api/src/types.rs
+++ b/fractal-matrix-api/src/types.rs
@@ -21,4 +21,5 @@ pub use crate::model::stickers::StickerGroup;
 pub use crate::model::sync::JoinedRoom;
 pub use crate::model::sync::SyncResponse;
 pub use crate::model::sync::UnreadNotificationsCount;
-pub use crate::model::userinfo::UserInfo;
+pub use crate::model::user::*;
+pub use crate::model::*;


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