[rust]: create backend api crate

This commit is contained in:
appflowy 2021-11-08 10:57:33 +08:00
parent 92010853c2
commit 55ea9e6cae
31 changed files with 501 additions and 380 deletions

View File

@ -154,7 +154,7 @@ class OverlayScreen extends StatelessWidget {
color: Colors.orange[200],
child: GestureDetector(
// ignore: avoid_print
onTapDown: (_) => print('Hello Flutter'),
onTapDown: (_) => debugPrint('Hello Flutter'),
child: const Center(child: FlutterLogo(size: 100)),
),
),
@ -202,8 +202,8 @@ class OverlayScreen extends StatelessWidget {
OptionOverlay.showWithAnchor(
context,
items: <String>['Alpha', 'Beta', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel'],
onHover: (value, index) => print('Did hover option $index, value $value'),
onTap: (value, index) => print('Did tap option $index, value $value'),
onHover: (value, index) => debugPrint('Did hover option $index, value $value'),
onTap: (value, index) => debugPrint('Did tap option $index, value $value'),
identifier: 'overlay_options',
anchorContext: buttonContext,
anchorDirection: providerContext.read<OverlayDemoConfiguration>().anchorDirection,

View File

@ -61,7 +61,7 @@ byteorder = {version = "1.3.4"}
async-stream = "0.3.2"
flowy-user-infra = { path = "../rust-lib/flowy-user-infra" }
flowy-workspace-infra = { path = "../rust-lib/flowy-workspace-infra"}
flowy-workspace-infra = { path = "../rust-lib/flowy-workspace-infra" }
flowy-document = { path = "../rust-lib/flowy-document" }
flowy-ws = { path = "../rust-lib/flowy-ws" }
flowy-ot = { path = "../rust-lib/flowy-ot" }
@ -99,13 +99,14 @@ parking_lot = "0.11"
once_cell = "1.7.2"
linkify = "0.5.0"
backend = { path = ".", features = ["flowy_test"]}
flowy-user = { path = "../rust-lib/flowy-user", features = ["http_server"] }
flowy-workspace = { path = "../rust-lib/flowy-workspace", default-features = false, features = ["http_server"] }
flowy-ws = { path = "../rust-lib/flowy-ws" }
flowy-backend-api = { path = "../rust-lib/flowy-backend-api"}
flowy-sdk = { path = "../rust-lib/flowy-sdk", features = ["http_server"] }
flowy-user = { path = "../rust-lib/flowy-user", features = ["http_server"] }
flowy-document = { path = "../rust-lib/flowy-document", features = ["flowy_test", "http_server"] }
flowy-ws = { path = "../rust-lib/flowy-ws" }
flowy-test = { path = "../rust-lib/flowy-test" }
flowy-infra = { path = "../rust-lib/flowy-infra" }
flowy-ot = { path = "../rust-lib/flowy-ot" }
flowy-document = { path = "../rust-lib/flowy-document", features = ["flowy_test", "http_server"] }
flowy-sqlite = { path = "../rust-lib/flowy-sqlite" }
futures-util = "0.3.15"

View File

@ -105,6 +105,7 @@ fn user_scope() -> Scope {
.service(web::resource("/app")
.route(web::post().to(app::create_handler))
.route(web::get().to(app::read_handler))
.route(web::delete().to(app::delete_handler))
.route(web::patch().to(app::update_handler))
)
.service(web::resource("/view")

View File

@ -9,7 +9,7 @@ use sqlx::PgPool;
use crate::service::{
app::{
app::{create_app, read_app, update_app},
app::{create_app, delete_app, read_app, update_app},
sql_builder::check_app_id,
},
user::LoggedUser,
@ -92,20 +92,20 @@ pub async fn update_handler(payload: Payload, pool: Data<PgPool>) -> Result<Http
Ok(FlowyResponse::success().into())
}
// pub async fn delete_handler(payload: Payload, pool: Data<PgPool>) ->
// Result<HttpResponse, ServerError> { let params: DeleteAppParams =
// parse_from_payload(payload).await?; let app_id =
// check_app_id(params.app_id.to_owned())?; let mut transaction = pool
// .begin()
// .await
// .context("Failed to acquire a Postgres connection to delete app")?;
//
// let _ = delete_app(&mut transaction, app_id).await?;
//
// transaction
// .commit()
// .await
// .context("Failed to commit SQL transaction to delete app.")?;
//
// Ok(FlowyResponse::success().into())
// }
pub async fn delete_handler(payload: Payload, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
let params: AppIdentifier = parse_from_payload(payload).await?;
let app_id = check_app_id(params.app_id.to_owned())?;
let mut transaction = pool
.begin()
.await
.context("Failed to acquire a Postgres connection to delete app")?;
let _ = delete_app(&mut transaction, app_id).await?;
transaction
.commit()
.await
.context("Failed to commit SQL transaction to delete app.")?;
Ok(FlowyResponse::success().into())
}

View File

@ -61,6 +61,7 @@ impl SqlBuilder {
self
}
#[allow(dead_code)]
pub fn add_arg_if<'a, T>(self, add: bool, field: &str, arg: T) -> Self
where
T: 'a + Send + Encode<'a, Postgres> + Type<Postgres>,
@ -123,12 +124,9 @@ impl SqlBuilder {
inner.field(field);
});
self.filters
.into_iter()
.enumerate()
.for_each(|(index, filter)| {
inner.and_where_eq(filter, format!("${}", index + 1));
});
self.filters.into_iter().enumerate().for_each(|(index, filter)| {
inner.and_where_eq(filter, format!("${}", index + 1));
});
let sql = inner.sql()?;
Ok((sql, self.fields_args))
@ -136,32 +134,23 @@ impl SqlBuilder {
BuilderType::Update => {
let mut inner = InnerBuilder::update_table(&self.table);
let field_len = self.fields.len();
self.fields
.into_iter()
.enumerate()
.for_each(|(index, field)| {
inner.set(&field, format!("${}", index + 1));
});
self.fields.into_iter().enumerate().for_each(|(index, field)| {
inner.set(&field, format!("${}", index + 1));
});
self.filters
.into_iter()
.enumerate()
.for_each(|(index, filter)| {
let index = index + field_len;
inner.and_where_eq(filter, format!("${}", index + 1));
});
self.filters.into_iter().enumerate().for_each(|(index, filter)| {
let index = index + field_len;
inner.and_where_eq(filter, format!("${}", index + 1));
});
let sql = inner.sql()?;
Ok((sql, self.fields_args))
},
BuilderType::Delete => {
let mut inner = InnerBuilder::delete_from(&self.table);
self.filters
.into_iter()
.enumerate()
.for_each(|(index, filter)| {
inner.and_where_eq(filter, format!("${}", index + 1));
});
self.filters.into_iter().enumerate().for_each(|(index, filter)| {
inner.and_where_eq(filter, format!("${}", index + 1));
});
let sql = inner.sql()?;
Ok((sql, self.fields_args))
},

View File

@ -1,5 +1,6 @@
use crate::helper::{spawn_user_server, TestUserServer};
use flowy_user::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams};
use flowy_net::errors::ErrorCode;
use flowy_user_infra::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams};
#[actix_rt::test]
async fn user_register() {
@ -77,7 +78,7 @@ async fn user_update_password() {
match server.sign_in(sign_in_params).await {
Ok(_) => {},
Err(e) => {
assert_eq!(e.code, flowy_user::errors::ErrorCode::PasswordNotMatch.value());
assert_eq!(e.code, ErrorCode::PasswordNotMatch);
},
}
}

View File

@ -1,6 +1,6 @@
use crate::helper::ViewTest;
use flowy_document::entities::doc::DocIdentifier;
use flowy_workspace::entities::view::ViewIdentifiers;
use flowy_workspace_infra::entities::view::ViewIdentifiers;
#[actix_rt::test]
async fn doc_read() {

View File

@ -1,5 +1,5 @@
use crate::helper::*;
use flowy_workspace::entities::{
use flowy_workspace_infra::entities::{
app::{AppIdentifier, UpdateAppParams},
trash::{TrashIdentifier, TrashIdentifiers, TrashType},
view::{UpdateViewParams, ViewIdentifier},

View File

@ -5,12 +5,14 @@ use backend::{
};
use backend::application::init_app_context;
use flowy_backend_api::{user_request::*, workspace_request::*};
use flowy_document::{
entities::doc::{Doc, DocIdentifier},
prelude::*,
};
use flowy_user::{errors::UserError, prelude::*};
use flowy_workspace::prelude::{server::*, *};
use flowy_net::errors::ServerError;
use flowy_user_infra::entities::*;
use flowy_workspace_infra::entities::prelude::*;
use sqlx::{Connection, Executor, PgConnection, PgPool};
use uuid::Uuid;
@ -31,9 +33,10 @@ impl TestUserServer {
server
}
pub async fn sign_in(&self, params: SignInParams) -> Result<SignInResponse, UserError> {
pub async fn sign_in(&self, params: SignInParams) -> Result<SignInResponse, ServerError> {
let url = format!("{}/api/auth", self.http_addr());
user_sign_in_request(params, &url).await
let resp = user_sign_in_request(params, &url).await?;
Ok(resp)
}
pub async fn sign_out(&self) {
@ -51,9 +54,10 @@ impl TestUserServer {
user_profile
}
pub async fn update_user_profile(&self, params: UpdateUserParams) -> Result<(), UserError> {
pub async fn update_user_profile(&self, params: UpdateUserParams) -> Result<(), ServerError> {
let url = format!("{}/api/user", self.http_addr());
update_user_profile_request(self.user_token(), params, &url).await
let _ = update_user_profile_request(self.user_token(), params, &url).await?;
Ok(())
}
pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace {

View File

@ -19,6 +19,7 @@ members = [
"flowy-ot",
"flowy-net",
"flowy-ws",
"flowy-backend-api",
]
exclude = ["../backend"]

View File

@ -7,11 +7,11 @@ edition = "2018"
[lib]
name = "dart_ffi"
# this value will change depending on the target os
# for iOS it would be `cdylib`
# for Macos it would be `cdylib`
# for iOS it would be `rlib`
# for Macos it would be `rlib`
# for android it would be `c-dylib`
# default cdylib
crate-type = ["cdylib"]
# default rlib
crate-type = ["rlib"]
[dependencies]

View File

@ -0,0 +1,14 @@
[package]
name = "flowy-backend-api"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
flowy-net = { path = "../flowy-net", features = ["flowy_request"] }
flowy-workspace-infra = { path = "../flowy-workspace-infra" }
flowy-user-infra = { path = "../flowy-user-infra" }
log = "0.4.14"
lazy_static = "1.4.0"
tokio = { version = "1", features = ["rt"] }

View File

@ -0,0 +1,3 @@
pub mod middleware;
pub mod user_request;
pub mod workspace_request;

View File

@ -0,0 +1,39 @@
use flowy_net::{request::ResponseMiddleware, response::FlowyResponse};
use lazy_static::lazy_static;
use std::sync::Arc;
use tokio::sync::broadcast;
lazy_static! {
pub static ref BACKEND_API_MIDDLEWARE: Arc<WorkspaceMiddleware> = Arc::new(WorkspaceMiddleware::new());
}
pub struct WorkspaceMiddleware {
invalid_token_sender: broadcast::Sender<String>,
}
impl WorkspaceMiddleware {
fn new() -> Self {
let (sender, _) = broadcast::channel(10);
WorkspaceMiddleware {
invalid_token_sender: sender,
}
}
pub fn invalid_token_subscribe(&self) -> broadcast::Receiver<String> { self.invalid_token_sender.subscribe() }
}
impl ResponseMiddleware for WorkspaceMiddleware {
fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
if let Some(error) = &response.error {
if error.is_unauthorized() {
log::error!("user is unauthorized");
match token {
None => {},
Some(token) => match self.invalid_token_sender.send(token.clone()) {
Ok(_) => {},
Err(e) => log::error!("{:?}", e),
},
}
}
}
}
}

View File

@ -0,0 +1,52 @@
use flowy_net::{config::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
use flowy_user_infra::entities::prelude::*;
pub(crate) fn request_builder() -> HttpRequestBuilder {
HttpRequestBuilder::new().middleware(super::middleware::BACKEND_API_MIDDLEWARE.clone())
}
pub async fn user_sign_up_request(params: SignUpParams, url: &str) -> Result<SignUpResponse, ServerError> {
let response = request_builder()
.post(&url.to_owned())
.protobuf(params)?
.response()
.await?;
Ok(response)
}
pub async fn user_sign_in_request(params: SignInParams, url: &str) -> Result<SignInResponse, ServerError> {
let response = request_builder()
.post(&url.to_owned())
.protobuf(params)?
.response()
.await?;
Ok(response)
}
pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.send()
.await?;
Ok(())
}
pub async fn get_user_profile_request(token: &str, url: &str) -> Result<UserProfile, ServerError> {
let user_profile = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.response()
.await?;
Ok(user_profile)
}
pub async fn update_user_profile_request(token: &str, params: UpdateUserParams, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}

View File

@ -0,0 +1,176 @@
use flowy_net::{config::HEADER_TOKEN, errors::ServerError, request::HttpRequestBuilder};
use flowy_workspace_infra::entities::prelude::*;
pub(crate) fn request_builder() -> HttpRequestBuilder {
HttpRequestBuilder::new().middleware(super::middleware::BACKEND_API_MIDDLEWARE.clone())
}
pub async fn create_workspace_request(
token: &str,
params: CreateWorkspaceParams,
url: &str,
) -> Result<Workspace, ServerError> {
let workspace = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(workspace)
}
pub async fn read_workspaces_request(
token: &str,
params: QueryWorkspaceParams,
url: &str,
) -> Result<RepeatedWorkspace, ServerError> {
let repeated_workspace = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response::<RepeatedWorkspace>()
.await?;
Ok(repeated_workspace)
}
pub async fn update_workspace_request(
token: &str,
params: UpdateWorkspaceParams,
url: &str,
) -> Result<(), ServerError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_workspace_request(
token: &str,
params: DeleteWorkspaceParams,
url: &str,
) -> Result<(), ServerError> {
let _ = request_builder()
.delete(url)
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
// App
pub async fn create_app_request(token: &str, params: CreateAppParams, url: &str) -> Result<App, ServerError> {
let app = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(app)
}
pub async fn read_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<Option<App>, ServerError> {
let app = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.option_response()
.await?;
Ok(app)
}
pub async fn update_app_request(token: &str, params: UpdateAppParams, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
// View
pub async fn create_view_request(token: &str, params: CreateViewParams, url: &str) -> Result<View, ServerError> {
let view = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(view)
}
pub async fn read_view_request(token: &str, params: ViewIdentifier, url: &str) -> Result<Option<View>, ServerError> {
let view = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.option_response()
.await?;
Ok(view)
}
pub async fn update_view_request(token: &str, params: UpdateViewParams, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_view_request(token: &str, params: ViewIdentifiers, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn create_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), ServerError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn read_trash_request(token: &str, url: &str) -> Result<RepeatedTrash, ServerError> {
let repeated_trash = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.response::<RepeatedTrash>()
.await?;
Ok(repeated_trash)
}

View File

@ -17,6 +17,7 @@ lazy_static! {
}
pub struct Builder {
#[allow(dead_code)]
name: String,
env_filter: String,
file_appender: RollingFileAppender,

View File

@ -10,7 +10,7 @@ flowy-dispatch = { path = "../flowy-dispatch"}
flowy-log = { path = "../flowy-log" }
flowy-user = { path = "../flowy-user" }
flowy-infra = { path = "../flowy-infra" }
flowy-workspace = { path = "../flowy-workspace" }
flowy-workspace = { path = "../flowy-workspace", default-features = false }
flowy-database = { path = "../flowy-database" }
flowy-document = { path = "../flowy-document" }
flowy-ws = { path = "../flowy-ws" }

View File

@ -9,7 +9,7 @@ edition = "2018"
flowy-sdk = { path = "../flowy-sdk"}
flowy-dispatch = { path = "../flowy-dispatch"}
flowy-user = { path = "../flowy-user"}
flowy-workspace = { path = "../flowy-workspace"}
flowy-workspace = { path = "../flowy-workspace", default-features = false}
flowy-infra = { path = "../flowy-infra"}
flowy-document = { path = "../flowy-document"}
flowy-net = { path = "../flowy-net"}

View File

@ -3,3 +3,7 @@ pub use user_profile::*;
pub mod auth;
mod user_profile;
pub mod prelude {
pub use crate::entities::{auth::*, user_profile::*};
}

View File

@ -7,6 +7,7 @@ edition = "2018"
[dependencies]
flowy-user-infra = { path = "../flowy-user-infra" }
flowy-backend-api = { path = "../flowy-backend-api" }
derive_more = {version = "0.99", features = ["display"]}
flowy-dispatch = { path = "../flowy-dispatch" }
flowy-derive = { path = "../flowy-derive" }

View File

@ -1,14 +1,11 @@
use crate::{
entities::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UserProfile},
entities::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile},
errors::UserError,
services::server::UserServerAPI,
};
use crate::{entities::UpdateUserParams, services::server::UserServerAPI};
use flowy_backend_api::user_request::*;
use flowy_infra::future::ResultFuture;
use flowy_net::{
config::*,
request::{HttpRequestBuilder, ResponseMiddleware},
};
use flowy_net::config::*;
pub struct UserServer {
config: ServerConfig,
@ -20,12 +17,18 @@ impl UserServer {
impl UserServerAPI for UserServer {
fn sign_up(&self, params: SignUpParams) -> ResultFuture<SignUpResponse, UserError> {
let url = self.config.sign_up_url();
ResultFuture::new(async move { user_sign_up_request(params, &url).await })
ResultFuture::new(async move {
let resp = user_sign_up_request(params, &url).await?;
Ok(resp)
})
}
fn sign_in(&self, params: SignInParams) -> ResultFuture<SignInResponse, UserError> {
let url = self.config.sign_in_url();
ResultFuture::new(async move { user_sign_in_request(params, &url).await })
ResultFuture::new(async move {
let resp = user_sign_in_request(params, &url).await?;
Ok(resp)
})
}
fn sign_out(&self, token: &str) -> ResultFuture<(), UserError> {
@ -40,91 +43,47 @@ impl UserServerAPI for UserServer {
fn update_user(&self, token: &str, params: UpdateUserParams) -> ResultFuture<(), UserError> {
let token = token.to_owned();
let url = self.config.user_profile_url();
ResultFuture::new(async move { update_user_profile_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = update_user_profile_request(&token, params, &url).await?;
Ok(())
})
}
fn get_user(&self, token: &str) -> ResultFuture<UserProfile, UserError> {
let token = token.to_owned();
let url = self.config.user_profile_url();
ResultFuture::new(async move { get_user_profile_request(&token, &url).await })
ResultFuture::new(async move {
let profile = get_user_profile_request(&token, &url).await?;
Ok(profile)
})
}
fn ws_addr(&self) -> String { self.config.ws_addr() }
}
use crate::notify::*;
use flowy_net::response::FlowyResponse;
use flowy_user_infra::errors::ErrorCode;
use lazy_static::lazy_static;
use std::sync::Arc;
lazy_static! {
static ref MIDDLEWARE: Arc<Middleware> = Arc::new(Middleware {});
}
// use crate::notify::*;
// use flowy_net::response::FlowyResponse;
// use flowy_user_infra::errors::ErrorCode;
struct Middleware {}
impl ResponseMiddleware for Middleware {
fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
if let Some(error) = &response.error {
if error.is_unauthorized() {
log::error!("user unauthorized");
match token {
None => {},
Some(token) => {
let error = UserError::new(ErrorCode::UserUnauthorized, "");
dart_notify(token, UserNotification::UserUnauthorized)
.error(error)
.send()
},
}
}
}
}
}
pub(crate) fn request_builder() -> HttpRequestBuilder { HttpRequestBuilder::new().middleware(MIDDLEWARE.clone()) }
pub async fn user_sign_up_request(params: SignUpParams, url: &str) -> Result<SignUpResponse, UserError> {
let response = request_builder()
.post(&url.to_owned())
.protobuf(params)?
.response()
.await?;
Ok(response)
}
pub async fn user_sign_in_request(params: SignInParams, url: &str) -> Result<SignInResponse, UserError> {
let response = request_builder()
.post(&url.to_owned())
.protobuf(params)?
.response()
.await?;
Ok(response)
}
pub async fn user_sign_out_request(token: &str, url: &str) -> Result<(), UserError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.send()
.await?;
Ok(())
}
pub async fn get_user_profile_request(token: &str, url: &str) -> Result<UserProfile, UserError> {
let user_profile = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.response()
.await?;
Ok(user_profile)
}
pub async fn update_user_profile_request(token: &str, params: UpdateUserParams, url: &str) -> Result<(), UserError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
// struct Middleware {}
//
//
//
// impl ResponseMiddleware for Middleware {
// fn receive_response(&self, token: &Option<String>, response:
// &FlowyResponse) { if let Some(error) = &response.error {
// if error.is_unauthorized() {
// log::error!("user unauthorized");
// match token {
// None => {},
// Some(token) => {
// let error =
// UserError::new(ErrorCode::UserUnauthorized, "");
// dart_notify(token, UserNotification::UserUnauthorized)
// .error(error) .send()
// },
// }
// }
// }
// }
// }

View File

@ -14,4 +14,10 @@ strum = "0.21"
strum_macros = "0.21"
derive_more = {version = "0.99", features = ["display"]}
log = "0.4.14"
flowy-document = { path = "../flowy-document" }
flowy-document = { path = "../flowy-document" }
[features]
default = []
backend = []
frontend = []

View File

@ -2,3 +2,7 @@ pub mod app;
pub mod trash;
pub mod view;
pub mod workspace;
pub mod prelude {
pub use crate::entities::{app::*, trash::*, view::*, workspace::*};
}

View File

@ -1,6 +1,9 @@
pub mod entities;
pub mod errors;
pub mod parser;
#[macro_use]
mod macros;
// #[cfg(feature = "backend")]
pub mod protobuf;

View File

@ -16,6 +16,7 @@ flowy-dart-notify = { path = "../flowy-dart-notify" }
flowy-document = { path = "../flowy-document" }
flowy-ot = { path = "../flowy-ot" }
flowy-net = { path = "../flowy-net", features = ["flowy_request"] }
flowy-backend-api = { path = "../flowy-backend-api"}
parking_lot = "0.11"
protobuf = {version = "2.18.0"}
@ -44,6 +45,6 @@ crossbeam-utils = "0.8"
flowy-test = { path = "../flowy-test" }
serial_test = "0.5.1"
[features]
default = []
http_server = []

View File

@ -1,35 +0,0 @@
use std::sync::Arc;
use lazy_static::lazy_static;
use flowy_net::{request::ResponseMiddleware, response::FlowyResponse};
use crate::{
errors::{ErrorCode, WorkspaceError},
notify::*,
};
lazy_static! {
pub(crate) static ref MIDDLEWARE: Arc<WorkspaceMiddleware> = Arc::new(WorkspaceMiddleware {});
}
pub(crate) struct WorkspaceMiddleware {}
impl ResponseMiddleware for WorkspaceMiddleware {
fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
if let Some(error) = &response.error {
if error.is_unauthorized() {
log::error!("workspace user is unauthorized");
match token {
None => {},
Some(token) => {
let error = WorkspaceError::new(ErrorCode::UserUnauthorized, "");
send_dart_notification(token, WorkspaceNotification::UserUnauthorized)
.error(error)
.send()
},
}
}
}
}
}

View File

@ -1,4 +1,3 @@
mod middleware;
mod server_api;
mod server_api_mock;
@ -29,6 +28,8 @@ use std::sync::Arc;
pub(crate) type Server = Arc<dyn WorkspaceServerAPI + Send + Sync>;
pub trait WorkspaceServerAPI {
fn init(&self);
// Workspace
fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError>;

View File

@ -13,10 +13,13 @@ use crate::{
},
},
errors::WorkspaceError,
notify::{send_dart_notification, WorkspaceNotification},
services::server::WorkspaceServerAPI,
};
use flowy_backend_api::{middleware::*, workspace_request::*};
use flowy_infra::future::ResultFuture;
use flowy_net::{config::*, request::HttpRequestBuilder};
use flowy_net::config::ServerConfig;
use flowy_workspace_infra::errors::ErrorCode;
pub struct WorkspaceServer {
config: ServerConfig,
@ -27,10 +30,30 @@ impl WorkspaceServer {
}
impl WorkspaceServerAPI for WorkspaceServer {
fn init(&self) {
let mut rx = BACKEND_API_MIDDLEWARE.invalid_token_subscribe();
tokio::spawn(async move {
loop {
match rx.recv().await {
Ok(invalid_token) => {
let error = WorkspaceError::new(ErrorCode::UserUnauthorized, "");
send_dart_notification(&invalid_token, WorkspaceNotification::UserUnauthorized)
.error(error)
.send()
},
Err(_) => {},
}
}
});
}
fn create_workspace(&self, token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> {
let token = token.to_owned();
let url = self.config.workspace_url();
ResultFuture::new(async move { create_workspace_request(&token, params, &url).await })
ResultFuture::new(async move {
let workspace = create_workspace_request(&token, params, &url).await?;
Ok(workspace)
})
}
fn read_workspace(
@ -40,257 +63,126 @@ impl WorkspaceServerAPI for WorkspaceServer {
) -> ResultFuture<RepeatedWorkspace, WorkspaceError> {
let token = token.to_owned();
let url = self.config.workspace_url();
ResultFuture::new(async move { read_workspaces_request(&token, params, &url).await })
ResultFuture::new(async move {
let repeated_workspace = read_workspaces_request(&token, params, &url).await?;
Ok(repeated_workspace)
})
}
fn update_workspace(&self, token: &str, params: UpdateWorkspaceParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.workspace_url();
ResultFuture::new(async move { update_workspace_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = update_workspace_request(&token, params, &url).await?;
Ok(())
})
}
fn delete_workspace(&self, token: &str, params: DeleteWorkspaceParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.workspace_url();
ResultFuture::new(async move { delete_workspace_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = delete_workspace_request(&token, params, &url).await?;
Ok(())
})
}
fn create_view(&self, token: &str, params: CreateViewParams) -> ResultFuture<View, WorkspaceError> {
let token = token.to_owned();
let url = self.config.view_url();
ResultFuture::new(async move { create_view_request(&token, params, &url).await })
ResultFuture::new(async move {
let view = create_view_request(&token, params, &url).await?;
Ok(view)
})
}
fn read_view(&self, token: &str, params: ViewIdentifier) -> ResultFuture<Option<View>, WorkspaceError> {
let token = token.to_owned();
let url = self.config.view_url();
ResultFuture::new(async move { read_view_request(&token, params, &url).await })
ResultFuture::new(async move {
let view = read_view_request(&token, params, &url).await?;
Ok(view)
})
}
fn delete_view(&self, token: &str, params: ViewIdentifiers) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.view_url();
ResultFuture::new(async move { delete_view_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = delete_view_request(&token, params, &url).await?;
Ok(())
})
}
fn update_view(&self, token: &str, params: UpdateViewParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.view_url();
ResultFuture::new(async move { update_view_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = update_view_request(&token, params, &url).await?;
Ok(())
})
}
fn create_app(&self, token: &str, params: CreateAppParams) -> ResultFuture<App, WorkspaceError> {
let token = token.to_owned();
let url = self.config.app_url();
ResultFuture::new(async move { create_app_request(&token, params, &url).await })
ResultFuture::new(async move {
let app = create_app_request(&token, params, &url).await?;
Ok(app)
})
}
fn read_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<Option<App>, WorkspaceError> {
let token = token.to_owned();
let url = self.config.app_url();
ResultFuture::new(async move { read_app_request(&token, params, &url).await })
ResultFuture::new(async move {
let app = read_app_request(&token, params, &url).await?;
Ok(app)
})
}
fn update_app(&self, token: &str, params: UpdateAppParams) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.app_url();
ResultFuture::new(async move { update_app_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = update_app_request(&token, params, &url).await?;
Ok(())
})
}
fn delete_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.app_url();
ResultFuture::new(async move { delete_app_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = delete_app_request(&token, params, &url).await?;
Ok(())
})
}
fn create_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.trash_url();
ResultFuture::new(async move { create_trash_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = create_trash_request(&token, params, &url).await?;
Ok(())
})
}
fn delete_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.trash_url();
ResultFuture::new(async move { delete_trash_request(&token, params, &url).await })
ResultFuture::new(async move {
let _ = delete_trash_request(&token, params, &url).await?;
Ok(())
})
}
fn read_trash(&self, token: &str) -> ResultFuture<RepeatedTrash, WorkspaceError> {
let token = token.to_owned();
let url = self.config.trash_url();
ResultFuture::new(async move { read_trash_request(&token, &url).await })
ResultFuture::new(async move {
let repeated_trash = read_trash_request(&token, &url).await?;
Ok(repeated_trash)
})
}
}
pub(crate) fn request_builder() -> HttpRequestBuilder {
HttpRequestBuilder::new().middleware(super::middleware::MIDDLEWARE.clone())
}
pub async fn create_workspace_request(
token: &str,
params: CreateWorkspaceParams,
url: &str,
) -> Result<Workspace, WorkspaceError> {
let workspace = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(workspace)
}
pub async fn read_workspaces_request(
token: &str,
params: QueryWorkspaceParams,
url: &str,
) -> Result<RepeatedWorkspace, WorkspaceError> {
let repeated_workspace = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response::<RepeatedWorkspace>()
.await?;
Ok(repeated_workspace)
}
pub async fn update_workspace_request(
token: &str,
params: UpdateWorkspaceParams,
url: &str,
) -> Result<(), WorkspaceError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_workspace_request(
token: &str,
params: DeleteWorkspaceParams,
url: &str,
) -> Result<(), WorkspaceError> {
let _ = request_builder()
.delete(url)
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
// App
pub async fn create_app_request(token: &str, params: CreateAppParams, url: &str) -> Result<App, WorkspaceError> {
let app = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(app)
}
pub async fn read_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<Option<App>, WorkspaceError> {
let app = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.option_response()
.await?;
Ok(app)
}
pub async fn update_app_request(token: &str, params: UpdateAppParams, url: &str) -> Result<(), WorkspaceError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<(), WorkspaceError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
// View
pub async fn create_view_request(token: &str, params: CreateViewParams, url: &str) -> Result<View, WorkspaceError> {
let view = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.response()
.await?;
Ok(view)
}
pub async fn read_view_request(token: &str, params: ViewIdentifier, url: &str) -> Result<Option<View>, WorkspaceError> {
let view = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.option_response()
.await?;
Ok(view)
}
pub async fn update_view_request(token: &str, params: UpdateViewParams, url: &str) -> Result<(), WorkspaceError> {
let _ = request_builder()
.patch(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_view_request(token: &str, params: ViewIdentifiers, url: &str) -> Result<(), WorkspaceError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn create_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), WorkspaceError> {
let _ = request_builder()
.post(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn delete_trash_request(token: &str, params: TrashIdentifiers, url: &str) -> Result<(), WorkspaceError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)
.protobuf(params)?
.send()
.await?;
Ok(())
}
pub async fn read_trash_request(token: &str, url: &str) -> Result<RepeatedTrash, WorkspaceError> {
let repeated_trash = request_builder()
.get(&url.to_owned())
.header(HEADER_TOKEN, token)
.response::<RepeatedTrash>()
.await?;
Ok(repeated_trash)
}

View File

@ -20,6 +20,8 @@ use flowy_infra::{future::ResultFuture, timestamp, uuid};
pub struct WorkspaceServerMock {}
impl WorkspaceServerAPI for WorkspaceServerMock {
fn init(&self) {}
fn create_workspace(&self, _token: &str, params: CreateWorkspaceParams) -> ResultFuture<Workspace, WorkspaceError> {
let time = timestamp();
let workspace = Workspace {

View File

@ -44,6 +44,7 @@ impl WorkspaceController {
}
pub fn init(&self) -> Result<(), WorkspaceError> {
let _ = self.server.init();
let _ = self.trash_can.init()?;
let _ = self.view_controller.init()?;
let _ = self.app_controller.init()?;