From f626f6f638edce7b951e1ad9c280eefce88ef0cd Mon Sep 17 00:00:00 2001 From: appflowy Date: Mon, 23 Aug 2021 08:27:29 +0800 Subject: [PATCH] add request builder --- backend/src/routers/user.rs | 1 + backend/src/user_service/auth.rs | 2 +- backend/src/ws_service/entities/connect.rs | 2 +- backend/src/ws_service/ws_server.rs | 2 +- rust-lib/flowy-net/Cargo.toml | 2 +- rust-lib/flowy-net/src/errors.rs | 70 ++++++++++++++ rust-lib/flowy-net/src/lib.rs | 1 + rust-lib/flowy-net/src/request/request.rs | 95 ++++++++++++++++++- rust-lib/flowy-net/src/response/response.rs | 68 +------------ .../flowy-net/src/response/response_http.rs | 6 +- rust-lib/flowy-user/src/errors.rs | 4 +- .../src/services/user/user_server.rs | 14 ++- 12 files changed, 185 insertions(+), 82 deletions(-) create mode 100644 rust-lib/flowy-net/src/errors.rs diff --git a/backend/src/routers/user.rs b/backend/src/routers/user.rs index e2ed77fcbb..4d5f8cf942 100644 --- a/backend/src/routers/user.rs +++ b/backend/src/routers/user.rs @@ -9,6 +9,7 @@ use flowy_net::response::*; use flowy_user::protobuf::SignUpParams; use crate::user_service::sign_up; +use flowy_net::errors::ServerError; use sqlx::PgPool; use std::sync::Arc; diff --git a/backend/src/user_service/auth.rs b/backend/src/user_service/auth.rs index 8557cb0a18..abeacafc03 100644 --- a/backend/src/user_service/auth.rs +++ b/backend/src/user_service/auth.rs @@ -1,6 +1,6 @@ use anyhow::Context; use chrono::Utc; -use flowy_net::response::{Code, FlowyResponse, ServerError}; +use flowy_net::{errors::ServerError, response::FlowyResponse}; use flowy_user::{entities::SignUpResponse, protobuf::SignUpParams}; use sqlx::{Error, PgPool, Postgres, Transaction}; use std::sync::Arc; diff --git a/backend/src/ws_service/entities/connect.rs b/backend/src/ws_service/entities/connect.rs index 20336b1bd3..10f04b285a 100644 --- a/backend/src/ws_service/entities/connect.rs +++ b/backend/src/ws_service/entities/connect.rs @@ -1,6 +1,6 @@ use crate::ws_service::ClientMessage; use actix::{Message, Recipient}; -use flowy_net::response::ServerError; +use flowy_net::errors::ServerError; use serde::{Deserialize, Serialize}; use std::fmt::Formatter; diff --git a/backend/src/ws_service/ws_server.rs b/backend/src/ws_service/ws_server.rs index 091b19ea17..ac307504ad 100644 --- a/backend/src/ws_service/ws_server.rs +++ b/backend/src/ws_service/ws_server.rs @@ -4,7 +4,7 @@ use crate::ws_service::{ }; use actix::{Actor, Context, Handler}; use dashmap::DashMap; -use flowy_net::response::ServerError; +use flowy_net::errors::ServerError; pub struct WSServer { sessions: DashMap, diff --git a/rust-lib/flowy-net/Cargo.toml b/rust-lib/flowy-net/Cargo.toml index 19dbb597ea..ee94fdc0d8 100644 --- a/rust-lib/flowy-net/Cargo.toml +++ b/rust-lib/flowy-net/Cargo.toml @@ -15,7 +15,7 @@ serde_repr = "0.1" pin-project = "1.0.0" futures-core = { version = "0.3", default-features = false } log = "0.4" -bytes = "1.0" +bytes = { version = "1.0", features = ["serde"]} lazy_static = "1.4.0" tokio = { version = "1", features = ["full"] } actix-web = {version = "4.0.0-beta.8", optional = true} diff --git a/rust-lib/flowy-net/src/errors.rs b/rust-lib/flowy-net/src/errors.rs new file mode 100644 index 0000000000..75849c287f --- /dev/null +++ b/rust-lib/flowy-net/src/errors.rs @@ -0,0 +1,70 @@ +use bytes::Bytes; +use serde::{Deserialize, Serialize, __private::Formatter}; +use serde_repr::*; +use std::{fmt, fmt::Debug}; + +use crate::response::FlowyResponse; + +#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone)] +pub struct ServerError { + pub code: Code, + pub msg: String, +} + +macro_rules! static_error { + ($name:ident, $status:expr) => { + #[allow(non_snake_case, missing_docs)] + pub fn $name(error: T) -> ServerError { + let msg = format!("{:?}", error); + ServerError { code: $status, msg } + } + }; +} + +impl ServerError { + static_error!(internal, Code::InternalError); + static_error!(http, Code::HttpError); + static_error!(payload_none, Code::PayloadUnexpectedNone); +} + +impl std::fmt::Display for ServerError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let msg = format!("{:?}:{}", self.code, self.msg); + f.write_str(&msg) + } +} + +impl std::convert::From<&ServerError> for FlowyResponse { + fn from(error: &ServerError) -> Self { + FlowyResponse { + data: Bytes::from(vec![]), + error: Some(error.clone()), + } + } +} + +#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone)] +#[repr(u16)] +pub enum Code { + InvalidToken = 1, + Unauthorized = 3, + PayloadOverflow = 4, + PayloadSerdeFail = 5, + PayloadUnexpectedNone = 6, + + ProtobufError = 10, + SerdeError = 11, + + EmailAlreadyExists = 50, + + ConnectRefused = 100, + ConnectTimeout = 101, + ConnectClose = 102, + ConnectCancel = 103, + + SqlError = 200, + + HttpError = 300, + + InternalError = 1000, +} diff --git a/rust-lib/flowy-net/src/lib.rs b/rust-lib/flowy-net/src/lib.rs index 2426021c58..4f8feb7562 100644 --- a/rust-lib/flowy-net/src/lib.rs +++ b/rust-lib/flowy-net/src/lib.rs @@ -1,4 +1,5 @@ pub mod config; +pub mod errors; pub mod future; pub mod request; pub mod response; diff --git a/rust-lib/flowy-net/src/request/request.rs b/rust-lib/flowy-net/src/request/request.rs index b4f4037450..da8c222ff3 100644 --- a/rust-lib/flowy-net/src/request/request.rs +++ b/rust-lib/flowy-net/src/request/request.rs @@ -1,26 +1,111 @@ -use crate::response::{Code, FlowyResponse, ServerError}; +use crate::{ + errors::{Code, ServerError}, + response::FlowyResponse, +}; use bytes::Bytes; use hyper::http; -use protobuf::{Message, ProtobufError}; -use reqwest::{Client, Response}; +use protobuf::ProtobufError; +use reqwest::{Client, Method, Response}; use std::{ convert::{TryFrom, TryInto}, time::Duration, }; use tokio::sync::oneshot; +pub struct HttpRequestBuilder { + url: String, + body: Option, + response: Option, + method: Method, +} + +impl HttpRequestBuilder { + fn new() -> Self { + Self { + url: "".to_owned(), + body: None, + response: None, + method: Method::GET, + } + } + + pub fn get(url: &str) -> Self { + let mut builder = Self::new(); + builder.url = url.to_owned(); + builder.method = Method::GET; + builder + } + + pub fn post(url: &str) -> Self { + let mut builder = Self::new(); + builder.url = url.to_owned(); + builder.method = Method::POST; + builder + } + + pub fn protobuf(mut self, body: T1) -> Result + where + T1: TryInto, + { + let body: Bytes = body.try_into()?; + self.body = Some(body); + Ok(self) + } + + pub async fn send(mut self) -> Result { + let (tx, rx) = oneshot::channel::>(); + // reqwest client is not 'Sync' by channel is. + let url = self.url.clone(); + let body = self.body.take(); + let method = self.method.clone(); + + tokio::spawn(async move { + let client = default_client(); + let mut builder = client.request(method, url); + + if let Some(body) = body { + builder = builder.body(body); + } + + let response = builder.send().await; + tx.send(response); + }); + + let response = rx.await??; + let data = get_response_data(response).await?; + self.response = Some(data); + Ok(self) + } + + pub fn response(mut self) -> Result + where + T2: TryFrom, + { + let data = self.response.take(); + match data { + None => { + let msg = format!("Request: {} receives unexpected empty body", self.url); + Err(ServerError::payload_none(msg)) + }, + Some(data) => Ok(T2::try_from(data)?), + } + } +} + +#[allow(dead_code)] pub async fn http_post(url: &str, data: T1) -> Result where T1: TryInto, T2: TryFrom, { - let request_bytes: Bytes = data.try_into()?; + let body: Bytes = data.try_into()?; let url = url.to_owned(); let (tx, rx) = oneshot::channel::>(); + // reqwest client is not 'Sync' by channel is. tokio::spawn(async move { let client = default_client(); - let response = client.post(&url).body(request_bytes).send().await; + let response = client.post(&url).body(body).send().await; tx.send(response); }); diff --git a/rust-lib/flowy-net/src/response/response.rs b/rust-lib/flowy-net/src/response/response.rs index 59fa4dd12d..b78b0ac508 100644 --- a/rust-lib/flowy-net/src/response/response.rs +++ b/rust-lib/flowy-net/src/response/response.rs @@ -1,71 +1,9 @@ +use crate::errors::{Code, ServerError}; use bytes::Bytes; -use serde::{Deserialize, Serialize, __private::Formatter}; -use serde_repr::*; -use std::{convert::TryInto, error::Error, fmt, fmt::Debug}; +use serde::{Deserialize, Serialize}; +use std::{convert::TryInto, error::Error, fmt::Debug}; use tokio::sync::oneshot::error::RecvError; -#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone)] -pub struct ServerError { - pub code: Code, - pub msg: String, -} - -macro_rules! static_error { - ($name:ident, $status:expr) => { - #[allow(non_snake_case, missing_docs)] - pub fn $name(error: T) -> ServerError { - let msg = format!("{:?}", error); - ServerError { code: $status, msg } - } - }; -} - -impl ServerError { - static_error!(internal, Code::InternalError); - static_error!(http, Code::HttpError); -} - -impl std::fmt::Display for ServerError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let msg = format!("{:?}:{}", self.code, self.msg); - f.write_str(&msg) - } -} - -impl std::convert::From<&ServerError> for FlowyResponse { - fn from(error: &ServerError) -> Self { - FlowyResponse { - data: Bytes::from(vec![]), - error: Some(error.clone()), - } - } -} - -#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone)] -#[repr(u16)] -pub enum Code { - InvalidToken = 1, - Unauthorized = 3, - PayloadOverflow = 4, - PayloadSerdeFail = 5, - - ProtobufError = 6, - SerdeError = 7, - - EmailAlreadyExists = 50, - - ConnectRefused = 100, - ConnectTimeout = 101, - ConnectClose = 102, - ConnectCancel = 103, - - SqlError = 200, - - HttpError = 300, - - InternalError = 1000, -} - #[derive(Debug, Serialize, Deserialize)] pub struct FlowyResponse { pub data: Bytes, diff --git a/rust-lib/flowy-net/src/response/response_http.rs b/rust-lib/flowy-net/src/response/response_http.rs index 689823bf2b..61fc069603 100644 --- a/rust-lib/flowy-net/src/response/response_http.rs +++ b/rust-lib/flowy-net/src/response/response_http.rs @@ -1,7 +1,7 @@ use crate::response::*; -use actix_web::{body::Body, error::ResponseError, BaseHttpResponse, HttpResponse}; -use reqwest::StatusCode; -use serde::Serialize; +use actix_web::{error::ResponseError, HttpResponse}; + +use crate::errors::ServerError; impl ResponseError for ServerError { fn error_response(&self) -> HttpResponse { diff --git a/rust-lib/flowy-user/src/errors.rs b/rust-lib/flowy-user/src/errors.rs index a908e6607f..e5d8283b5b 100644 --- a/rust-lib/flowy-user/src/errors.rs +++ b/rust-lib/flowy-user/src/errors.rs @@ -132,8 +132,8 @@ impl std::convert::From for UserError { } } -impl std::convert::From for UserError { - fn from(error: flowy_net::response::ServerError) -> Self { +impl std::convert::From for UserError { + fn from(error: flowy_net::errors::ServerError) -> Self { ErrorBuilder::new(UserErrCode::NetworkError) .error(error) .build() diff --git a/rust-lib/flowy-user/src/services/user/user_server.rs b/rust-lib/flowy-user/src/services/user/user_server.rs index 1dfa9c153e..09af14e703 100644 --- a/rust-lib/flowy-user/src/services/user/user_server.rs +++ b/rust-lib/flowy-user/src/services/user/user_server.rs @@ -3,7 +3,11 @@ use crate::{ errors::{ErrorBuilder, UserErrCode, UserError}, }; -use flowy_net::{config::SIGN_UP_URL, future::ResultFuture, request::http_post}; +use flowy_net::{ + config::SIGN_UP_URL, + future::ResultFuture, + request::{http_post, HttpRequestBuilder}, +}; use std::sync::Arc; pub trait UserServer { @@ -27,8 +31,12 @@ impl UserServerImpl {} impl UserServer for UserServerImpl { fn sign_up(&self, params: SignUpParams) -> ResultFuture { ResultFuture::new(async move { - let resp = http_post(SIGN_UP_URL.as_ref(), params).await?; - Ok(resp) + let response = HttpRequestBuilder::post(SIGN_UP_URL.as_ref()) + .protobuf(params)? + .send() + .await? + .response()?; + Ok(response) }) }