]> git.seodisparate.com - EN605.607.81.SP22_ASDM_Project/commitdiff
605.607 WIP! working get-id and disconnect
authorStephen Seo <seo.disparate@gmail.com>
Wed, 6 Apr 2022 04:39:12 +0000 (13:39 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Wed, 6 Apr 2022 04:39:12 +0000 (13:39 +0900)
The frontend "server" and backend server must be served via the same
port/address. This can be done by using nginx as the server, and using
reverse-proxies from nginx to the frontend and backend servers.

back_end/Cargo.lock
back_end/Cargo.toml
back_end/src/main.rs
front_end/src/constants.rs
front_end/src/html_helper.rs
front_end/src/state.rs
front_end/src/yew_components.rs

index 829423c70521d6af468fab128d2e2fb3a6a01f0b..c5d4c5016daf94cd6e31c251e0d68e56a1022cac 100644 (file)
@@ -161,6 +161,8 @@ dependencies = [
  "oorandom",
  "rand",
  "rusqlite",
+ "serde",
+ "serde_derive",
  "serde_json",
  "tokio",
  "warp",
@@ -776,6 +778,20 @@ name = "serde"
 version = "1.0.136"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
 
 [[package]]
 name = "serde_json"
index 0c7f4cc45b336e160c68e8bf87c44e1b67bc5cb5..64b2d70f79ecfae7e181c9c07a54bb0231e729ba 100644 (file)
@@ -14,3 +14,5 @@ rusqlite = "0.27.0"
 rand = "0.8.4"
 futures = "0.3"
 oorandom = "11.1.3"
+serde = { version = "1.0", features = ["derive"] }
+serde_derive = "1.0"
index 04c86cf7661861381c066f72f64bd47c7dbf2b73..b078ae5e14b026bb25b34380218cd6f118e6651a 100644 (file)
@@ -43,19 +43,29 @@ async fn main() {
             let db_tx_clone = db_tx_clone.clone();
             let s_helper_tx_clone = s_helper_tx.clone();
             async move {
-                let body_str_result = dbg!(std::str::from_utf8(bytes.as_ref()));
+                let body_str_result = std::str::from_utf8(bytes.as_ref());
                 if let Ok(body_str) = body_str_result {
                     let json_result = serde_json::from_str(body_str);
                     if let Ok(json_value) = json_result {
-                        Ok(warp::reply::json(
-                            &json_handlers::handle_json(json_value, db_tx_clone, s_helper_tx_clone)
+                        Ok(warp::reply::with_header(
+                            json_handlers::handle_json(json_value, db_tx_clone, s_helper_tx_clone)
                                 .unwrap_or_else(|e| e),
+                            "Content-Type",
+                            "application/json",
                         ))
                     } else {
-                        Ok(warp::reply::json(&String::from("{\"type\": \"invalid_syntax\"}")))
+                        Ok(warp::reply::with_header(
+                            String::from("{\"type\": \"invalid_syntax\"}"),
+                            "Content-Type",
+                            "application/json",
+                        ))
                     }
                 } else {
-                    Ok::<warp::reply::Json, Rejection>(warp::reply::json(&String::from("{\"type\": \"invalid_syntax\"}")))
+                    Ok::<warp::reply::WithHeader<String>, Rejection>(warp::reply::with_header(
+                        String::from("{\"type\": \"invalid_syntax\"}"),
+                        "Content-Type",
+                        "application/json",
+                    ))
                 }
             }
         });
index 7b63ae2c76da6b5ec6f8cc684a58948e2d8d2823..1c813a830b446ead724670913c7a64389728bf53 100644 (file)
@@ -15,4 +15,4 @@ pub const PLAYER_CLEANUP_TIMEOUT: u64 = 300;
 pub const BACKEND_TICK_DURATION_MILLIS: i32 = 500;
 
 // TODO: Change this to "https://asdm.seodisparate.com/api" when backend is installed
-pub const BACKEND_URL: &str = "http://localhost:1237/";
+pub const BACKEND_URL: &str = "http://testlocalhost/api";
index 3d76d3d4d6a7bd5b7f0fb4eecfedb1f587956bbb..3d61b2734f16718cc84bff8fa6b12dd88f92e686 100644 (file)
@@ -1,6 +1,11 @@
-use wasm_bindgen::JsValue;
+use js_sys::{Function, JsString, Promise};
+use std::collections::HashMap;
+use wasm_bindgen::{JsCast, JsValue};
+use wasm_bindgen_futures::JsFuture;
 use web_sys::{window, Document, Request, RequestInit, Window};
 
+use crate::constants::BACKEND_URL;
+
 pub fn get_window_document() -> Result<(Window, Document), String> {
     let window = window().ok_or_else(|| String::from("Failed to get window"))?;
     let document = window
@@ -106,3 +111,52 @@ pub fn create_json_request(target_url: &str, json_body: &str) -> Result<Request,
 
     Ok(request)
 }
+
+pub async fn send_to_backend(entries: HashMap<String, String>) -> Result<String, String> {
+    let mut send_json_string = String::from("{");
+    for (key, value) in entries {
+        send_json_string.push('"');
+        send_json_string.push_str(&key);
+        send_json_string.push_str("\":\"");
+        send_json_string.push_str(&value);
+        send_json_string.push_str("\",");
+    }
+    send_json_string.truncate(send_json_string.len() - 1);
+    send_json_string.push('}');
+
+    // TODO check usage of "no-cors"
+    let function = Function::new_no_args(&format!(
+        "
+        let fetch_settings = {{}};
+        fetch_settings.method = 'POST';
+        fetch_settings.headers = {{}};
+        fetch_settings.headers['Content-Type'] = 'application/json';
+        fetch_settings.headers['Accept'] = 'text/html,application/json';
+        //fetch_settings.mode = 'no-cors';
+        fetch_settings.body = '{}';
+
+        return fetch('{}', fetch_settings)
+            .then((response) => {{
+                return response.text();
+            }});
+    ",
+        send_json_string, BACKEND_URL,
+    ));
+
+    let jsvalue: JsValue = function
+        .call0(&function)
+        .map_err(|e| format!("Failed to POST to backend: {:?}", e))?;
+    let promise: Promise = jsvalue.dyn_into().map_err(|e| {
+        format!(
+            "Failed to get Promise out of JsValue when POSTing to backend: {:?}",
+            e
+        )
+    })?;
+    let future_result: JsValue = JsFuture::from(promise)
+        .await
+        .map_err(|e| format!("Failed to await promise when POSTing to backend: {:?}", e))?;
+
+    let json_string = String::from(JsString::from(future_result));
+
+    Ok(json_string)
+}
index 2ceb3f9f9f871baea0f5b37c0a3e895afb42ab72..4183a90303f41236a71f6f6c430f82448b608612 100644 (file)
@@ -2,6 +2,8 @@ use crate::ai::AIDifficulty;
 use crate::constants::{COLS, ROWS};
 use crate::game_logic::{check_win_draw, WinType};
 
+use serde::{Deserialize, Serialize};
+
 use std::cell::Cell;
 use std::collections::hash_set::HashSet;
 use std::fmt::Display;
@@ -424,6 +426,14 @@ pub fn string_from_board(board: BoardType, placed: usize) -> (String, Option<Boa
     }
 }
 
+#[derive(Debug, Serialize, Deserialize)]
+pub struct PairingRequestResponse {
+    pub r#type: String,
+    pub id: u32,
+    pub status: String,
+    pub color: Option<String>,
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
index 7c034536d695f1cd12c8e0451984fd66e15693b7..582822bad6d49bac77f9123c4e75eb01e3172310 100644 (file)
@@ -6,12 +6,15 @@ use crate::constants::{
 use crate::game_logic::{check_win_draw, WinType};
 use crate::html_helper::{
     append_to_info_text, create_json_request, element_append_class, element_remove_class,
-    get_window_document,
+    get_window_document, send_to_backend,
 };
 use crate::random_helper::get_seeded_random;
-use crate::state::{BoardState, GameState, MainMenuMessage, SharedState, Turn};
+use crate::state::{
+    BoardState, GameState, MainMenuMessage, PairingRequestResponse, SharedState, Turn,
+};
 
 use std::cell::Cell;
+use std::collections::HashMap;
 use std::rc::Rc;
 
 use js_sys::{Function, Promise};
@@ -285,153 +288,51 @@ impl Wrapper {
     fn get_networked_player_id(&mut self, ctx: &Context<Self>) {
         // make a request to get the player_id
         ctx.link().send_future(async {
-            // get window
-            let window = web_sys::window().expect("Should be able to get Window");
-            // get request
-            let request = create_json_request(BACKEND_URL, "{\"type\": \"pairing_request\"}")
-                .expect("Should be able to create the JSON request for player_id");
-            // send request
-            let promise = window.fetch_with_request(&request);
-            // get request result
-            let jsvalue_result = JsFuture::from(promise)
-                .await
-                .map_err(|e| format!("{:?}", e));
-            if let Err(e) = jsvalue_result {
-                return WrapperMsg::BackendResponse(BREnum::Error(format!(
-                    "ERROR jsvalue_result: {:?}",
-                    e
-                )));
-            }
-            log::warn!("{:?}", jsvalue_result.as_ref().unwrap());
-            // get response from request result
-            let response_result: Result<Response, _> = jsvalue_result.unwrap().dyn_into();
-            if let Err(e) = response_result {
-                return WrapperMsg::BackendResponse(BREnum::Error(format!(
-                    "ERROR response_result: {:?}",
-                    e
-                )));
-            }
-            let json_jsvalue_promise_result = response_result.unwrap().json();
-            if let Err(e) = json_jsvalue_promise_result {
-                return WrapperMsg::BackendResponse(BREnum::Error(format!(
-                    "ERROR json_jsvalue_promise_result: {:?}",
-                    e
-                )));
-            }
-            let json_jsvalue_result = JsFuture::from(json_jsvalue_promise_result.unwrap()).await;
-            if let Err(e) = json_jsvalue_result {
-                return WrapperMsg::BackendResponse(BREnum::Error(format!(
-                    "ERROR json_jsvalue_result: {:?}",
-                    e
-                )));
-            }
-            let json_result = json_jsvalue_result.unwrap().into_serde();
-            if let Err(e) = json_result {
-                return WrapperMsg::BackendResponse(BREnum::Error(format!(
-                    "ERROR json_result: {:?}",
-                    e
-                )));
-            }
-            // get serde json Value from result
-            let json_value: SerdeJSONValue = json_result.unwrap();
-            log::warn!("{:?}", json_value);
+            let mut json_entries = HashMap::new();
+            json_entries.insert("type".into(), "pairing_request".into());
 
-            // get and check "type" in JSON
-            let type_opt = json_value.get("type");
-            if type_opt.is_none() {
-                return WrapperMsg::BackendResponse(BREnum::Error(
-                    "ERROR: No \"type\" entry in JSON".into(),
-                ));
-            }
-            let json_type = type_opt.unwrap();
-            if let Some(type_string) = json_type.as_str() {
-                if type_string != "pairing_response" {
-                    return WrapperMsg::BackendResponse(BREnum::Error(
-                        "ERROR: Invalid \"type\" from response JSON".into(),
-                    ));
-                }
-            } else {
-                return WrapperMsg::BackendResponse(BREnum::Error(
-                    "ERROR: Missing \"type\" from response JSON".into(),
-                ));
+            let send_to_backend_result = send_to_backend(json_entries).await;
+            if let Err(e) = send_to_backend_result {
+                return WrapperMsg::BackendResponse(BREnum::Error(format!("{:?}", e)));
             }
 
-            // get and check "id" in JSON
-            let player_id: u32;
-            if let Some(wrapped_player_id) = json_value.get("id") {
-                if let Some(player_id_u64) = wrapped_player_id.as_u64() {
-                    let player_id_conv_result: Result<u32, _> = player_id_u64.try_into();
-                    if player_id_conv_result.is_err() {
-                        return WrapperMsg::BackendResponse(BREnum::Error(
-                            "ERROR: \"id\" is too large".into(),
-                        ));
-                    }
-                    player_id = player_id_conv_result.unwrap();
-                } else {
-                    return WrapperMsg::BackendResponse(BREnum::Error(
-                        "ERROR: \"id\" is not a u64".into(),
-                    ));
-                }
-            } else {
-                return WrapperMsg::BackendResponse(BREnum::Error(
-                    "ERROR: Missing \"id\" from response JSON".into(),
-                ));
+            let request_result: Result<PairingRequestResponse, _> =
+                serde_json::from_str(&send_to_backend_result.unwrap());
+            if let Err(e) = request_result {
+                return WrapperMsg::BackendResponse(BREnum::Error(format!("{:?}", e)));
             }
+            let request = request_result.unwrap();
 
-            // get and check status
-            #[derive(Copy, Clone, Debug, PartialEq, Eq)]
-            enum Status {
-                Waiting,
-                Paired,
-            }
-            let mut status: Status;
-            if let Some(status_value) = json_value.get("status") {
-                if let Some(status_str) = status_value.as_str() {
-                    if status_str == "waiting" {
-                        status = Status::Waiting;
-                    } else if status_str == "paired" {
-                        status = Status::Paired;
-                    } else {
-                        return WrapperMsg::BackendResponse(BREnum::Error(
-                            "ERROR: Got invalid \"status\" response in JSON".into(),
-                        ));
-                    }
-                } else {
-                    return WrapperMsg::BackendResponse(BREnum::Error(
-                        "ERROR: \"status\" response in JSON is not a str".into(),
-                    ));
-                }
-            } else {
+            if request.r#type != "pairing_response" {
                 return WrapperMsg::BackendResponse(BREnum::Error(
-                    "ERROR: \"status\" response is missing in JSON".into(),
+                    "Backend returned invalid type for pairing_request".into(),
                 ));
             }
 
-            // set "disconnect" callback here so that the client sends
-            // disconnect message when the page is closed
+            // set up onbeforeunload to disconnect with the received id
             let function = Function::new_no_args(&format!(
                 "
-                window.onunload = function(event) {{
-                    let request_conf = {{}};
-                    request_conf.method = 'POST';
-                    request_conf.headers = \"'Content-Type': 'application/json'\";
-                    request_conf.body = \"{{ \"type\": \"disconnect\", \"id\": {} }}\";
-
-                    const request = new Request('{}', request_conf);
-
-                    fetch(request);
-                }};
+                window.addEventListener(\"beforeunload\", function(event) {{
+                    let xhr = new XMLHttpRequest();
+                    xhr.open('POST', '{}');
+                    xhr.send('{{\"type\": \"disconnect\", \"id\": {}}}');
+                }});
             ",
-                player_id, BACKEND_URL
+                BACKEND_URL, request.id
             ));
             function.call0(&function).ok();
 
-            if status == Status::Paired {
-                // Get which side current player is on if paired
-                // TODO
-                return WrapperMsg::BackendResponse(BREnum::Error("ERROR: unimplemented".into()));
+            if let Some(color) = request.color {
+                WrapperMsg::BackendResponse(BREnum::GotID(
+                    request.id,
+                    if color == "cyan" {
+                        Some(Turn::CyanPlayer)
+                    } else {
+                        Some(Turn::MagentaPlayer)
+                    },
+                ))
             } else {
-                WrapperMsg::BackendResponse(BREnum::GotID(player_id, None))
+                WrapperMsg::BackendResponse(BREnum::GotID(request.id, None))
             }
         });
     }
@@ -1057,6 +958,7 @@ impl Component for Wrapper {
                 }
                 BREnum::GotID(id, turn_opt) => {
                     self.player_id = Some(id);
+                    log::warn!("Got player id {}", id);
                 }
             },
         } // match (msg)