mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
config doc reqeust
This commit is contained in:
parent
a5a07944dc
commit
2b4a9c2497
@ -77,7 +77,6 @@ impl Token {
|
||||
use actix_web::{dev::Payload, FromRequest, HttpRequest};
|
||||
use flowy_net::config::HEADER_TOKEN;
|
||||
use futures::future::{ready, Ready};
|
||||
use std::convert::TryInto;
|
||||
|
||||
impl FromRequest for Token {
|
||||
type Config = ();
|
||||
|
@ -46,9 +46,8 @@ impl LoggedUser {
|
||||
}
|
||||
|
||||
use actix_web::{dev::Payload, FromRequest, HttpRequest};
|
||||
use flowy_net::config::HEADER_TOKEN;
|
||||
|
||||
use futures::future::{ready, Ready};
|
||||
use std::convert::TryInto;
|
||||
|
||||
impl FromRequest for LoggedUser {
|
||||
type Config = ();
|
||||
|
@ -18,7 +18,7 @@ use flowy_workspace::{
|
||||
app::parser::{AppDesc, AppName},
|
||||
workspace::parser::WorkspaceId,
|
||||
},
|
||||
protobuf::{CreateAppParams, QueryAppParams, RepeatedApp, RepeatedView, UpdateAppParams},
|
||||
protobuf::{CreateAppParams, QueryAppParams, RepeatedView, UpdateAppParams},
|
||||
};
|
||||
use protobuf::Message;
|
||||
use sqlx::{postgres::PgArguments, PgPool, Postgres};
|
||||
|
@ -146,7 +146,7 @@ pub async fn spawn_app() -> TestApp {
|
||||
|
||||
let _ = tokio::spawn(async {
|
||||
let _ = application.run_until_stopped();
|
||||
drop_test_database(database_name).await;
|
||||
// drop_test_database(database_name).await;
|
||||
});
|
||||
|
||||
TestApp {
|
||||
@ -181,6 +181,7 @@ async fn configure_database(config: &DatabaseSettings) -> PgPool {
|
||||
connection_pool
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn drop_test_database(database_name: String) {
|
||||
// https://stackoverflow.com/questions/36502401/postgres-drop-database-error-pq-cannot-drop-the-currently-open-database?rq=1
|
||||
let configuration = {
|
||||
|
@ -9,7 +9,6 @@ edition = "2018"
|
||||
[dependencies]
|
||||
derive_more = {version = "0.99", features = ["display"]}
|
||||
flowy-dispatch = { path = "../flowy-dispatch" }
|
||||
flowy-log = { path = "../flowy-log" }
|
||||
flowy-derive = { path = "../flowy-derive" }
|
||||
flowy-database = { path = "../flowy-database" }
|
||||
flowy-infra = { path = "../flowy-infra" }
|
||||
|
@ -27,7 +27,7 @@ impl TryInto<CreateDocParams> for CreateDocRequest {
|
||||
type Error = DocError;
|
||||
|
||||
fn try_into(self) -> Result<CreateDocParams, Self::Error> {
|
||||
let id = DocViewId::parse(self.id)
|
||||
let id = DocId::parse(self.id)
|
||||
.map_err(|e| ErrorBuilder::new(ErrorCode::DocIdInvalid).msg(e).build())?
|
||||
.0;
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
entities::doc::parser::{DocId, DocPath},
|
||||
errors::*,
|
||||
};
|
||||
use crate::{entities::doc::parser::DocId, errors::*};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use std::convert::TryInto;
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DocDesc(pub String);
|
||||
|
||||
impl DocDesc {
|
||||
pub fn parse(s: String) -> Result<DocDesc, String> {
|
||||
if s.graphemes(true).count() > 1000 {
|
||||
return Err(format!("Doc desc too long"));
|
||||
}
|
||||
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#[derive(Debug)]
|
||||
pub struct DocName(pub String);
|
||||
|
||||
impl DocName {
|
||||
pub fn parse(s: String) -> Result<DocName, String> {
|
||||
if s.trim().is_empty() {
|
||||
return Err(format!("Doc name can not be empty or whitespace"));
|
||||
}
|
||||
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#[derive(Debug)]
|
||||
pub struct DocPath(pub String);
|
||||
|
||||
impl DocPath {
|
||||
pub fn parse(s: String) -> Result<DocPath, String> {
|
||||
if s.trim().is_empty() {
|
||||
return Err(format!("Doc path can not be empty or whitespace"));
|
||||
}
|
||||
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#[derive(Debug)]
|
||||
pub struct DocViewId(pub String);
|
||||
|
||||
impl DocViewId {
|
||||
pub fn parse(s: String) -> Result<DocViewId, String> {
|
||||
if s.trim().is_empty() {
|
||||
return Err(format!("Doc view id can not be empty or whitespace"));
|
||||
}
|
||||
|
||||
Ok(Self(s))
|
||||
}
|
||||
}
|
@ -1,11 +1,3 @@
|
||||
mod doc_desc;
|
||||
mod doc_id;
|
||||
mod doc_name;
|
||||
mod doc_path;
|
||||
mod doc_view_id;
|
||||
|
||||
pub use doc_desc::*;
|
||||
pub use doc_id::*;
|
||||
pub use doc_name::*;
|
||||
pub use doc_path::*;
|
||||
pub use doc_view_id::*;
|
||||
|
@ -1,11 +1,6 @@
|
||||
use crate::{
|
||||
entities::doc::*,
|
||||
errors::DocError,
|
||||
services::{doc_controller::DocController, file_manager::FileManager},
|
||||
};
|
||||
use crate::{entities::doc::*, errors::DocError, services::doc_controller::DocController};
|
||||
use flowy_dispatch::prelude::*;
|
||||
use std::{convert::TryInto, path::Path};
|
||||
use tokio::sync::RwLock;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[tracing::instrument(skip(data, controller))]
|
||||
pub async fn create_doc_handler(data: Data<CreateDocRequest>, controller: Unit<DocController>) -> DataResult<Doc, DocError> {
|
||||
@ -23,7 +18,7 @@ pub async fn read_doc_handler(data: Data<QueryDocRequest>, controller: Unit<DocC
|
||||
|
||||
#[tracing::instrument(skip(data, controller))]
|
||||
pub async fn update_doc_handler(data: Data<UpdateDocRequest>, controller: Unit<DocController>) -> Result<(), DocError> {
|
||||
let mut params: UpdateDocParams = data.into_inner().try_into()?;
|
||||
let params: UpdateDocParams = data.into_inner().try_into()?;
|
||||
let _ = controller.update_doc(params).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
mod observable;
|
||||
|
||||
pub use observable::*;
|
||||
pub(crate) use observable::*;
|
||||
|
@ -10,4 +10,5 @@ impl std::convert::Into<i32> for DocObservable {
|
||||
fn into(self) -> i32 { self as i32 }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn observable(id: &str, ty: DocObservable) -> ObservableBuilder { ObservableBuilder::new(id, ty, OBSERVABLE_CATEGORY) }
|
||||
|
@ -62,14 +62,14 @@ impl DocController {
|
||||
}
|
||||
|
||||
impl DocController {
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
#[tracing::instrument(skip(self), err)]
|
||||
async fn create_doc_on_server(&self, params: CreateDocParams) -> Result<Doc, DocError> {
|
||||
let token = self.user.token()?;
|
||||
let doc = self.server.create_doc(&token, params).await?;
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, params), err)]
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
fn update_doc_on_server(&self, params: UpdateDocParams) -> Result<(), DocError> {
|
||||
let token = self.user.token()?;
|
||||
let server = self.server.clone();
|
||||
@ -85,13 +85,13 @@ impl DocController {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, params), err)]
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
fn read_doc_on_server(&self, params: QueryDocParams) -> Result<(), DocError> {
|
||||
let token = self.user.token()?;
|
||||
let server = self.server.clone();
|
||||
tokio::spawn(async move {
|
||||
// Opti: handle the error and retry?
|
||||
let doc = server.read_doc(&token, params).await?;
|
||||
let _doc = server.read_doc(&token, params).await?;
|
||||
// save to disk
|
||||
// notify
|
||||
|
||||
@ -100,7 +100,7 @@ impl DocController {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, params), err)]
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
fn delete_doc_on_server(&self, params: QueryDocParams) -> Result<(), DocError> {
|
||||
let token = self.user.token()?;
|
||||
let server = self.server.clone();
|
||||
|
@ -48,15 +48,14 @@ pub struct FileInfo {
|
||||
pub encoding: CharacterEncoding,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_load_file<P>(path: P) -> Result<(String, FileInfo), FileError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut f =
|
||||
File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
|
||||
let mut f = File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
|
||||
let mut bytes = Vec::new();
|
||||
f.read_to_end(&mut bytes)
|
||||
.map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
|
||||
f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
|
||||
|
||||
let encoding = CharacterEncoding::guess(&bytes);
|
||||
let s = try_decode(bytes, encoding, path.as_ref())?;
|
||||
@ -69,12 +68,8 @@ where
|
||||
Ok((s, info))
|
||||
}
|
||||
|
||||
pub(crate) fn try_save(
|
||||
path: &Path,
|
||||
text: &str,
|
||||
encoding: CharacterEncoding,
|
||||
_file_info: Option<&FileInfo>,
|
||||
) -> io::Result<()> {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_save(path: &Path, text: &str, encoding: CharacterEncoding, _file_info: Option<&FileInfo>) -> io::Result<()> {
|
||||
let tmp_extension = path.extension().map_or_else(
|
||||
|| OsString::from("swp"),
|
||||
|ext| {
|
||||
@ -97,20 +92,14 @@ pub(crate) fn try_save(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn try_decode(
|
||||
bytes: Vec<u8>,
|
||||
encoding: CharacterEncoding,
|
||||
path: &Path,
|
||||
) -> Result<String, FileError> {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<String, FileError> {
|
||||
match encoding {
|
||||
CharacterEncoding::Utf8 => {
|
||||
Ok(String::from(str::from_utf8(&bytes).map_err(|_e| {
|
||||
FileError::UnknownEncoding(path.to_owned())
|
||||
})?))
|
||||
},
|
||||
CharacterEncoding::Utf8 => Ok(String::from(
|
||||
str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
|
||||
)),
|
||||
CharacterEncoding::Utf8WithBom => {
|
||||
let s = String::from_utf8(bytes)
|
||||
.map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
|
||||
let s = String::from_utf8(bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
|
||||
Ok(String::from(&s[UTF8_BOM.len()..]))
|
||||
},
|
||||
}
|
||||
@ -123,8 +112,5 @@ pub(crate) fn create_dir_if_not_exist(dir: &str) -> Result<(), io::Error> {
|
||||
}
|
||||
|
||||
pub(crate) fn get_modified_time<P: AsRef<Path>>(path: P) -> Option<SystemTime> {
|
||||
File::open(path)
|
||||
.and_then(|f| f.metadata())
|
||||
.and_then(|meta| meta.modified())
|
||||
.ok()
|
||||
File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ impl FileManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn open<T>(&mut self, path: &Path, id: T) -> Result<String, FileError>
|
||||
where
|
||||
T: Into<FileId>,
|
||||
@ -35,6 +36,7 @@ impl FileManager {
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn save<T>(&mut self, path: &Path, text: &String, id: T) -> Result<(), FileError>
|
||||
where
|
||||
T: Into<FileId>,
|
||||
@ -58,12 +60,8 @@ impl FileManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_file(
|
||||
&mut self,
|
||||
id: &str,
|
||||
dir: &str,
|
||||
text: &str,
|
||||
) -> Result<PathBuf, FileError> {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn create_file(&mut self, id: &str, dir: &str, text: &str) -> Result<PathBuf, FileError> {
|
||||
let path = PathBuf::from(format!("{}/{}", dir, id));
|
||||
let file_id: FileId = id.to_owned().into();
|
||||
log::info!("Create doc at: {:?}", path);
|
||||
@ -75,9 +73,7 @@ impl FileManager {
|
||||
pub(crate) fn get_info(&self, id: &FileId) -> Option<&FileInfo> { self.file_info.get(id) }
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn get_file_id(&self, path: &Path) -> Option<FileId> {
|
||||
self.open_files.get(path).cloned()
|
||||
}
|
||||
pub(crate) fn get_file_id(&self, path: &Path) -> Option<FileId> { self.open_files.get(path).cloned() }
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn check_file(&mut self, path: &Path, id: &FileId) -> bool {
|
||||
@ -91,9 +87,9 @@ impl FileManager {
|
||||
false
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn save_new(&mut self, path: &Path, text: &str, id: &FileId) -> Result<(), FileError> {
|
||||
try_save(path, text, CharacterEncoding::Utf8, self.get_info(id))
|
||||
.map_err(|e| FileError::Io(e, path.to_owned()))?;
|
||||
try_save(path, text, CharacterEncoding::Utf8, self.get_info(id)).map_err(|e| FileError::Io(e, path.to_owned()))?;
|
||||
let info = FileInfo {
|
||||
encoding: CharacterEncoding::Utf8,
|
||||
path: path.to_owned(),
|
||||
@ -105,6 +101,7 @@ impl FileManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn save_existing(&mut self, path: &Path, text: &String, id: &FileId) -> Result<(), FileError> {
|
||||
let prev_path = self.file_info[id].path.clone();
|
||||
if prev_path != path {
|
||||
@ -114,8 +111,7 @@ impl FileManager {
|
||||
return Err(FileError::HasChanged(path.to_owned()));
|
||||
} else {
|
||||
let encoding = self.file_info[&id].encoding;
|
||||
try_save(path, text, encoding, self.get_info(id))
|
||||
.map_err(|e| FileError::Io(e, path.to_owned()))?;
|
||||
try_save(path, text, encoding, self.get_info(id)).map_err(|e| FileError::Io(e, path.to_owned()))?;
|
||||
self.file_info.get_mut(&id).unwrap().modified_time = get_modified_time(path);
|
||||
}
|
||||
Ok(())
|
||||
|
@ -15,7 +15,7 @@ impl ResponseMiddleware for DocMiddleware {
|
||||
|
||||
match token {
|
||||
None => {},
|
||||
Some(token) => {
|
||||
Some(_token) => {
|
||||
// let error =
|
||||
// WorkspaceError::new(ErrorCode::UserUnauthorized, "");
|
||||
// observable(token,
|
||||
|
@ -4,18 +4,30 @@ use crate::{
|
||||
services::server::DocumentServerAPI,
|
||||
};
|
||||
use flowy_infra::future::ResultFuture;
|
||||
use flowy_net::{config::HEADER_TOKEN, request::HttpRequestBuilder};
|
||||
use flowy_net::{config::*, request::HttpRequestBuilder};
|
||||
|
||||
pub struct DocServer {}
|
||||
|
||||
impl DocumentServerAPI for DocServer {
|
||||
fn create_doc(&self, token: &str, params: CreateDocParams) -> ResultFuture<Doc, DocError> { unimplemented!() }
|
||||
fn create_doc(&self, token: &str, params: CreateDocParams) -> ResultFuture<Doc, DocError> {
|
||||
let token = token.to_owned();
|
||||
ResultFuture::new(async move { create_doc_request(&token, params, DOC_URL.as_ref()).await })
|
||||
}
|
||||
|
||||
fn read_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<Option<Doc>, DocError> { unimplemented!() }
|
||||
fn read_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<Option<Doc>, DocError> {
|
||||
let token = token.to_owned();
|
||||
ResultFuture::new(async move { read_doc_request(&token, params, DOC_URL.as_ref()).await })
|
||||
}
|
||||
|
||||
fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError> { unimplemented!() }
|
||||
fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError> {
|
||||
let token = token.to_owned();
|
||||
ResultFuture::new(async move { update_doc_request(&token, params, DOC_URL.as_ref()).await })
|
||||
}
|
||||
|
||||
fn delete_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<(), DocError> { unimplemented!() }
|
||||
fn delete_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<(), DocError> {
|
||||
let token = token.to_owned();
|
||||
ResultFuture::new(async move { delete_doc_request(&token, params, DOC_URL.as_ref()).await })
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn request_builder() -> HttpRequestBuilder { HttpRequestBuilder::new().middleware(super::middleware::MIDDLEWARE.clone()) }
|
||||
@ -25,9 +37,38 @@ pub async fn create_doc_request(token: &str, params: CreateDocParams, url: &str)
|
||||
.post(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await?
|
||||
.response()
|
||||
.await?;
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
pub async fn read_doc_request(token: &str, params: QueryDocParams, url: &str) -> Result<Option<Doc>, DocError> {
|
||||
let doc = request_builder()
|
||||
.get(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.option_response()
|
||||
.await?;
|
||||
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
pub async fn update_doc_request(token: &str, params: UpdateDocParams, url: &str) -> Result<(), DocError> {
|
||||
let _ = request_builder()
|
||||
.patch(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_doc_request(token: &str, params: QueryDocParams, url: &str) -> Result<(), DocError> {
|
||||
let _ = request_builder()
|
||||
.delete(url)
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -3,15 +3,14 @@ use crate::{
|
||||
errors::DocError,
|
||||
services::server::DocumentServerAPI,
|
||||
};
|
||||
use flowy_infra::{future::ResultFuture, uuid};
|
||||
use flowy_infra::future::ResultFuture;
|
||||
pub struct DocServerMock {}
|
||||
|
||||
impl DocumentServerAPI for DocServerMock {
|
||||
fn create_doc(&self, _token: &str, _params: CreateDocParams) -> ResultFuture<Doc, DocError> {
|
||||
let uuid = uuid();
|
||||
fn create_doc(&self, _token: &str, params: CreateDocParams) -> ResultFuture<Doc, DocError> {
|
||||
let doc = Doc {
|
||||
id: uuid,
|
||||
data: "".to_string(),
|
||||
id: params.id,
|
||||
data: params.data,
|
||||
};
|
||||
|
||||
ResultFuture::new(async { Ok(doc) })
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
errors::DocError,
|
||||
module::DocumentDatabase,
|
||||
sql_tables::doc::{DocTable, DocTableChangeset},
|
||||
};
|
||||
use flowy_database::{
|
||||
@ -8,7 +7,7 @@ use flowy_database::{
|
||||
schema::{doc_table, doc_table::dsl},
|
||||
SqliteConnection,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct DocTableSql {}
|
||||
|
||||
impl DocTableSql {
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::entities::doc::{CreateDocParams, Doc, UpdateDocParams};
|
||||
use crate::entities::doc::{Doc, UpdateDocParams};
|
||||
use flowy_database::schema::doc_table;
|
||||
use flowy_infra::timestamp;
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
|
||||
#[table_name = "doc_table"]
|
||||
@ -14,7 +13,7 @@ impl DocTable {
|
||||
pub fn new(doc: Doc) -> Self {
|
||||
Self {
|
||||
id: doc.id,
|
||||
data: "".to_owned(),
|
||||
data: doc.data,
|
||||
version: 0,
|
||||
}
|
||||
}
|
||||
|
@ -2,24 +2,35 @@ use crate::helper::*;
|
||||
use flowy_test::FlowyEnv;
|
||||
|
||||
#[test]
|
||||
fn file_create_test() {
|
||||
fn doc_create_test() {
|
||||
let sdk = FlowyEnv::setup().sdk;
|
||||
let doc_desc = create_doc(&sdk, "hello world", "flutter ❤️ rust", "123");
|
||||
dbg!(&doc_desc);
|
||||
let doc = create_doc(&sdk, "flutter ❤️ rust");
|
||||
dbg!(&doc);
|
||||
|
||||
let doc = read_doc_data(&sdk, &doc_desc.id, &doc_desc.path);
|
||||
assert_eq!(doc.text, "123".to_owned());
|
||||
let doc = read_doc_data(&sdk, &doc.id);
|
||||
assert_eq!(doc.data, "flutter ❤️ rust".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_update_text_test() {
|
||||
fn doc_update_test() {
|
||||
let sdk = FlowyEnv::setup().sdk;
|
||||
let doc_desc = create_doc(&sdk, "hello world", "flutter ❤️ rust", "");
|
||||
let doc_desc = create_doc(&sdk, "flutter ❤️ rust");
|
||||
dbg!(&doc_desc);
|
||||
|
||||
let content = "😁😁😁😁😁😁😁😁😁😁".to_owned();
|
||||
save_doc(&sdk, &doc_desc, &content);
|
||||
|
||||
let doc = read_doc_data(&sdk, &doc_desc.id, &doc_desc.path);
|
||||
assert_eq!(doc.text, content);
|
||||
let doc = read_doc_data(&sdk, &doc_desc.id);
|
||||
assert_eq!(doc.data, content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doc_update_big_data_test() {
|
||||
let sdk = FlowyEnv::setup().sdk;
|
||||
let doc_desc = create_doc(&sdk, "");
|
||||
let content = "flutter ❤️ rust".repeat(1000000);
|
||||
save_doc(&sdk, &doc_desc, &content);
|
||||
|
||||
let doc = read_doc_data(&sdk, &doc_desc.id);
|
||||
assert_eq!(doc.data, content);
|
||||
}
|
||||
|
@ -4,11 +4,9 @@ use flowy_document::{entities::doc::*, event::EditorEvent::*};
|
||||
use flowy_infra::uuid;
|
||||
use flowy_test::prelude::*;
|
||||
|
||||
pub fn create_doc(sdk: &FlowyTestSDK, name: &str, desc: &str, text: &str) -> Doc {
|
||||
pub fn create_doc(sdk: &FlowyTestSDK, text: &str) -> Doc {
|
||||
let request = CreateDocRequest {
|
||||
id: uuid(),
|
||||
name: name.to_owned(),
|
||||
desc: desc.to_owned(),
|
||||
data: text.to_owned(),
|
||||
};
|
||||
|
||||
@ -20,11 +18,9 @@ pub fn create_doc(sdk: &FlowyTestSDK, name: &str, desc: &str, text: &str) -> Doc
|
||||
doc
|
||||
}
|
||||
|
||||
pub fn save_doc(sdk: &FlowyTestSDK, desc: &Doc, content: &str) {
|
||||
pub fn save_doc(sdk: &FlowyTestSDK, doc: &Doc, content: &str) {
|
||||
let request = UpdateDocRequest {
|
||||
id: desc.id.clone(),
|
||||
name: Some(desc.name.clone()),
|
||||
desc: Some(desc.desc.clone()),
|
||||
id: doc.id.clone(),
|
||||
data: Some(content.to_owned()),
|
||||
};
|
||||
|
||||
@ -46,17 +42,12 @@ pub fn save_doc(sdk: &FlowyTestSDK, desc: &Doc, content: &str) {
|
||||
// doc
|
||||
// }
|
||||
|
||||
pub(crate) fn read_doc_data(sdk: &FlowyTestSDK, doc_id: &str, path: &str) -> DocData {
|
||||
let request = QueryDocDataRequest {
|
||||
pub(crate) fn read_doc_data(sdk: &FlowyTestSDK, doc_id: &str) -> Doc {
|
||||
let request = QueryDocRequest {
|
||||
doc_id: doc_id.to_string(),
|
||||
path: path.to_string(),
|
||||
};
|
||||
|
||||
let doc = DocTest::new(sdk.clone())
|
||||
.event(ReadDocData)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.parse::<DocData>();
|
||||
let doc = DocTest::new(sdk.clone()).event(ReadDoc).request(request).sync_send().parse::<Doc>();
|
||||
|
||||
doc
|
||||
}
|
||||
|
@ -14,4 +14,7 @@ lazy_static! {
|
||||
pub static ref WORKSPACE_URL: String = format!("{}/api/workspace", HOST);
|
||||
pub static ref APP_URL: String = format!("{}/api/app", HOST);
|
||||
pub static ref VIEW_URL: String = format!("{}/api/view", HOST);
|
||||
|
||||
//
|
||||
pub static ref DOC_URL: String = format!("{}/api/doc", HOST);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ impl ServerError {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_not_found(&self) -> bool { self.code == ErrorCode::RecordNotFound }
|
||||
pub fn is_record_not_found(&self) -> bool { self.code == ErrorCode::RecordNotFound }
|
||||
|
||||
pub fn is_unauthorized(&self) -> bool { self.code == ErrorCode::UserUnauthorized }
|
||||
}
|
||||
|
@ -1,17 +1,8 @@
|
||||
use crate::{
|
||||
config::HEADER_TOKEN,
|
||||
errors::{ErrorCode, ServerError},
|
||||
response::FlowyResponse,
|
||||
};
|
||||
use crate::{config::HEADER_TOKEN, errors::ServerError, response::FlowyResponse};
|
||||
use bytes::Bytes;
|
||||
use hyper::{http, http::HeaderValue};
|
||||
use hyper::http;
|
||||
use protobuf::ProtobufError;
|
||||
use reqwest::{
|
||||
header::{HeaderMap, ToStrError},
|
||||
Client,
|
||||
Method,
|
||||
Response,
|
||||
};
|
||||
use reqwest::{header::HeaderMap, Client, Method, Response};
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
sync::Arc,
|
||||
@ -94,7 +85,50 @@ impl HttpRequestBuilder {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub async fn send(mut self) -> Result<Self, ServerError> {
|
||||
pub async fn send(self) -> Result<(), ServerError> {
|
||||
let _ = self.inner_send().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn response<T>(self) -> Result<T, ServerError>
|
||||
where
|
||||
T: TryFrom<Bytes, Error = ProtobufError>,
|
||||
{
|
||||
let builder = self.inner_send().await?;
|
||||
match builder.response {
|
||||
None => Err(unexpected_empty_payload(&builder.url)),
|
||||
Some(data) => Ok(T::try_from(data)?),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn option_response<T>(self) -> Result<Option<T>, ServerError>
|
||||
where
|
||||
T: TryFrom<Bytes, Error = ProtobufError>,
|
||||
{
|
||||
let result = self.inner_send().await;
|
||||
match result {
|
||||
Ok(builder) => match builder.response {
|
||||
None => Err(unexpected_empty_payload(&builder.url)),
|
||||
Some(data) => Ok(Some(T::try_from(data)?)),
|
||||
},
|
||||
Err(error) => match error.is_record_not_found() {
|
||||
true => Ok(None),
|
||||
false => Err(error),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn token(&self) -> Option<String> {
|
||||
match self.headers.get(HEADER_TOKEN) {
|
||||
None => None,
|
||||
Some(header) => match header.to_str() {
|
||||
Ok(val) => Some(val.to_owned()),
|
||||
Err(_) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn inner_send(mut self) -> Result<Self, ServerError> {
|
||||
let (tx, rx) = oneshot::channel::<Result<Response, _>>();
|
||||
let url = self.url.clone();
|
||||
let body = self.body.take();
|
||||
@ -132,29 +166,11 @@ impl HttpRequestBuilder {
|
||||
Some(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn response<T2>(self) -> Result<T2, ServerError>
|
||||
where
|
||||
T2: TryFrom<Bytes, Error = ProtobufError>,
|
||||
{
|
||||
match self.response {
|
||||
None => {
|
||||
let msg = format!("Request: {} receives unexpected empty body", self.url);
|
||||
Err(ServerError::payload_none().context(msg))
|
||||
},
|
||||
Some(data) => Ok(T2::try_from(data)?),
|
||||
}
|
||||
}
|
||||
|
||||
fn token(&self) -> Option<String> {
|
||||
match self.headers.get(HEADER_TOKEN) {
|
||||
None => None,
|
||||
Some(header) => match header.to_str() {
|
||||
Ok(val) => Some(val.to_owned()),
|
||||
Err(_) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
fn unexpected_empty_payload(url: &str) -> ServerError {
|
||||
let msg = format!("Request: {} receives unexpected empty payload", url);
|
||||
ServerError::payload_none().context(msg)
|
||||
}
|
||||
|
||||
async fn flowy_response_from(original: Response) -> Result<FlowyResponse, ServerError> {
|
||||
|
@ -27,7 +27,7 @@ impl DocumentUser for EditorUserImpl {
|
||||
let dir = self
|
||||
.user_session
|
||||
.user_dir()
|
||||
.map_err(|e| ErrorBuilder::new(ErrorCode::EditorUserNotLoginYet).error(e).build())?;
|
||||
.map_err(|e| ErrorBuilder::new(ErrorCode::UserUnauthorized).error(e).build())?;
|
||||
|
||||
let doc_dir = format!("{}/doc", dir);
|
||||
if !Path::new(&doc_dir).exists() {
|
||||
|
@ -38,6 +38,7 @@ fn crate_log_filter(level: Option<String>) -> String {
|
||||
filters.push(format!("flowy_sdk={}", level));
|
||||
filters.push(format!("flowy_workspace={}", level));
|
||||
filters.push(format!("flowy_user={}", level));
|
||||
filters.push(format!("flowy_document={}", level));
|
||||
filters.push(format!("flowy_observable={}", level));
|
||||
filters.push(format!("info"));
|
||||
filters.join(",")
|
||||
|
@ -8,7 +8,6 @@ edition = "2018"
|
||||
[dependencies]
|
||||
derive_more = {version = "0.99", features = ["display"]}
|
||||
flowy-dispatch = { path = "../flowy-dispatch" }
|
||||
flowy-log = { path = "../flowy-log" }
|
||||
flowy-derive = { path = "../flowy-derive" }
|
||||
flowy-database = { path = "../flowy-database" }
|
||||
flowy-sqlite = { path = "../flowy-sqlite" }
|
||||
|
@ -1,2 +1,2 @@
|
||||
mod observable;
|
||||
pub use observable::*;
|
||||
pub(crate) use observable::*;
|
||||
|
@ -72,24 +72,12 @@ impl ResponseMiddleware for Middleware {
|
||||
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)?
|
||||
.send()
|
||||
.await?
|
||||
.response()
|
||||
.await?;
|
||||
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)?
|
||||
.send()
|
||||
.await?
|
||||
.response()
|
||||
.await?;
|
||||
let response = request_builder().post(&url.to_owned()).protobuf(params)?.response().await?;
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
@ -102,8 +90,6 @@ pub async fn get_user_profile_request(token: &str, url: &str) -> Result<UserProf
|
||||
let user_profile = request_builder()
|
||||
.get(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.send()
|
||||
.await?
|
||||
.response()
|
||||
.await?;
|
||||
Ok(user_profile)
|
||||
|
@ -7,7 +7,6 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
flowy-dispatch = { path = "../flowy-dispatch" }
|
||||
flowy-log = { path = "../flowy-log" }
|
||||
flowy-derive = { path = "../flowy-derive" }
|
||||
flowy-database = { path = "../flowy-database" }
|
||||
flowy-sqlite = { path = "../flowy-sqlite" }
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
};
|
||||
use flowy_net::{request::ResponseMiddleware, response::FlowyResponse};
|
||||
|
||||
struct WorkspaceMiddleware {}
|
||||
pub(crate) struct WorkspaceMiddleware {}
|
||||
impl ResponseMiddleware for WorkspaceMiddleware {
|
||||
fn receive_response(&self, token: &Option<String>, response: &FlowyResponse) {
|
||||
if let Some(error) = &response.error {
|
||||
|
@ -15,12 +15,7 @@ use crate::{
|
||||
services::server::WorkspaceServerAPI,
|
||||
};
|
||||
use flowy_infra::future::ResultFuture;
|
||||
use flowy_net::{
|
||||
config::*,
|
||||
request::{HttpRequestBuilder, ResponseMiddleware},
|
||||
response::FlowyResponse,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use flowy_net::{config::*, request::HttpRequestBuilder};
|
||||
|
||||
pub struct WorkspaceServer {}
|
||||
|
||||
@ -92,27 +87,20 @@ pub async fn create_workspace_request(token: &str, params: CreateWorkspaceParams
|
||||
.post(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await?
|
||||
.response()
|
||||
.await?;
|
||||
Ok(workspace)
|
||||
}
|
||||
|
||||
pub async fn read_workspaces_request(token: &str, params: QueryWorkspaceParams, url: &str) -> Result<RepeatedWorkspace, WorkspaceError> {
|
||||
let result = request_builder()
|
||||
let repeated_workspace = request_builder()
|
||||
.get(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await?
|
||||
.response::<RepeatedWorkspace>()
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
match result {
|
||||
Ok(repeated_workspace) => Ok(repeated_workspace),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
Ok(repeated_workspace)
|
||||
}
|
||||
|
||||
pub async fn update_workspace_request(token: &str, params: UpdateWorkspaceParams, url: &str) -> Result<(), WorkspaceError> {
|
||||
@ -141,31 +129,20 @@ pub async fn create_app_request(token: &str, params: CreateAppParams, url: &str)
|
||||
.post(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await?
|
||||
.response()
|
||||
.await?;
|
||||
Ok(app)
|
||||
}
|
||||
|
||||
pub async fn read_app_request(token: &str, params: QueryAppParams, url: &str) -> Result<Option<App>, WorkspaceError> {
|
||||
let result = request_builder()
|
||||
let app = request_builder()
|
||||
.get(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await;
|
||||
.option_response()
|
||||
.await?;
|
||||
|
||||
match result {
|
||||
Ok(builder) => Ok(Some(builder.response::<App>().await?)),
|
||||
Err(e) => {
|
||||
if e.is_not_found() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(e.into())
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(app)
|
||||
}
|
||||
|
||||
pub async fn update_app_request(token: &str, params: UpdateAppParams, url: &str) -> Result<(), WorkspaceError> {
|
||||
@ -194,34 +171,20 @@ pub async fn create_view_request(token: &str, params: CreateViewParams, url: &st
|
||||
.post(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await?
|
||||
.response()
|
||||
.await?;
|
||||
Ok(view)
|
||||
}
|
||||
|
||||
pub async fn read_view_request(token: &str, params: QueryViewParams, url: &str) -> Result<Option<View>, WorkspaceError> {
|
||||
let result = request_builder()
|
||||
let view = request_builder()
|
||||
.get(&url.to_owned())
|
||||
.header(HEADER_TOKEN, token)
|
||||
.protobuf(params)?
|
||||
.send()
|
||||
.await;
|
||||
.option_response()
|
||||
.await?;
|
||||
|
||||
match result {
|
||||
Ok(builder) => {
|
||||
let view = builder.response::<View>().await?;
|
||||
Ok(Some(view))
|
||||
},
|
||||
Err(e) => {
|
||||
if e.is_not_found() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(e.into())
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(view)
|
||||
}
|
||||
|
||||
pub async fn update_view_request(token: &str, params: UpdateViewParams, url: &str) -> Result<(), WorkspaceError> {
|
||||
|
@ -59,14 +59,10 @@ impl WorkspaceTableSql {
|
||||
workspace_id: &str,
|
||||
conn: &SqliteConnection,
|
||||
) -> Result<Vec<AppTable>, WorkspaceError> {
|
||||
let apps = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
|
||||
let workspace_table: WorkspaceTable = dsl::workspace_table
|
||||
.filter(workspace_table::id.eq(workspace_id))
|
||||
.first::<WorkspaceTable>(conn)?;
|
||||
let apps = AppTable::belonging_to(&workspace_table).load::<AppTable>(conn)?;
|
||||
Ok(apps)
|
||||
})?;
|
||||
|
||||
let workspace_table: WorkspaceTable = dsl::workspace_table
|
||||
.filter(workspace_table::id.eq(workspace_id))
|
||||
.first::<WorkspaceTable>(conn)?;
|
||||
let apps = AppTable::belonging_to(&workspace_table).load::<AppTable>(conn)?;
|
||||
Ok(apps)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user