mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix bugs
This commit is contained in:
parent
e4e40ebe20
commit
152cb17701
@ -7,10 +7,11 @@ use crate::{
|
||||
util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
|
||||
};
|
||||
use backend_service::errors::{invalid_params, ServerError};
|
||||
use bytes::Bytes;
|
||||
use chrono::Utc;
|
||||
use flowy_collaboration::{
|
||||
entities::revision::{RevType, Revision},
|
||||
protobuf::{CreateDocParams, RepeatedRevision},
|
||||
entities::revision::{RepeatedRevision, RevType, Revision},
|
||||
protobuf::CreateDocParams,
|
||||
};
|
||||
use flowy_core_data_model::{
|
||||
parser::{
|
||||
@ -71,12 +72,13 @@ pub(crate) async fn create_view(
|
||||
params: CreateViewParams,
|
||||
user_id: &str,
|
||||
) -> Result<View, ServerError> {
|
||||
let view_id = check_view_id(params.view_id.clone())?;
|
||||
let name = ViewName::parse(params.name).map_err(invalid_params)?;
|
||||
let belong_to_id = AppId::parse(params.belong_to_id).map_err(invalid_params)?;
|
||||
let thumbnail = ViewThumbnail::parse(params.thumbnail).map_err(invalid_params)?;
|
||||
let desc = ViewDesc::parse(params.desc).map_err(invalid_params)?;
|
||||
|
||||
let (sql, args, view) = NewViewSqlBuilder::new(belong_to_id.as_ref())
|
||||
let (sql, args, view) = NewViewSqlBuilder::new(view_id, belong_to_id.as_ref())
|
||||
.name(name.as_ref())
|
||||
.desc(desc.as_ref())
|
||||
.thumbnail(thumbnail.as_ref())
|
||||
@ -88,18 +90,13 @@ pub(crate) async fn create_view(
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
|
||||
let doc_id = view.id.clone();
|
||||
let revision: flowy_collaboration::protobuf::Revision =
|
||||
Revision::initial_revision(user_id, &doc_id, RevType::Remote)
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let mut repeated_revision = RepeatedRevision::new();
|
||||
repeated_revision.set_items(vec![revision].into());
|
||||
|
||||
let delta_data = Bytes::from(params.view_data);
|
||||
let md5 = format!("{:x}", md5::compute(&delta_data));
|
||||
let revision = Revision::new(&view.id, 0, 0, delta_data, RevType::Remote, user_id, md5);
|
||||
let repeated_revision = RepeatedRevision::new(vec![revision]);
|
||||
let mut create_doc_params = CreateDocParams::new();
|
||||
create_doc_params.set_revisions(repeated_revision);
|
||||
create_doc_params.set_revisions(repeated_revision.try_into().unwrap());
|
||||
create_doc_params.set_id(view.id.clone());
|
||||
|
||||
let _ = create_document(&kv_store, create_doc_params).await?;
|
||||
|
||||
Ok(view)
|
||||
|
@ -16,12 +16,11 @@ pub struct NewViewSqlBuilder {
|
||||
}
|
||||
|
||||
impl NewViewSqlBuilder {
|
||||
pub fn new(belong_to_id: &str) -> Self {
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
pub fn new(view_id: Uuid, belong_to_id: &str) -> Self {
|
||||
let time = Utc::now();
|
||||
|
||||
let table = ViewTable {
|
||||
id: uuid,
|
||||
id: view_id,
|
||||
belong_to_id: belong_to_id.to_string(),
|
||||
name: "".to_string(),
|
||||
description: "".to_string(),
|
||||
@ -94,13 +93,17 @@ impl NewViewSqlBuilder {
|
||||
pub(crate) fn check_view_ids(ids: Vec<String>) -> Result<Vec<Uuid>, ServerError> {
|
||||
let mut view_ids = vec![];
|
||||
for id in ids {
|
||||
let view_id = ViewId::parse(id).map_err(invalid_params)?;
|
||||
let view_id = Uuid::parse_str(view_id.as_ref())?;
|
||||
view_ids.push(view_id);
|
||||
view_ids.push(check_view_id(id)?);
|
||||
}
|
||||
Ok(view_ids)
|
||||
}
|
||||
|
||||
pub(crate) fn check_view_id(id: String) -> Result<Uuid, ServerError> {
|
||||
let view_id = ViewId::parse(id).map_err(invalid_params)?;
|
||||
let view_id = Uuid::parse_str(view_id.as_ref())?;
|
||||
Ok(view_id)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, sqlx::FromRow)]
|
||||
pub struct ViewTable {
|
||||
pub(crate) id: uuid::Uuid,
|
||||
|
@ -1,7 +1,13 @@
|
||||
use crate::{
|
||||
context::FlowyPersistence,
|
||||
entities::logged_user::LoggedUser,
|
||||
services::core::view::{create_view, delete_view, persistence::check_view_ids, read_view, update_view},
|
||||
services::core::view::{
|
||||
create_view,
|
||||
delete_view,
|
||||
persistence::{check_view_id, check_view_ids},
|
||||
read_view,
|
||||
update_view,
|
||||
},
|
||||
util::serde_ext::parse_from_payload,
|
||||
};
|
||||
use actix_web::{
|
||||
@ -62,7 +68,7 @@ pub async fn read_handler(payload: Payload, pool: Data<PgPool>, user: LoggedUser
|
||||
|
||||
pub async fn update_handler(payload: Payload, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
|
||||
let params: UpdateViewParams = parse_from_payload(payload).await?;
|
||||
let view_id = check_view_ids(vec![params.view_id.clone()])?.pop().unwrap();
|
||||
let view_id = check_view_id(params.view_id.clone())?;
|
||||
let name = match params.has_name() {
|
||||
false => None,
|
||||
true => Some(ViewName::parse(params.get_name().to_owned()).map_err(invalid_params)?.0),
|
||||
|
@ -1,6 +1,6 @@
|
||||
#![allow(clippy::module_inception)]
|
||||
|
||||
pub(crate) mod persistence;
|
||||
pub mod persistence;
|
||||
pub(crate) mod router;
|
||||
pub(crate) mod ws_actor;
|
||||
pub(crate) mod ws_receiver;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
context::FlowyPersistence,
|
||||
services::kv::{KVStore, KVTransaction, KeyValue},
|
||||
services::kv::{KVStore, KeyValue},
|
||||
util::serde_ext::parse_from_bytes,
|
||||
};
|
||||
use anyhow::Context;
|
||||
@ -16,7 +15,7 @@ use flowy_collaboration::protobuf::{
|
||||
};
|
||||
use lib_ot::{core::OperationTransformable, rich_text::RichTextDelta};
|
||||
use protobuf::Message;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -30,14 +29,12 @@ pub(crate) async fn create_document(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(persistence), err)]
|
||||
pub(crate) async fn read_document(
|
||||
persistence: &Arc<FlowyPersistence>,
|
||||
#[tracing::instrument(level = "debug", skip(kv_store), err)]
|
||||
pub async fn read_document(
|
||||
kv_store: &Arc<DocumentKVPersistence>,
|
||||
params: DocIdentifier,
|
||||
) -> Result<DocumentInfo, ServerError> {
|
||||
let _ = Uuid::parse_str(¶ms.doc_id).context("Parse document id to uuid failed")?;
|
||||
|
||||
let kv_store = persistence.kv_store();
|
||||
let revisions = kv_store.batch_get_revisions(¶ms.doc_id, None).await?;
|
||||
make_doc_from_revisions(¶ms.doc_id, revisions)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use actix_web::{
|
||||
};
|
||||
use backend_service::{errors::ServerError, response::FlowyResponse};
|
||||
use flowy_collaboration::protobuf::{CreateDocParams, DocIdentifier, ResetDocumentParams};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn create_document_handler(
|
||||
@ -28,7 +28,8 @@ pub async fn read_document_handler(
|
||||
persistence: Data<Arc<FlowyPersistence>>,
|
||||
) -> Result<HttpResponse, ServerError> {
|
||||
let params: DocIdentifier = parse_from_payload(payload).await?;
|
||||
let doc = read_document(persistence.get_ref(), params).await?;
|
||||
let kv_store = persistence.kv_store();
|
||||
let doc = read_document(&kv_store, params).await?;
|
||||
let response = FlowyResponse::success().pb(doc)?;
|
||||
Ok(response.into())
|
||||
}
|
||||
|
@ -85,23 +85,19 @@ impl DocumentWebSocketActor {
|
||||
persistence,
|
||||
});
|
||||
|
||||
match self.handle_revision(user, document_client_data).await {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
tracing::error!("[DocumentWebSocketActor]: process client data error {:?}", e);
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_revision(&self, user: Arc<ServerDocUser>, client_data: DocumentClientWSData) -> Result<()> {
|
||||
match &client_data.ty {
|
||||
match &document_client_data.ty {
|
||||
DocumentClientWSDataType::ClientPushRev => {
|
||||
let _ = self
|
||||
match self
|
||||
.doc_manager
|
||||
.apply_revisions(user, client_data)
|
||||
.apply_revisions(user, document_client_data)
|
||||
.await
|
||||
.map_err(internal_error)?;
|
||||
.map_err(internal_error)
|
||||
{
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
tracing::error!("[DocumentWebSocketActor]: process client data failed: {:?}", e);
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ use flowy_collaboration::{
|
||||
errors::CollaborateError,
|
||||
protobuf::DocIdentifier,
|
||||
};
|
||||
use lib_infra::future::{BoxResultFuture, FutureResultSend};
|
||||
use lib_infra::future::BoxResultFuture;
|
||||
|
||||
use flowy_collaboration::sync::{DocumentPersistence, ServerDocumentManager};
|
||||
use std::{
|
||||
@ -83,7 +83,7 @@ impl DocumentPersistence for DocumentPersistenceImpl {
|
||||
doc_id: doc_id.to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
let persistence = self.0.clone();
|
||||
let persistence = self.0.kv_store();
|
||||
Box::pin(async move {
|
||||
let mut pb_doc = read_document(&persistence, params)
|
||||
.await
|
||||
@ -115,11 +115,9 @@ impl DocumentPersistence for DocumentPersistenceImpl {
|
||||
let kv_store = self.0.kv_store();
|
||||
let doc_id = doc_id.to_owned();
|
||||
let f = || async move {
|
||||
let expected_len = rev_ids.len();
|
||||
let mut pb = kv_store.batch_get_revisions(&doc_id, rev_ids).await?;
|
||||
let repeated_revision: RepeatedRevision = (&mut pb).try_into()?;
|
||||
let revisions = repeated_revision.into_inner();
|
||||
assert_eq!(expected_len, revisions.len());
|
||||
Ok(revisions)
|
||||
};
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::{
|
||||
services::kv::{KVStore, KVTransaction, KeyValue},
|
||||
services::kv::{KVTransaction, KeyValue},
|
||||
util::sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
|
||||
};
|
||||
use anyhow::Context;
|
||||
use async_trait::async_trait;
|
||||
use backend_service::errors::ServerError;
|
||||
use bytes::Bytes;
|
||||
use futures_core::future::BoxFuture;
|
||||
use lib_infra::future::{BoxResultFuture, FutureResultSend};
|
||||
|
||||
use lib_infra::future::BoxResultFuture;
|
||||
use sql_builder::SqlBuilder as RawSqlBuilder;
|
||||
use sqlx::{
|
||||
postgres::{PgArguments, PgRow},
|
||||
@ -17,7 +17,6 @@ use sqlx::{
|
||||
Postgres,
|
||||
Row,
|
||||
};
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
const KV_TABLE: &str = "kv_table";
|
||||
|
||||
@ -26,6 +25,33 @@ pub struct PostgresKV {
|
||||
}
|
||||
|
||||
impl PostgresKV {
|
||||
pub async fn get(&self, key: &str) -> Result<Option<Bytes>, ServerError> {
|
||||
let key = key.to_owned();
|
||||
self.transaction(|mut transaction| Box::pin(async move { transaction.get(&key).await }))
|
||||
.await
|
||||
}
|
||||
pub async fn set(&self, key: &str, value: Bytes) -> Result<(), ServerError> {
|
||||
let key = key.to_owned();
|
||||
self.transaction(|mut transaction| Box::pin(async move { transaction.set(&key, value).await }))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn remove(&self, key: &str) -> Result<(), ServerError> {
|
||||
let key = key.to_owned();
|
||||
self.transaction(|mut transaction| Box::pin(async move { transaction.remove(&key).await }))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn batch_set(&self, kvs: Vec<KeyValue>) -> Result<(), ServerError> {
|
||||
self.transaction(|mut transaction| Box::pin(async move { transaction.batch_set(kvs).await }))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn batch_get(&self, keys: Vec<String>) -> Result<Vec<KeyValue>, ServerError> {
|
||||
self.transaction(|mut transaction| Box::pin(async move { transaction.batch_get(keys).await }))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn transaction<F, O>(&self, f: F) -> Result<O, ServerError>
|
||||
where
|
||||
F: for<'a> FnOnce(Box<dyn KVTransaction + 'a>) -> BoxResultFuture<O, ServerError>,
|
||||
@ -61,14 +87,13 @@ impl<'a, 'b> KVTransaction for PostgresTransaction<'a, 'b> {
|
||||
.fetch_one(self.0 as &mut DBTransaction<'b>)
|
||||
.await;
|
||||
|
||||
let result = match result {
|
||||
match result {
|
||||
Ok(val) => Ok(Some(Bytes::from(val.blob))),
|
||||
Err(error) => match error {
|
||||
Error::RowNotFound => Ok(None),
|
||||
_ => Err(map_sqlx_error(error)),
|
||||
},
|
||||
};
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
async fn set(&mut self, key: &str, bytes: Bytes) -> Result<(), ServerError> {
|
||||
|
@ -3,12 +3,10 @@ mod kv;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use futures_core::future::BoxFuture;
|
||||
|
||||
pub(crate) use kv::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use backend_service::errors::ServerError;
|
||||
use lib_infra::future::{BoxResultFuture, FutureResultSend};
|
||||
|
||||
// TODO: Generic the KVStore that enable switching KVStore to another
|
||||
// implementation
|
||||
|
@ -50,3 +50,30 @@ async fn kv_batch_set_test() {
|
||||
|
||||
assert_eq!(kvs, kvs_from_db);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn kv_batch_get_start_with_test() {
|
||||
let server = spawn_server().await;
|
||||
let kv = server.app_ctx.persistence.kv_store();
|
||||
let kvs = vec![
|
||||
KeyValue {
|
||||
key: "abc:1".to_string(),
|
||||
value: "a".to_string().into(),
|
||||
},
|
||||
KeyValue {
|
||||
key: "abc:2".to_string(),
|
||||
value: "b".to_string().into(),
|
||||
},
|
||||
];
|
||||
|
||||
kv.batch_set(kvs.clone()).await.unwrap();
|
||||
kv.transaction(|mut transaction| {
|
||||
Box::pin(async move {
|
||||
let kvs_from_db = transaction.batch_get_start_with("abc").await.unwrap();
|
||||
assert_eq!(kvs, kvs_from_db);
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -6,19 +6,22 @@ use flowy_document::services::doc::edit::ClientDocEditor as ClientEditDocContext
|
||||
use flowy_test::{helper::ViewTest, FlowySDKTest};
|
||||
use flowy_user::services::user::UserSession;
|
||||
use futures_util::{stream, stream::StreamExt};
|
||||
use sqlx::PgPool;
|
||||
|
||||
use std::sync::Arc;
|
||||
use bytes::Bytes;
|
||||
use tokio::time::{sleep, Duration};
|
||||
// use crate::helper::*;
|
||||
use crate::util::helper::{spawn_server, TestServer};
|
||||
use flowy_collaboration::{entities::doc::DocIdentifier, protobuf::ResetDocumentParams};
|
||||
use lib_ot::rich_text::{RichTextAttribute, RichTextDelta};
|
||||
use parking_lot::RwLock;
|
||||
use flowy_collaboration::entities::revision::RepeatedRevision;
|
||||
use backend::services::document::persistence::{DocumentKVPersistence, read_document, reset_document};
|
||||
|
||||
use flowy_collaboration::entities::revision::{RepeatedRevision, Revision, RevType};
|
||||
use lib_ot::core::Interval;
|
||||
|
||||
use flowy_net::services::ws::FlowyWSConnect;
|
||||
use crate::util::helper::*;
|
||||
|
||||
|
||||
pub struct DocumentTest {
|
||||
server: TestServer,
|
||||
@ -32,7 +35,7 @@ pub enum DocScript {
|
||||
ClientOpenDoc,
|
||||
AssertClient(&'static str),
|
||||
AssertServer(&'static str, i64),
|
||||
ServerSaveDocument(RepeatedRevision), // delta_json, rev_id
|
||||
ServerSaveDocument(String, i64), // delta_json, rev_id
|
||||
}
|
||||
|
||||
impl DocumentTest {
|
||||
@ -78,10 +81,8 @@ impl ScriptContext {
|
||||
}
|
||||
|
||||
async fn open_doc(&mut self) {
|
||||
let flowy_document = self.client_sdk.flowy_document.clone();
|
||||
let doc_id = self.doc_id.clone();
|
||||
|
||||
let edit_context = flowy_document.open(DocIdentifier { doc_id }).await.unwrap();
|
||||
let edit_context = self.client_sdk.document_ctx.open(DocIdentifier { doc_id }).await.unwrap();
|
||||
self.client_edit_context = Some(edit_context);
|
||||
}
|
||||
|
||||
@ -106,6 +107,7 @@ async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript
|
||||
context.write().open_doc().await;
|
||||
},
|
||||
DocScript::ClientInsertText(index, s) => {
|
||||
sleep(Duration::from_millis(2000)).await;
|
||||
context.read().client_edit_context().insert(index, s).await.unwrap();
|
||||
},
|
||||
DocScript::ClientFormatText(interval, attribute) => {
|
||||
@ -123,24 +125,32 @@ async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript
|
||||
},
|
||||
DocScript::AssertServer(s, rev_id) => {
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
let persistence = Data::new(context.read().server.app_ctx.persistence.kv_store());
|
||||
let doc_identifier: flowy_collaboration::protobuf::DocIdentifier = DocIdentifier {
|
||||
doc_id
|
||||
}.try_into().unwrap();
|
||||
|
||||
// let doc_identifier = DocIdentifier {
|
||||
// doc_id
|
||||
// };
|
||||
//
|
||||
// let doc = context.read().server.read_doc()
|
||||
|
||||
|
||||
// let pg_pool = context.read().server_pg_pool.clone();
|
||||
// let doc_manager = context.read().server_doc_manager.clone();
|
||||
// let edit_doc = doc_manager.get(&doc_id).await.unwrap();
|
||||
// let json = edit_doc.document_json().await.unwrap();
|
||||
// assert_eq(s, &json);
|
||||
// assert_eq!(edit_doc.rev_id().await.unwrap(), rev_id);
|
||||
let document_info = read_document(persistence.get_ref(), doc_identifier).await.unwrap();
|
||||
assert_eq(s, &document_info.text);
|
||||
assert_eq!(document_info.rev_id, rev_id);
|
||||
},
|
||||
DocScript::ServerSaveDocument(repeated_revision) => {
|
||||
let pg_pool = Data::new(context.read().server.pg_pool.clone());
|
||||
reset_doc(&doc_id, repeated_revision, pg_pool).await;
|
||||
DocScript::ServerSaveDocument(document_json, rev_id) => {
|
||||
let delta_data = Bytes::from(document_json);
|
||||
let user_id = context.read().client_user_session.user_id().unwrap();
|
||||
let md5 = format!("{:x}", md5::compute(&delta_data));
|
||||
let base_rev_id = if rev_id == 0 { rev_id } else { rev_id - 1 };
|
||||
let revision = Revision::new(
|
||||
&doc_id,
|
||||
base_rev_id,
|
||||
rev_id,
|
||||
delta_data,
|
||||
RevType::Remote,
|
||||
&user_id,
|
||||
md5,
|
||||
);
|
||||
|
||||
let kv_store = Data::new(context.read().server.app_ctx.persistence.kv_store());
|
||||
reset_doc(&doc_id, RepeatedRevision::new(vec![revision]), kv_store.get_ref()).await;
|
||||
},
|
||||
// DocScript::Sleep(sec) => {
|
||||
// sleep(Duration::from_secs(sec)).await;
|
||||
@ -174,10 +184,10 @@ async fn create_doc(flowy_test: &FlowySDKTest) -> String {
|
||||
view_test.view.id
|
||||
}
|
||||
|
||||
async fn reset_doc(doc_id: &str, repeated_revision: RepeatedRevision, pool: Data<PgPool>) {
|
||||
async fn reset_doc(doc_id: &str, repeated_revision: RepeatedRevision, kv_store: &Arc<DocumentKVPersistence>) {
|
||||
let pb: flowy_collaboration::protobuf::RepeatedRevision = repeated_revision.try_into().unwrap();
|
||||
let mut params = ResetDocumentParams::new();
|
||||
params.set_doc_id(doc_id.to_owned());
|
||||
params.set_revisions(pb);
|
||||
// let _ = reset_document_handler(pool.get_ref(), params).await.unwrap();
|
||||
let _ = reset_document(kv_store, params).await.unwrap();
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ use lib_ot::{core::Interval, rich_text::RichTextAttribute};
|
||||
async fn delta_sync_while_editing() {
|
||||
let test = DocumentTest::new().await;
|
||||
test.run_scripts(vec![
|
||||
DocScript::ClientConnectWS,
|
||||
DocScript::ClientOpenDoc,
|
||||
DocScript::ClientInsertText(0, "abc"),
|
||||
DocScript::ClientInsertText(3, "123"),
|
||||
@ -34,7 +33,6 @@ async fn delta_sync_while_editing() {
|
||||
async fn delta_sync_multi_revs() {
|
||||
let test = DocumentTest::new().await;
|
||||
test.run_scripts(vec![
|
||||
DocScript::ClientConnectWS,
|
||||
DocScript::ClientOpenDoc,
|
||||
DocScript::ClientInsertText(0, "abc"),
|
||||
DocScript::ClientInsertText(3, "123"),
|
||||
@ -81,6 +79,7 @@ async fn delta_sync_with_http_request() {
|
||||
let mut document = Document::new::<FlowyDoc>();
|
||||
document.insert(0, "123").unwrap();
|
||||
document.insert(3, "456").unwrap();
|
||||
|
||||
let json = document.to_json();
|
||||
|
||||
test.run_scripts(vec![
|
||||
|
@ -1,5 +1,5 @@
|
||||
use backend::{
|
||||
application::{get_connection_pool, init_app_context, Application},
|
||||
application::{init_app_context, Application},
|
||||
config::{get_configuration, DatabaseSettings},
|
||||
context::AppContext,
|
||||
};
|
||||
@ -9,10 +9,14 @@ use backend_service::{
|
||||
user_request::*,
|
||||
workspace_request::*,
|
||||
};
|
||||
use flowy_collaboration::entities::doc::{CreateDocParams, DocIdentifier, DocumentInfo};
|
||||
use flowy_collaboration::{
|
||||
document::default::initial_delta_string,
|
||||
entities::doc::{CreateDocParams, DocIdentifier, DocumentInfo},
|
||||
};
|
||||
use flowy_core_data_model::entities::prelude::*;
|
||||
use flowy_document::services::server::{create_doc_request, read_doc_request};
|
||||
use flowy_user_data_model::entities::*;
|
||||
use lib_infra::uuid_string;
|
||||
use sqlx::{Connection, Executor, PgConnection, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -203,7 +207,6 @@ pub async fn spawn_user_server() -> TestUserServer {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestServer {
|
||||
pub pg_pool: PgPool,
|
||||
pub app_ctx: AppContext,
|
||||
pub client_server_config: ClientServerConfiguration,
|
||||
}
|
||||
@ -234,9 +237,6 @@ pub async fn spawn_server() -> TestServer {
|
||||
client_server_config.reset_host_with_port("localhost", application_port);
|
||||
|
||||
TestServer {
|
||||
pg_pool: get_connection_pool(&configuration.database)
|
||||
.await
|
||||
.expect("Failed to connect to the database"),
|
||||
app_ctx,
|
||||
client_server_config,
|
||||
}
|
||||
@ -312,7 +312,15 @@ pub async fn create_test_view(application: &TestUserServer, app_id: &str) -> Vie
|
||||
let desc = "This is my first view".to_string();
|
||||
let thumbnail = "http://1.png".to_string();
|
||||
|
||||
let params = CreateViewParams::new(app_id.to_owned(), name, desc, ViewType::Doc, thumbnail);
|
||||
let params = CreateViewParams::new(
|
||||
app_id.to_owned(),
|
||||
name,
|
||||
desc,
|
||||
ViewType::Doc,
|
||||
thumbnail,
|
||||
initial_delta_string(),
|
||||
uuid_string(),
|
||||
);
|
||||
let app = application.create_view(params).await;
|
||||
app
|
||||
}
|
||||
|
@ -137,6 +137,8 @@ class CreateViewParams extends $pb.GeneratedMessage {
|
||||
..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'desc')
|
||||
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'thumbnail')
|
||||
..e<ViewType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewType', $pb.PbFieldType.OE, defaultOrMaker: ViewType.Blank, valueOf: ViewType.valueOf, enumValues: ViewType.values)
|
||||
..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewData')
|
||||
..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'viewId')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@ -147,6 +149,8 @@ class CreateViewParams extends $pb.GeneratedMessage {
|
||||
$core.String? desc,
|
||||
$core.String? thumbnail,
|
||||
ViewType? viewType,
|
||||
$core.String? viewData,
|
||||
$core.String? viewId,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (belongToId != null) {
|
||||
@ -164,6 +168,12 @@ class CreateViewParams extends $pb.GeneratedMessage {
|
||||
if (viewType != null) {
|
||||
_result.viewType = viewType;
|
||||
}
|
||||
if (viewData != null) {
|
||||
_result.viewData = viewData;
|
||||
}
|
||||
if (viewId != null) {
|
||||
_result.viewId = viewId;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory CreateViewParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
@ -231,6 +241,24 @@ class CreateViewParams extends $pb.GeneratedMessage {
|
||||
$core.bool hasViewType() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearViewType() => clearField(5);
|
||||
|
||||
@$pb.TagNumber(6)
|
||||
$core.String get viewData => $_getSZ(5);
|
||||
@$pb.TagNumber(6)
|
||||
set viewData($core.String v) { $_setString(5, v); }
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool hasViewData() => $_has(5);
|
||||
@$pb.TagNumber(6)
|
||||
void clearViewData() => clearField(6);
|
||||
|
||||
@$pb.TagNumber(7)
|
||||
$core.String get viewId => $_getSZ(6);
|
||||
@$pb.TagNumber(7)
|
||||
set viewId($core.String v) { $_setString(6, v); }
|
||||
@$pb.TagNumber(7)
|
||||
$core.bool hasViewId() => $_has(6);
|
||||
@$pb.TagNumber(7)
|
||||
void clearViewId() => clearField(7);
|
||||
}
|
||||
|
||||
class View extends $pb.GeneratedMessage {
|
||||
|
@ -45,11 +45,13 @@ const CreateViewParams$json = const {
|
||||
const {'1': 'desc', '3': 3, '4': 1, '5': 9, '10': 'desc'},
|
||||
const {'1': 'thumbnail', '3': 4, '4': 1, '5': 9, '10': 'thumbnail'},
|
||||
const {'1': 'view_type', '3': 5, '4': 1, '5': 14, '6': '.ViewType', '10': 'viewType'},
|
||||
const {'1': 'view_data', '3': 6, '4': 1, '5': 9, '10': 'viewData'},
|
||||
const {'1': 'view_id', '3': 7, '4': 1, '5': 9, '10': 'viewId'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `CreateViewParams`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List createViewParamsDescriptor = $convert.base64Decode('ChBDcmVhdGVWaWV3UGFyYW1zEiAKDGJlbG9uZ190b19pZBgBIAEoCVIKYmVsb25nVG9JZBISCgRuYW1lGAIgASgJUgRuYW1lEhIKBGRlc2MYAyABKAlSBGRlc2MSHAoJdGh1bWJuYWlsGAQgASgJUgl0aHVtYm5haWwSJgoJdmlld190eXBlGAUgASgOMgkuVmlld1R5cGVSCHZpZXdUeXBl');
|
||||
final $typed_data.Uint8List createViewParamsDescriptor = $convert.base64Decode('ChBDcmVhdGVWaWV3UGFyYW1zEiAKDGJlbG9uZ190b19pZBgBIAEoCVIKYmVsb25nVG9JZBISCgRuYW1lGAIgASgJUgRuYW1lEhIKBGRlc2MYAyABKAlSBGRlc2MSHAoJdGh1bWJuYWlsGAQgASgJUgl0aHVtYm5haWwSJgoJdmlld190eXBlGAUgASgOMgkuVmlld1R5cGVSCHZpZXdUeXBlEhsKCXZpZXdfZGF0YRgGIAEoCVIIdmlld0RhdGESFwoHdmlld19pZBgHIAEoCVIGdmlld0lk');
|
||||
@$core.Deprecated('Use viewDescriptor instead')
|
||||
const View$json = const {
|
||||
'1': 'View',
|
||||
|
@ -4,8 +4,8 @@ use chrono::Utc;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use flowy_collaboration::{document::default::initial_read_me, entities::doc::DocumentDelta};
|
||||
use flowy_core_data_model::user_default;
|
||||
use flowy_collaboration::document::default::{initial_delta, initial_read_me};
|
||||
use flowy_core_data_model::{entities::view::CreateViewParams, user_default};
|
||||
use flowy_net::entities::NetworkType;
|
||||
|
||||
use crate::{
|
||||
@ -85,24 +85,28 @@ impl CoreContext {
|
||||
let apps = workspace.take_apps().into_inner();
|
||||
let cloned_workspace = workspace.clone();
|
||||
|
||||
let _ = self.workspace_controller.create_workspace(workspace).await?;
|
||||
let _ = self.workspace_controller.create_workspace_on_local(workspace).await?;
|
||||
for mut app in apps {
|
||||
let app_id = app.id.clone();
|
||||
let views = app.take_belongings().into_inner();
|
||||
let _ = self.app_controller.create_app(app).await?;
|
||||
let _ = self.app_controller.create_app_on_local(app).await?;
|
||||
for (index, view) in views.into_iter().enumerate() {
|
||||
if index == 0 {
|
||||
let delta = initial_read_me();
|
||||
let doc_delta = DocumentDelta {
|
||||
doc_id: view.id.clone(),
|
||||
text: delta.to_json(),
|
||||
};
|
||||
let _ = self.view_controller.apply_doc_delta(doc_delta).await?;
|
||||
self.view_controller.set_latest_view(&view);
|
||||
|
||||
// Close the view after initialize
|
||||
self.view_controller.close_view(view.id.clone().into()).await?;
|
||||
}
|
||||
let _ = self.view_controller.create_view(view).await?;
|
||||
let view_data = if index == 0 {
|
||||
initial_read_me().to_json()
|
||||
} else {
|
||||
initial_delta().to_json()
|
||||
};
|
||||
self.view_controller.set_latest_view(&view);
|
||||
let params = CreateViewParams {
|
||||
belong_to_id: app_id.clone(),
|
||||
name: view.name,
|
||||
desc: view.desc,
|
||||
thumbnail: "".to_string(),
|
||||
view_type: view.view_type,
|
||||
view_data,
|
||||
view_id: view.id.clone(),
|
||||
};
|
||||
let _ = self.view_controller.create_view_from_params(params).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,17 +40,17 @@ impl AppController {
|
||||
}
|
||||
|
||||
pub fn init(&self) -> Result<(), FlowyError> {
|
||||
self.listen_trash_can_event();
|
||||
self.listen_trash_controller_event();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, params), fields(name = %params.name) err)]
|
||||
pub(crate) async fn create_app_from_params(&self, params: CreateAppParams) -> Result<App, FlowyError> {
|
||||
let app = self.create_app_on_server(params).await?;
|
||||
self.create_app(app).await
|
||||
self.create_app_on_local(app).await
|
||||
}
|
||||
|
||||
pub(crate) async fn create_app(&self, app: App) -> Result<App, FlowyError> {
|
||||
pub(crate) async fn create_app_on_local(&self, app: App) -> Result<App, FlowyError> {
|
||||
let conn = &*self.database.db_connection()?;
|
||||
conn.immediate_transaction::<_, FlowyError, _>(|| {
|
||||
let _ = self.save_app(app.clone(), &*conn)?;
|
||||
@ -71,7 +71,7 @@ impl AppController {
|
||||
let conn = self.database.db_connection()?;
|
||||
let app_table = AppTableSql::read_app(¶ms.app_id, &*conn)?;
|
||||
|
||||
let trash_ids = self.trash_can.trash_ids(&conn)?;
|
||||
let trash_ids = self.trash_can.read_trash_ids(&conn)?;
|
||||
if trash_ids.contains(&app_table.id) {
|
||||
return Err(FlowyError::record_not_found());
|
||||
}
|
||||
@ -165,7 +165,7 @@ impl AppController {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn listen_trash_can_event(&self) {
|
||||
fn listen_trash_controller_event(&self) {
|
||||
let mut rx = self.trash_can.subscribe();
|
||||
let database = self.database.clone();
|
||||
let trash_can = self.trash_can.clone();
|
||||
@ -245,56 +245,9 @@ pub fn read_local_workspace_apps(
|
||||
conn: &SqliteConnection,
|
||||
) -> Result<RepeatedApp, FlowyError> {
|
||||
let mut app_tables = AppTableSql::read_workspace_apps(workspace_id, false, conn)?;
|
||||
let trash_ids = trash_controller.trash_ids(conn)?;
|
||||
let trash_ids = trash_controller.read_trash_ids(conn)?;
|
||||
app_tables.retain(|app_table| !trash_ids.contains(&app_table.id));
|
||||
|
||||
let apps = app_tables.into_iter().map(|table| table.into()).collect::<Vec<App>>();
|
||||
Ok(RepeatedApp { items: apps })
|
||||
}
|
||||
|
||||
// #[tracing::instrument(level = "debug", skip(self), err)]
|
||||
// pub(crate) async fn delete_app(&self, app_id: &str) -> Result<(),
|
||||
// FlowyError> { let conn = &*self.database.db_connection()?;
|
||||
// conn.immediate_transaction::<_, FlowyError, _>(|| {
|
||||
// let app = AppTableSql::delete_app(app_id, &*conn)?;
|
||||
// let apps = self.read_local_apps(&app.workspace_id, &*conn)?;
|
||||
// send_dart_notification(&app.workspace_id,
|
||||
// WorkspaceNotification::WorkspaceDeleteApp) .payload(apps)
|
||||
// .send();
|
||||
// Ok(())
|
||||
// })?;
|
||||
//
|
||||
// let _ = self.delete_app_on_server(app_id);
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// #[tracing::instrument(level = "debug", skip(self), err)]
|
||||
// fn delete_app_on_server(&self, app_id: &str) -> Result<(), FlowyError> {
|
||||
// let token = self.user.token()?;
|
||||
// let server = self.server.clone();
|
||||
// let params = DeleteAppParams {
|
||||
// app_id: app_id.to_string(),
|
||||
// };
|
||||
// spawn(async move {
|
||||
// match server.delete_app(&token, params).await {
|
||||
// Ok(_) => {},
|
||||
// Err(e) => {
|
||||
// // TODO: retry?
|
||||
// log::error!("Delete app failed: {:?}", e);
|
||||
// },
|
||||
// }
|
||||
// });
|
||||
// // let action = RetryAction::new(self.server.clone(), self.user.clone(),
|
||||
// move // |token, server| { let params = params.clone();
|
||||
// // async move {
|
||||
// // match server.delete_app(&token, params).await {
|
||||
// // Ok(_) => {},
|
||||
// // Err(e) => log::error!("Delete app failed: {:?}", e),
|
||||
// // }
|
||||
// // Ok::<(), FlowyError>(())
|
||||
// // }
|
||||
// // });
|
||||
// //
|
||||
// // spawn_retry(500, 3, action);
|
||||
// Ok(())
|
||||
// }
|
||||
|
@ -29,17 +29,17 @@ pub(crate) async fn create_app_handler(
|
||||
|
||||
pub(crate) async fn delete_app_handler(
|
||||
data: Data<QueryAppRequest>,
|
||||
controller: Unit<Arc<AppController>>,
|
||||
trash_can: Unit<Arc<TrashController>>,
|
||||
view_controller: Unit<Arc<AppController>>,
|
||||
trash_controller: Unit<Arc<TrashController>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let params: AppIdentifier = data.into_inner().try_into()?;
|
||||
let trash = controller
|
||||
let trash = view_controller
|
||||
.read_app_tables(vec![params.app_id])?
|
||||
.into_iter()
|
||||
.map(|view_table| view_table.into())
|
||||
.collect::<Vec<Trash>>();
|
||||
|
||||
let _ = trash_can.add(trash).await?;
|
||||
let _ = trash_controller.add(trash).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
errors::FlowyError,
|
||||
services::server::WorkspaceServerAPI,
|
||||
};
|
||||
use lib_infra::{future::FutureResult, timestamp, uuid};
|
||||
use lib_infra::{future::FutureResult, timestamp, uuid_string};
|
||||
|
||||
pub struct WorkspaceServerMock {}
|
||||
|
||||
@ -18,7 +18,7 @@ impl WorkspaceServerAPI for WorkspaceServerMock {
|
||||
fn create_workspace(&self, _token: &str, params: CreateWorkspaceParams) -> FutureResult<Workspace, FlowyError> {
|
||||
let time = timestamp();
|
||||
let workspace = Workspace {
|
||||
id: uuid(),
|
||||
id: uuid_string(),
|
||||
name: params.name,
|
||||
desc: params.desc,
|
||||
apps: RepeatedApp::default(),
|
||||
@ -51,7 +51,7 @@ impl WorkspaceServerAPI for WorkspaceServerMock {
|
||||
fn create_view(&self, _token: &str, params: CreateViewParams) -> FutureResult<View, FlowyError> {
|
||||
let time = timestamp();
|
||||
let view = View {
|
||||
id: uuid(),
|
||||
id: uuid_string(),
|
||||
belong_to_id: params.belong_to_id,
|
||||
name: params.name,
|
||||
desc: params.desc,
|
||||
@ -79,7 +79,7 @@ impl WorkspaceServerAPI for WorkspaceServerMock {
|
||||
fn create_app(&self, _token: &str, params: CreateAppParams) -> FutureResult<App, FlowyError> {
|
||||
let time = timestamp();
|
||||
let app = App {
|
||||
id: uuid(),
|
||||
id: uuid_string(),
|
||||
workspace_id: params.workspace_id,
|
||||
name: params.name,
|
||||
desc: params.desc,
|
||||
|
@ -182,7 +182,7 @@ impl TrashController {
|
||||
Ok(repeated_trash)
|
||||
}
|
||||
|
||||
pub fn trash_ids(&self, conn: &SqliteConnection) -> Result<Vec<String>, FlowyError> {
|
||||
pub fn read_trash_ids(&self, conn: &SqliteConnection) -> Result<Vec<String>, FlowyError> {
|
||||
let ids = TrashTableSql::read_all(&*conn)?
|
||||
.into_inner()
|
||||
.into_iter()
|
||||
|
@ -21,6 +21,7 @@ use crate::{
|
||||
use flowy_core_data_model::entities::share::{ExportData, ExportParams};
|
||||
use flowy_database::kv::KV;
|
||||
use flowy_document::context::DocumentContext;
|
||||
use lib_infra::uuid_string;
|
||||
|
||||
const LATEST_VIEW_ID: &str = "latest_view_id";
|
||||
|
||||
@ -57,11 +58,11 @@ impl ViewController {
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, params), fields(name = %params.name), err)]
|
||||
pub(crate) async fn create_view_from_params(&self, params: CreateViewParams) -> Result<View, FlowyError> {
|
||||
let view = self.create_view_on_server(params.clone()).await?;
|
||||
self.create_view(view).await
|
||||
let view = self.create_view_on_server(params).await?;
|
||||
self.create_view_on_local(view).await
|
||||
}
|
||||
|
||||
pub(crate) async fn create_view(&self, view: View) -> Result<View, FlowyError> {
|
||||
pub(crate) async fn create_view_on_local(&self, view: View) -> Result<View, FlowyError> {
|
||||
let conn = &*self.database.db_connection()?;
|
||||
let trash_can = self.trash_can.clone();
|
||||
|
||||
@ -86,7 +87,7 @@ impl ViewController {
|
||||
let conn = self.database.db_connection()?;
|
||||
let view_table = ViewTableSql::read_view(¶ms.view_id, &*conn)?;
|
||||
|
||||
let trash_ids = self.trash_can.trash_ids(&conn)?;
|
||||
let trash_ids = self.trash_can.read_trash_ids(&conn)?;
|
||||
if trash_ids.contains(&view_table.id) {
|
||||
return Err(FlowyError::record_not_found());
|
||||
}
|
||||
@ -120,7 +121,7 @@ impl ViewController {
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self,params), fields(doc_id = %params.doc_id), err)]
|
||||
pub(crate) async fn close_view(&self, params: DocIdentifier) -> Result<(), FlowyError> {
|
||||
let _ = self.document_ctx.close(params).await?;
|
||||
let _ = self.document_ctx.doc_ctrl.close(¶ms.doc_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -131,14 +132,14 @@ impl ViewController {
|
||||
let _ = KV::remove(LATEST_VIEW_ID);
|
||||
}
|
||||
}
|
||||
let _ = self.document_ctx.close(params).await?;
|
||||
let _ = self.document_ctx.doc_ctrl.close(¶ms.doc_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, params), fields(doc_id = %params.doc_id), err)]
|
||||
pub(crate) async fn duplicate_view(&self, params: DocIdentifier) -> Result<(), FlowyError> {
|
||||
let view: View = ViewTableSql::read_view(¶ms.doc_id, &*self.database.db_connection()?)?.into();
|
||||
let _delta_data = self
|
||||
let delta_data = self
|
||||
.document_ctx
|
||||
.read_document_data(params, self.database.db_pool()?)
|
||||
.await?;
|
||||
@ -149,6 +150,8 @@ impl ViewController {
|
||||
desc: view.desc.clone(),
|
||||
thumbnail: "".to_owned(),
|
||||
view_type: view.view_type.clone(),
|
||||
view_data: delta_data.text,
|
||||
view_id: uuid_string(),
|
||||
};
|
||||
|
||||
let _ = self.create_view_from_params(duplicate_params).await?;
|
||||
@ -174,7 +177,7 @@ impl ViewController {
|
||||
pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, FlowyError> {
|
||||
// TODO: read from server
|
||||
let conn = self.database.db_connection()?;
|
||||
let repeated_view = read_local_belonging_view(belong_to_id, self.trash_can.clone(), &conn)?;
|
||||
let repeated_view = read_belonging_views_on_local(belong_to_id, self.trash_can.clone(), &conn)?;
|
||||
Ok(repeated_view)
|
||||
}
|
||||
|
||||
@ -195,13 +198,13 @@ impl ViewController {
|
||||
|
||||
//
|
||||
let _ = notify_views_changed(&updated_view.belong_to_id, self.trash_can.clone(), conn)?;
|
||||
|
||||
let _ = self.update_view_on_server(params);
|
||||
Ok(updated_view)
|
||||
}
|
||||
|
||||
pub(crate) async fn apply_doc_delta(&self, params: DocumentDelta) -> Result<DocumentDelta, FlowyError> {
|
||||
let doc = self.document_ctx.apply_doc_delta(params).await?;
|
||||
let db_pool = self.document_ctx.user.db_pool()?;
|
||||
let doc = self.document_ctx.doc_ctrl.apply_local_delta(params, db_pool).await?;
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
@ -308,7 +311,7 @@ async fn handle_trash_event(
|
||||
TrashEvent::NewTrash(identifiers, ret) => {
|
||||
let result = || {
|
||||
let conn = &*db_result?;
|
||||
let view_tables = get_view_table_from(identifiers, conn)?;
|
||||
let view_tables = read_view_tables(identifiers, conn)?;
|
||||
for view_table in view_tables {
|
||||
let _ = notify_views_changed(&view_table.belong_to_id, trash_can.clone(), conn)?;
|
||||
notify_dart(view_table, WorkspaceNotification::ViewDeleted);
|
||||
@ -320,7 +323,7 @@ async fn handle_trash_event(
|
||||
TrashEvent::Putback(identifiers, ret) => {
|
||||
let result = || {
|
||||
let conn = &*db_result?;
|
||||
let view_tables = get_view_table_from(identifiers, conn)?;
|
||||
let view_tables = read_view_tables(identifiers, conn)?;
|
||||
for view_table in view_tables {
|
||||
let _ = notify_views_changed(&view_table.belong_to_id, trash_can.clone(), conn)?;
|
||||
notify_dart(view_table, WorkspaceNotification::ViewRestored);
|
||||
@ -337,7 +340,7 @@ async fn handle_trash_event(
|
||||
for identifier in identifiers.items {
|
||||
let view_table = ViewTableSql::read_view(&identifier.id, conn)?;
|
||||
let _ = ViewTableSql::delete_view(&identifier.id, conn)?;
|
||||
let _ = context.delete(identifier.id.clone().into())?;
|
||||
let _ = context.doc_ctrl.delete(identifier.id.clone().into())?;
|
||||
notify_ids.insert(view_table.belong_to_id);
|
||||
}
|
||||
|
||||
@ -354,7 +357,7 @@ async fn handle_trash_event(
|
||||
}
|
||||
}
|
||||
|
||||
fn get_view_table_from(identifiers: TrashIdentifiers, conn: &SqliteConnection) -> Result<Vec<ViewTable>, FlowyError> {
|
||||
fn read_view_tables(identifiers: TrashIdentifiers, conn: &SqliteConnection) -> Result<Vec<ViewTable>, FlowyError> {
|
||||
let mut view_tables = vec![];
|
||||
let _ = conn.immediate_transaction::<_, FlowyError, _>(|| {
|
||||
for identifier in identifiers.items {
|
||||
@ -377,7 +380,7 @@ fn notify_views_changed(
|
||||
trash_can: Arc<TrashController>,
|
||||
conn: &SqliteConnection,
|
||||
) -> FlowyResult<()> {
|
||||
let repeated_view = read_local_belonging_view(belong_to_id, trash_can.clone(), conn)?;
|
||||
let repeated_view = read_belonging_views_on_local(belong_to_id, trash_can.clone(), conn)?;
|
||||
tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str());
|
||||
send_dart_notification(&belong_to_id, WorkspaceNotification::AppViewsChanged)
|
||||
.payload(repeated_view)
|
||||
@ -385,13 +388,13 @@ fn notify_views_changed(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_local_belonging_view(
|
||||
fn read_belonging_views_on_local(
|
||||
belong_to_id: &str,
|
||||
trash_can: Arc<TrashController>,
|
||||
trash_controller: Arc<TrashController>,
|
||||
conn: &SqliteConnection,
|
||||
) -> FlowyResult<RepeatedView> {
|
||||
let mut view_tables = ViewTableSql::read_views(belong_to_id, conn)?;
|
||||
let trash_ids = trash_can.trash_ids(conn)?;
|
||||
let trash_ids = trash_controller.read_trash_ids(conn)?;
|
||||
view_tables.retain(|view_table| !trash_ids.contains(&view_table.id));
|
||||
|
||||
let views = view_tables
|
||||
|
@ -62,21 +62,21 @@ pub(crate) async fn apply_doc_delta_handler(
|
||||
|
||||
pub(crate) async fn delete_view_handler(
|
||||
data: Data<QueryViewRequest>,
|
||||
controller: Unit<Arc<ViewController>>,
|
||||
trash_can: Unit<Arc<TrashController>>,
|
||||
view_controller: Unit<Arc<ViewController>>,
|
||||
trash_controller: Unit<Arc<TrashController>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let params: ViewIdentifiers = data.into_inner().try_into()?;
|
||||
for view_id in ¶ms.view_ids {
|
||||
let _ = controller.delete_view(view_id.into()).await;
|
||||
let _ = view_controller.delete_view(view_id.into()).await;
|
||||
}
|
||||
|
||||
let trash = controller
|
||||
let trash = view_controller
|
||||
.read_view_tables(params.view_ids)?
|
||||
.into_iter()
|
||||
.map(|view_table| view_table.into())
|
||||
.collect::<Vec<Trash>>();
|
||||
|
||||
let _ = trash_can.add(trash).await?;
|
||||
let _ = trash_controller.add(trash).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -42,10 +42,10 @@ impl WorkspaceController {
|
||||
params: CreateWorkspaceParams,
|
||||
) -> Result<Workspace, FlowyError> {
|
||||
let workspace = self.create_workspace_on_server(params.clone()).await?;
|
||||
self.create_workspace(workspace).await
|
||||
self.create_workspace_on_local(workspace).await
|
||||
}
|
||||
|
||||
pub(crate) async fn create_workspace(&self, workspace: Workspace) -> Result<Workspace, FlowyError> {
|
||||
pub(crate) async fn create_workspace_on_local(&self, workspace: Workspace) -> Result<Workspace, FlowyError> {
|
||||
let user_id = self.user.user_id()?;
|
||||
let token = self.user.token()?;
|
||||
let workspace_table = WorkspaceTable::new(workspace.clone(), &user_id);
|
||||
|
@ -19,8 +19,8 @@ pub trait DocumentUser: Send + Sync {
|
||||
}
|
||||
|
||||
pub struct DocumentContext {
|
||||
doc_ctrl: Arc<DocController>,
|
||||
user: Arc<dyn DocumentUser>,
|
||||
pub doc_ctrl: Arc<DocController>,
|
||||
pub user: Arc<dyn DocumentUser>,
|
||||
}
|
||||
|
||||
impl DocumentContext {
|
||||
@ -40,21 +40,11 @@ impl DocumentContext {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete(&self, params: DocIdentifier) -> Result<(), FlowyError> {
|
||||
let _ = self.doc_ctrl.delete(params)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn open(&self, params: DocIdentifier) -> Result<Arc<ClientDocEditor>, FlowyError> {
|
||||
let edit_context = self.doc_ctrl.open(params, self.user.db_pool()?).await?;
|
||||
Ok(edit_context)
|
||||
}
|
||||
|
||||
pub async fn close(&self, params: DocIdentifier) -> Result<(), FlowyError> {
|
||||
let _ = self.doc_ctrl.close(¶ms.doc_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_document_data(
|
||||
&self,
|
||||
params: DocIdentifier,
|
||||
@ -64,14 +54,4 @@ impl DocumentContext {
|
||||
let delta = edit_context.delta().await?;
|
||||
Ok(delta)
|
||||
}
|
||||
|
||||
pub async fn apply_doc_delta(&self, params: DocumentDelta) -> Result<DocumentDelta, FlowyError> {
|
||||
// workaround: compare the rust's delta with flutter's delta. Will be removed
|
||||
// very soon
|
||||
let doc = self
|
||||
.doc_ctrl
|
||||
.apply_local_delta(params.clone(), self.user.db_pool()?)
|
||||
.await?;
|
||||
Ok(doc)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use flowy_error::FlowyResult;
|
||||
use lib_infra::future::FutureResult;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) struct DocController {
|
||||
pub struct DocController {
|
||||
server: Server,
|
||||
ws_receivers: Arc<DocumentWSReceivers>,
|
||||
ws_sender: Arc<dyn DocumentWebSocket>,
|
||||
@ -52,7 +52,7 @@ impl DocController {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn open(
|
||||
pub async fn open(
|
||||
&self,
|
||||
params: DocIdentifier,
|
||||
pool: Arc<ConnectionPool>,
|
||||
@ -66,7 +66,7 @@ impl DocController {
|
||||
Ok(edit_doc_ctx)
|
||||
}
|
||||
|
||||
pub(crate) fn close(&self, doc_id: &str) -> Result<(), FlowyError> {
|
||||
pub fn close(&self, doc_id: &str) -> Result<(), FlowyError> {
|
||||
tracing::debug!("Close document {}", doc_id);
|
||||
self.open_cache.remove(doc_id);
|
||||
self.ws_receivers.remove_receiver(doc_id);
|
||||
@ -74,7 +74,7 @@ impl DocController {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
pub(crate) fn delete(&self, params: DocIdentifier) -> Result<(), FlowyError> {
|
||||
pub fn delete(&self, params: DocIdentifier) -> Result<(), FlowyError> {
|
||||
let doc_id = ¶ms.doc_id;
|
||||
self.open_cache.remove(doc_id);
|
||||
self.ws_receivers.remove_receiver(doc_id);
|
||||
@ -86,7 +86,7 @@ impl DocController {
|
||||
// json : {"retain":7,"attributes":{"bold":null}}
|
||||
// deserialize delta: [ {retain: 7, attributes: {Bold: AttributeValue(None)}} ]
|
||||
#[tracing::instrument(level = "debug", skip(self, delta, db_pool), fields(doc_id = %delta.doc_id), err)]
|
||||
pub(crate) async fn apply_local_delta(
|
||||
pub async fn apply_local_delta(
|
||||
&self,
|
||||
delta: DocumentDelta,
|
||||
db_pool: Arc<ConnectionPool>,
|
||||
|
@ -70,7 +70,7 @@ pub struct FlowySDK {
|
||||
#[allow(dead_code)]
|
||||
config: FlowySDKConfig,
|
||||
pub user_session: Arc<UserSession>,
|
||||
pub flowy_document: Arc<DocumentContext>,
|
||||
pub document_ctx: Arc<DocumentContext>,
|
||||
pub core: Arc<CoreContext>,
|
||||
pub dispatcher: Arc<EventDispatcher>,
|
||||
pub ws_manager: Arc<FlowyWSConnect>,
|
||||
@ -101,7 +101,7 @@ impl FlowySDK {
|
||||
Self {
|
||||
config,
|
||||
user_session,
|
||||
flowy_document,
|
||||
document_ctx: flowy_document,
|
||||
core: core_ctx,
|
||||
dispatcher,
|
||||
ws_manager,
|
||||
|
@ -31,7 +31,7 @@ impl EditorTest {
|
||||
let _ = sdk.init_user().await;
|
||||
let test = ViewTest::new(&sdk).await;
|
||||
let doc_identifier: DocIdentifier = test.view.id.clone().into();
|
||||
let editor = sdk.flowy_document.open(doc_identifier).await.unwrap();
|
||||
let editor = sdk.document_ctx.open(doc_identifier).await.unwrap();
|
||||
Self { sdk, editor }
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ use flowy_user::{
|
||||
event::UserEvent::{InitUser, SignIn, SignOut, SignUp},
|
||||
};
|
||||
use lib_dispatch::prelude::{EventDispatcher, ModuleRequest, ToBytes};
|
||||
use lib_infra::uuid;
|
||||
use lib_infra::uuid_string;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
@ -293,7 +293,7 @@ pub fn root_dir() -> String {
|
||||
root_dir
|
||||
}
|
||||
|
||||
pub fn random_email() -> String { format!("{}@appflowy.io", uuid()) }
|
||||
pub fn random_email() -> String { format!("{}@appflowy.io", uuid_string()) }
|
||||
|
||||
pub fn login_email() -> String { "annie2@appflowy.io".to_string() }
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::helper::*;
|
||||
use backend_service::configuration::{get_client_server_configuration, ClientServerConfiguration};
|
||||
use flowy_sdk::{FlowySDK, FlowySDKConfig};
|
||||
use flowy_user::entities::UserProfile;
|
||||
use lib_infra::uuid;
|
||||
use lib_infra::uuid_string;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{event_builder::*, helper::*, *};
|
||||
@ -31,7 +31,7 @@ impl FlowySDKTest {
|
||||
}
|
||||
|
||||
pub fn setup_with(server_config: ClientServerConfiguration) -> Self {
|
||||
let config = FlowySDKConfig::new(&root_dir(), server_config, &uuid()).log_filter("debug");
|
||||
let config = FlowySDKConfig::new(&root_dir(), server_config, &uuid_string()).log_filter("debug");
|
||||
let sdk = FlowySDK::new(config);
|
||||
Self(sdk)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use crate::services::server::UserServerAPI;
|
||||
use lib_infra::{future::FutureResult, uuid};
|
||||
use lib_infra::{future::FutureResult, uuid_string};
|
||||
|
||||
pub struct UserServerMock {}
|
||||
|
||||
@ -12,7 +12,7 @@ impl UserServerMock {}
|
||||
|
||||
impl UserServerAPI for UserServerMock {
|
||||
fn sign_up(&self, params: SignUpParams) -> FutureResult<SignUpResponse, FlowyError> {
|
||||
let uid = uuid();
|
||||
let uid = uuid_string();
|
||||
FutureResult::new(async move {
|
||||
Ok(SignUpResponse {
|
||||
user_id: uid.clone(),
|
||||
@ -24,7 +24,7 @@ impl UserServerAPI for UserServerMock {
|
||||
}
|
||||
|
||||
fn sign_in(&self, params: SignInParams) -> FutureResult<SignInResponse, FlowyError> {
|
||||
let user_id = uuid();
|
||||
let user_id = uuid_string();
|
||||
FutureResult::new(async {
|
||||
Ok(SignInResponse {
|
||||
user_id: user_id.clone(),
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::helper::*;
|
||||
use flowy_test::{event_builder::UserModuleEventBuilder, FlowySDKTest};
|
||||
use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
|
||||
use lib_infra::uuid;
|
||||
use lib_infra::uuid_string;
|
||||
use serial_test::*;
|
||||
|
||||
#[tokio::test]
|
||||
@ -53,7 +53,7 @@ async fn user_update_with_name() {
|
||||
async fn user_update_with_email() {
|
||||
let sdk = FlowySDKTest::setup();
|
||||
let user = sdk.init_user().await;
|
||||
let new_email = format!("{}@gmail.com", uuid());
|
||||
let new_email = format!("{}@gmail.com", uuid_string());
|
||||
let request = UpdateUserRequest::new(&user.id).email(&new_email);
|
||||
let _ = UserModuleEventBuilder::new(sdk.clone())
|
||||
.event(UpdateUser)
|
||||
|
@ -2,7 +2,7 @@ use bytes::Bytes;
|
||||
use dashmap::DashMap;
|
||||
use flowy_collaboration::{entities::prelude::*, errors::CollaborateError, sync::*};
|
||||
// use flowy_net::services::ws::*;
|
||||
use lib_infra::future::{BoxResultFuture, FutureResultSend};
|
||||
use lib_infra::future::BoxResultFuture;
|
||||
use lib_ws::{WSModule, WebSocketRawMessage};
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
|
@ -54,7 +54,7 @@ impl ClientServerConfiguration {
|
||||
|
||||
pub fn view_url(&self) -> String { format!("{}/api/view", self.base_url()) }
|
||||
|
||||
pub fn doc_url(&self) -> String { format!("{}/api/document", self.base_url()) }
|
||||
pub fn doc_url(&self) -> String { format!("{}/api/doc", self.base_url()) }
|
||||
|
||||
pub fn trash_url(&self) -> String { format!("{}/api/trash", self.base_url()) }
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
entities::revision::{RepeatedRevision, Revision},
|
||||
errors::{internal_error, CollaborateError},
|
||||
errors::CollaborateError,
|
||||
};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use lib_ot::{core::OperationTransformable, errors::OTError, rich_text::RichTextDelta};
|
||||
@ -43,8 +43,11 @@ impl DocumentInfo {
|
||||
for revision in revisions {
|
||||
base_rev_id = revision.base_rev_id;
|
||||
rev_id = revision.rev_id;
|
||||
let delta = RichTextDelta::from_bytes(revision.delta_data).map_err(internal_error)?;
|
||||
document_delta = document_delta.compose(&delta).map_err(internal_error)?;
|
||||
let delta = RichTextDelta::from_bytes(revision.delta_data)
|
||||
.map_err(|e| CollaborateError::internal().context(format!("Parser revision failed. {:?}", e)))?;
|
||||
document_delta = document_delta
|
||||
.compose(&delta)
|
||||
.map_err(|e| CollaborateError::internal().context(format!("Compose delta failed. {:?}", e)))?;
|
||||
}
|
||||
let text = document_delta.to_json();
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::document::default::initial_delta;
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use lib_ot::rich_text::RichTextDelta;
|
||||
@ -43,12 +42,6 @@ impl Revision {
|
||||
#[allow(dead_code)]
|
||||
pub fn is_initial(&self) -> bool { self.rev_id == 0 }
|
||||
|
||||
pub fn initial_revision(user_id: &str, doc_id: &str, ty: RevType) -> Self {
|
||||
let delta_data = initial_delta().to_bytes();
|
||||
let md5 = format!("{:x}", md5::compute(&delta_data));
|
||||
Revision::new(doc_id, 0, 0, delta_data, ty, user_id, md5)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
doc_id: &str,
|
||||
base_rev_id: i64,
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
use async_stream::stream;
|
||||
use dashmap::DashMap;
|
||||
use futures::stream::StreamExt;
|
||||
use lib_infra::future::{BoxResultFuture, FutureResultSend};
|
||||
use lib_infra::future::BoxResultFuture;
|
||||
use lib_ot::rich_text::RichTextDelta;
|
||||
use std::{convert::TryFrom, fmt::Debug, sync::Arc};
|
||||
use tokio::{
|
||||
@ -62,14 +62,13 @@ impl ServerDocumentManager {
|
||||
|
||||
let result = match self.get_document_handler(&doc_id).await {
|
||||
None => {
|
||||
let _ = self.create_document(&doc_id, revisions).await.map_err(internal_error)?;
|
||||
let _ = self.create_document(&doc_id, revisions).await.map_err(|e| {
|
||||
CollaborateError::internal().context(format!("Server crate document failed: {}", e))
|
||||
})?;
|
||||
Ok(())
|
||||
},
|
||||
Some(handler) => {
|
||||
let _ = handler
|
||||
.apply_revisions(doc_id.clone(), user, revisions)
|
||||
.await
|
||||
.map_err(internal_error)?;
|
||||
let _ = handler.apply_revisions(doc_id.clone(), user, revisions).await?;
|
||||
Ok(())
|
||||
},
|
||||
};
|
||||
@ -102,6 +101,7 @@ impl ServerDocumentManager {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, revisions), err)]
|
||||
async fn create_document(
|
||||
&self,
|
||||
doc_id: &str,
|
||||
@ -117,7 +117,7 @@ impl ServerDocumentManager {
|
||||
let persistence = self.persistence.clone();
|
||||
let handle = spawn_blocking(|| OpenDocHandle::new(doc, persistence))
|
||||
.await
|
||||
.map_err(internal_error)?;
|
||||
.map_err(|e| CollaborateError::internal().context(format!("Create open doc handler failed: {}", e)))?;
|
||||
let handle = Arc::new(handle?);
|
||||
self.open_doc_map.insert(doc_id, handle.clone());
|
||||
Ok(handle)
|
||||
@ -125,6 +125,7 @@ impl ServerDocumentManager {
|
||||
}
|
||||
|
||||
struct OpenDocHandle {
|
||||
doc_id: String,
|
||||
sender: mpsc::Sender<DocumentCommand>,
|
||||
persistence: Arc<dyn DocumentPersistence>,
|
||||
users: DashMap<String, Arc<dyn RevisionUser>>,
|
||||
@ -132,17 +133,20 @@ struct OpenDocHandle {
|
||||
|
||||
impl OpenDocHandle {
|
||||
fn new(doc: DocumentInfo, persistence: Arc<dyn DocumentPersistence>) -> Result<Self, CollaborateError> {
|
||||
let doc_id = doc.doc_id.clone();
|
||||
let (sender, receiver) = mpsc::channel(100);
|
||||
let users = DashMap::new();
|
||||
let queue = DocumentCommandQueue::new(receiver, doc)?;
|
||||
tokio::task::spawn(queue.run());
|
||||
Ok(Self {
|
||||
doc_id,
|
||||
sender,
|
||||
persistence,
|
||||
users,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, user, revisions), err)]
|
||||
async fn apply_revisions(
|
||||
&self,
|
||||
doc_id: String,
|
||||
@ -159,18 +163,28 @@ impl OpenDocHandle {
|
||||
persistence,
|
||||
ret,
|
||||
};
|
||||
|
||||
let _ = self.send(msg, rx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send<T>(&self, msg: DocumentCommand, rx: oneshot::Receiver<T>) -> CollaborateResult<T> {
|
||||
let _ = self.sender.send(msg).await.map_err(internal_error)?;
|
||||
let result = rx.await.map_err(internal_error)?;
|
||||
Ok(result)
|
||||
let _ = self
|
||||
.sender
|
||||
.send(msg)
|
||||
.await
|
||||
.map_err(|e| CollaborateError::internal().context(format!("Send document command failed: {}", e)))?;
|
||||
Ok(rx.await.map_err(internal_error)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
impl std::ops::Drop for OpenDocHandle {
|
||||
fn drop(&mut self) {
|
||||
log::debug!("{} OpenDocHandle drop", self.doc_id);
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Debug)]
|
||||
enum DocumentCommand {
|
||||
ApplyRevisions {
|
||||
doc_id: String,
|
||||
@ -229,12 +243,20 @@ impl DocumentCommandQueue {
|
||||
persistence,
|
||||
ret,
|
||||
} => {
|
||||
self.synchronizer
|
||||
.apply_revisions(doc_id, user, revisions, persistence)
|
||||
let result = self
|
||||
.synchronizer
|
||||
.sync_revisions(doc_id, user, revisions, persistence)
|
||||
.await
|
||||
.unwrap();
|
||||
let _ = ret.send(Ok(()));
|
||||
.map_err(internal_error);
|
||||
log::debug!("handle message {:?}", result);
|
||||
let _ = ret.send(result);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Drop for DocumentCommandQueue {
|
||||
fn drop(&mut self) {
|
||||
log::debug!("{} DocumentCommandQueue drop", self.doc_id);
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,11 @@ use crate::{
|
||||
revision::{Revision, RevisionRange},
|
||||
ws::{DocumentServerWSData, DocumentServerWSDataBuilder},
|
||||
},
|
||||
errors::CollaborateError,
|
||||
sync::DocumentPersistence,
|
||||
};
|
||||
use futures::TryFutureExt;
|
||||
use lib_ot::{core::OperationTransformable, errors::OTError, rich_text::RichTextDelta};
|
||||
|
||||
use lib_ot::{core::OperationTransformable, rich_text::RichTextDelta};
|
||||
use parking_lot::RwLock;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
@ -48,19 +49,16 @@ impl RevisionSynchronizer {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, user, revisions, persistence), err)]
|
||||
pub async fn apply_revisions(
|
||||
pub async fn sync_revisions(
|
||||
&self,
|
||||
doc_id: String,
|
||||
user: Arc<dyn RevisionUser>,
|
||||
revisions: Vec<Revision>,
|
||||
persistence: Arc<dyn DocumentPersistence>,
|
||||
) -> Result<(), OTError> {
|
||||
) -> Result<(), CollaborateError> {
|
||||
if revisions.is_empty() {
|
||||
// Return all the revisions to client
|
||||
let revisions = persistence
|
||||
.get_doc_revisions(&doc_id)
|
||||
.map_err(|e| OTError::internal().context(e))
|
||||
.await?;
|
||||
let revisions = persistence.get_doc_revisions(&doc_id).await?;
|
||||
let data = DocumentServerWSDataBuilder::build_push_message(&doc_id, revisions);
|
||||
user.receive(SyncResponse::Push(data));
|
||||
return Ok(());
|
||||
@ -78,10 +76,8 @@ impl RevisionSynchronizer {
|
||||
let server_rev_id = next(server_base_rev_id);
|
||||
if server_base_rev_id == first_revision.base_rev_id || server_rev_id == first_revision.rev_id {
|
||||
// The rev is in the right order, just compose it.
|
||||
{
|
||||
for revision in &revisions {
|
||||
let _ = self.compose_revision(revision)?;
|
||||
}
|
||||
for revision in &revisions {
|
||||
let _ = self.compose_revision(revision)?;
|
||||
}
|
||||
user.receive(SyncResponse::NewRevision(revisions));
|
||||
} else {
|
||||
@ -108,7 +104,11 @@ impl RevisionSynchronizer {
|
||||
let rev_ids: Vec<i64> = (from_rev_id..=to_rev_id).collect();
|
||||
let revisions = match persistence.get_revisions(&self.doc_id, rev_ids).await {
|
||||
Ok(revisions) => {
|
||||
assert_eq!(revisions.is_empty(), false);
|
||||
assert_eq!(
|
||||
revisions.is_empty(),
|
||||
false,
|
||||
"revisions should not be empty if the doc exists"
|
||||
);
|
||||
revisions
|
||||
},
|
||||
Err(e) => {
|
||||
@ -126,7 +126,7 @@ impl RevisionSynchronizer {
|
||||
|
||||
pub fn doc_json(&self) -> String { self.document.read().to_json() }
|
||||
|
||||
fn compose_revision(&self, revision: &Revision) -> Result<(), OTError> {
|
||||
fn compose_revision(&self, revision: &Revision) -> Result<(), CollaborateError> {
|
||||
let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
|
||||
let _ = self.compose_delta(delta)?;
|
||||
let _ = self.rev_id.fetch_update(SeqCst, SeqCst, |_e| Some(revision.rev_id));
|
||||
@ -134,13 +134,13 @@ impl RevisionSynchronizer {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, revision))]
|
||||
fn transform_revision(&self, revision: &Revision) -> Result<(RichTextDelta, RichTextDelta), OTError> {
|
||||
fn transform_revision(&self, revision: &Revision) -> Result<(RichTextDelta, RichTextDelta), CollaborateError> {
|
||||
let cli_delta = RichTextDelta::from_bytes(&revision.delta_data)?;
|
||||
let result = self.document.read().delta().transform(&cli_delta)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn compose_delta(&self, delta: RichTextDelta) -> Result<(), OTError> {
|
||||
fn compose_delta(&self, delta: RichTextDelta) -> Result<(), CollaborateError> {
|
||||
if delta.is_empty() {
|
||||
log::warn!("Composed delta is empty");
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
view::{ViewName, ViewThumbnail},
|
||||
},
|
||||
};
|
||||
|
||||
use flowy_collaboration::document::default::initial_delta_string;
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use std::convert::TryInto;
|
||||
|
||||
@ -68,16 +68,32 @@ pub struct CreateViewParams {
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub view_type: ViewType,
|
||||
|
||||
#[pb(index = 6)]
|
||||
pub view_data: String,
|
||||
|
||||
#[pb(index = 7)]
|
||||
pub view_id: String,
|
||||
}
|
||||
|
||||
impl CreateViewParams {
|
||||
pub fn new(belong_to_id: String, name: String, desc: String, view_type: ViewType, thumbnail: String) -> Self {
|
||||
pub fn new(
|
||||
belong_to_id: String,
|
||||
name: String,
|
||||
desc: String,
|
||||
view_type: ViewType,
|
||||
thumbnail: String,
|
||||
view_data: String,
|
||||
view_id: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
belong_to_id,
|
||||
name,
|
||||
desc,
|
||||
thumbnail,
|
||||
view_type,
|
||||
view_data,
|
||||
view_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,7 +104,8 @@ impl TryInto<CreateViewParams> for CreateViewRequest {
|
||||
fn try_into(self) -> Result<CreateViewParams, Self::Error> {
|
||||
let name = ViewName::parse(self.name)?.0;
|
||||
let belong_to_id = AppId::parse(self.belong_to_id)?.0;
|
||||
|
||||
let view_data = initial_delta_string();
|
||||
let view_id = uuid::Uuid::new_v4().to_string();
|
||||
let thumbnail = match self.thumbnail {
|
||||
None => "".to_string(),
|
||||
Some(thumbnail) => ViewThumbnail::parse(thumbnail)?.0,
|
||||
@ -100,6 +117,8 @@ impl TryInto<CreateViewParams> for CreateViewRequest {
|
||||
self.desc,
|
||||
self.view_type,
|
||||
thumbnail,
|
||||
view_data,
|
||||
view_id,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -387,6 +387,8 @@ pub struct CreateViewParams {
|
||||
pub desc: ::std::string::String,
|
||||
pub thumbnail: ::std::string::String,
|
||||
pub view_type: ViewType,
|
||||
pub view_data: ::std::string::String,
|
||||
pub view_id: ::std::string::String,
|
||||
// special fields
|
||||
pub unknown_fields: ::protobuf::UnknownFields,
|
||||
pub cached_size: ::protobuf::CachedSize,
|
||||
@ -521,6 +523,58 @@ impl CreateViewParams {
|
||||
pub fn set_view_type(&mut self, v: ViewType) {
|
||||
self.view_type = v;
|
||||
}
|
||||
|
||||
// string view_data = 6;
|
||||
|
||||
|
||||
pub fn get_view_data(&self) -> &str {
|
||||
&self.view_data
|
||||
}
|
||||
pub fn clear_view_data(&mut self) {
|
||||
self.view_data.clear();
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
pub fn set_view_data(&mut self, v: ::std::string::String) {
|
||||
self.view_data = v;
|
||||
}
|
||||
|
||||
// Mutable pointer to the field.
|
||||
// If field is not initialized, it is initialized with default value first.
|
||||
pub fn mut_view_data(&mut self) -> &mut ::std::string::String {
|
||||
&mut self.view_data
|
||||
}
|
||||
|
||||
// Take field
|
||||
pub fn take_view_data(&mut self) -> ::std::string::String {
|
||||
::std::mem::replace(&mut self.view_data, ::std::string::String::new())
|
||||
}
|
||||
|
||||
// string view_id = 7;
|
||||
|
||||
|
||||
pub fn get_view_id(&self) -> &str {
|
||||
&self.view_id
|
||||
}
|
||||
pub fn clear_view_id(&mut self) {
|
||||
self.view_id.clear();
|
||||
}
|
||||
|
||||
// Param is passed by value, moved
|
||||
pub fn set_view_id(&mut self, v: ::std::string::String) {
|
||||
self.view_id = v;
|
||||
}
|
||||
|
||||
// Mutable pointer to the field.
|
||||
// If field is not initialized, it is initialized with default value first.
|
||||
pub fn mut_view_id(&mut self) -> &mut ::std::string::String {
|
||||
&mut self.view_id
|
||||
}
|
||||
|
||||
// Take field
|
||||
pub fn take_view_id(&mut self) -> ::std::string::String {
|
||||
::std::mem::replace(&mut self.view_id, ::std::string::String::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for CreateViewParams {
|
||||
@ -547,6 +601,12 @@ impl ::protobuf::Message for CreateViewParams {
|
||||
5 => {
|
||||
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.view_type, 5, &mut self.unknown_fields)?
|
||||
},
|
||||
6 => {
|
||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.view_data)?;
|
||||
},
|
||||
7 => {
|
||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.view_id)?;
|
||||
},
|
||||
_ => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
|
||||
},
|
||||
@ -574,6 +634,12 @@ impl ::protobuf::Message for CreateViewParams {
|
||||
if self.view_type != ViewType::Blank {
|
||||
my_size += ::protobuf::rt::enum_size(5, self.view_type);
|
||||
}
|
||||
if !self.view_data.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(6, &self.view_data);
|
||||
}
|
||||
if !self.view_id.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(7, &self.view_id);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
||||
self.cached_size.set(my_size);
|
||||
my_size
|
||||
@ -595,6 +661,12 @@ impl ::protobuf::Message for CreateViewParams {
|
||||
if self.view_type != ViewType::Blank {
|
||||
os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.view_type))?;
|
||||
}
|
||||
if !self.view_data.is_empty() {
|
||||
os.write_string(6, &self.view_data)?;
|
||||
}
|
||||
if !self.view_id.is_empty() {
|
||||
os.write_string(7, &self.view_id)?;
|
||||
}
|
||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
@ -658,6 +730,16 @@ impl ::protobuf::Message for CreateViewParams {
|
||||
|m: &CreateViewParams| { &m.view_type },
|
||||
|m: &mut CreateViewParams| { &mut m.view_type },
|
||||
));
|
||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
||||
"view_data",
|
||||
|m: &CreateViewParams| { &m.view_data },
|
||||
|m: &mut CreateViewParams| { &mut m.view_data },
|
||||
));
|
||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
||||
"view_id",
|
||||
|m: &CreateViewParams| { &m.view_id },
|
||||
|m: &mut CreateViewParams| { &mut m.view_id },
|
||||
));
|
||||
::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateViewParams>(
|
||||
"CreateViewParams",
|
||||
fields,
|
||||
@ -679,6 +761,8 @@ impl ::protobuf::Clear for CreateViewParams {
|
||||
self.desc.clear();
|
||||
self.thumbnail.clear();
|
||||
self.view_type = ViewType::Blank;
|
||||
self.view_data.clear();
|
||||
self.view_id.clear();
|
||||
self.unknown_fields.clear();
|
||||
}
|
||||
}
|
||||
@ -1394,88 +1478,95 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
long_to_id\x18\x01\x20\x01(\tR\nbelongToId\x12\x12\n\x04name\x18\x02\x20\
|
||||
\x01(\tR\x04name\x12\x12\n\x04desc\x18\x03\x20\x01(\tR\x04desc\x12\x1e\n\
|
||||
\tthumbnail\x18\x04\x20\x01(\tH\0R\tthumbnail\x12&\n\tview_type\x18\x05\
|
||||
\x20\x01(\x0e2\t.ViewTypeR\x08viewTypeB\x12\n\x10one_of_thumbnail\"\xa2\
|
||||
\x20\x01(\x0e2\t.ViewTypeR\x08viewTypeB\x12\n\x10one_of_thumbnail\"\xd8\
|
||||
\x01\n\x10CreateViewParams\x12\x20\n\x0cbelong_to_id\x18\x01\x20\x01(\tR\
|
||||
\nbelongToId\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12\x12\n\x04\
|
||||
desc\x18\x03\x20\x01(\tR\x04desc\x12\x1c\n\tthumbnail\x18\x04\x20\x01(\t\
|
||||
R\tthumbnail\x12&\n\tview_type\x18\x05\x20\x01(\x0e2\t.ViewTypeR\x08view\
|
||||
Type\"\x97\x02\n\x04View\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\
|
||||
\x20\n\x0cbelong_to_id\x18\x02\x20\x01(\tR\nbelongToId\x12\x12\n\x04name\
|
||||
\x18\x03\x20\x01(\tR\x04name\x12\x12\n\x04desc\x18\x04\x20\x01(\tR\x04de\
|
||||
sc\x12&\n\tview_type\x18\x05\x20\x01(\x0e2\t.ViewTypeR\x08viewType\x12\
|
||||
\x18\n\x07version\x18\x06\x20\x01(\x03R\x07version\x12-\n\nbelongings\
|
||||
\x18\x07\x20\x01(\x0b2\r.RepeatedViewR\nbelongings\x12#\n\rmodified_time\
|
||||
\x18\x08\x20\x01(\x03R\x0cmodifiedTime\x12\x1f\n\x0bcreate_time\x18\t\
|
||||
\x20\x01(\x03R\ncreateTime\"+\n\x0cRepeatedView\x12\x1b\n\x05items\x18\
|
||||
\x01\x20\x03(\x0b2\x05.ViewR\x05items*\x1e\n\x08ViewType\x12\t\n\x05Blan\
|
||||
k\x10\0\x12\x07\n\x03Doc\x10\x01J\xd1\n\n\x06\x12\x04\0\0!\x01\n\x08\n\
|
||||
\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x08\x01\n\n\n\x03\
|
||||
\x04\0\x01\x12\x03\x02\x08\x19\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\
|
||||
\x1c\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\
|
||||
\0\x01\x12\x03\x03\x0b\x17\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x1a\
|
||||
\x1b\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x04\0\x02\
|
||||
\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\
|
||||
\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x12\x13\n\x0b\n\x04\x04\0\
|
||||
\x02\x02\x12\x03\x05\x04\x14\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\
|
||||
\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\x03\x05\x0b\x0f\n\x0c\n\x05\x04\
|
||||
\0\x02\x02\x03\x12\x03\x05\x12\x13\n\x0b\n\x04\x04\0\x08\0\x12\x03\x06\
|
||||
\x044\n\x0c\n\x05\x04\0\x08\0\x01\x12\x03\x06\n\x1a\n\x0b\n\x04\x04\0\
|
||||
\x02\x03\x12\x03\x06\x1d2\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x06\x1d#\
|
||||
\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06$-\n\x0c\n\x05\x04\0\x02\x03\
|
||||
\x03\x12\x03\x0601\n\x0b\n\x04\x04\0\x02\x04\x12\x03\x07\x04\x1b\n\x0c\n\
|
||||
\x05\x04\0\x02\x04\x06\x12\x03\x07\x04\x0c\n\x0c\n\x05\x04\0\x02\x04\x01\
|
||||
\x12\x03\x07\r\x16\n\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x07\x19\x1a\n\n\
|
||||
\n\x02\x04\x01\x12\x04\t\0\x0f\x01\n\n\n\x03\x04\x01\x01\x12\x03\t\x08\
|
||||
\x18\n\x0b\n\x04\x04\x01\x02\0\x12\x03\n\x04\x1c\n\x0c\n\x05\x04\x01\x02\
|
||||
\0\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\n\x0b\x17\n\
|
||||
\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\n\x1a\x1b\n\x0b\n\x04\x04\x01\x02\
|
||||
\x01\x12\x03\x0b\x04\x14\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x0b\x04\
|
||||
\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x0b\x0b\x0f\n\x0c\n\x05\x04\
|
||||
\x01\x02\x01\x03\x12\x03\x0b\x12\x13\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\
|
||||
\x0c\x04\x14\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\x0c\x04\n\n\x0c\n\
|
||||
\x05\x04\x01\x02\x02\x01\x12\x03\x0c\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x02\
|
||||
\x03\x12\x03\x0c\x12\x13\n\x0b\n\x04\x04\x01\x02\x03\x12\x03\r\x04\x19\n\
|
||||
\x0c\n\x05\x04\x01\x02\x03\x05\x12\x03\r\x04\n\n\x0c\n\x05\x04\x01\x02\
|
||||
\x03\x01\x12\x03\r\x0b\x14\n\x0c\n\x05\x04\x01\x02\x03\x03\x12\x03\r\x17\
|
||||
\x18\n\x0b\n\x04\x04\x01\x02\x04\x12\x03\x0e\x04\x1b\n\x0c\n\x05\x04\x01\
|
||||
\x02\x04\x06\x12\x03\x0e\x04\x0c\n\x0c\n\x05\x04\x01\x02\x04\x01\x12\x03\
|
||||
\x0e\r\x16\n\x0c\n\x05\x04\x01\x02\x04\x03\x12\x03\x0e\x19\x1a\n\n\n\x02\
|
||||
\x04\x02\x12\x04\x10\0\x1a\x01\n\n\n\x03\x04\x02\x01\x12\x03\x10\x08\x0c\
|
||||
\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x11\x04\x12\n\x0c\n\x05\x04\x02\x02\0\
|
||||
\x05\x12\x03\x11\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x11\x0b\r\n\
|
||||
\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x11\x10\x11\n\x0b\n\x04\x04\x02\x02\
|
||||
\x01\x12\x03\x12\x04\x1c\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\x12\x04\
|
||||
\n\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\x12\x0b\x17\n\x0c\n\x05\x04\
|
||||
\x02\x02\x01\x03\x12\x03\x12\x1a\x1b\n\x0b\n\x04\x04\x02\x02\x02\x12\x03\
|
||||
\x13\x04\x14\n\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\x13\x04\n\n\x0c\n\
|
||||
\x05\x04\x02\x02\x02\x01\x12\x03\x13\x0b\x0f\n\x0c\n\x05\x04\x02\x02\x02\
|
||||
\x03\x12\x03\x13\x12\x13\n\x0b\n\x04\x04\x02\x02\x03\x12\x03\x14\x04\x14\
|
||||
\n\x0c\n\x05\x04\x02\x02\x03\x05\x12\x03\x14\x04\n\n\x0c\n\x05\x04\x02\
|
||||
\x02\x03\x01\x12\x03\x14\x0b\x0f\n\x0c\n\x05\x04\x02\x02\x03\x03\x12\x03\
|
||||
\x14\x12\x13\n\x0b\n\x04\x04\x02\x02\x04\x12\x03\x15\x04\x1b\n\x0c\n\x05\
|
||||
\x04\x02\x02\x04\x06\x12\x03\x15\x04\x0c\n\x0c\n\x05\x04\x02\x02\x04\x01\
|
||||
\x12\x03\x15\r\x16\n\x0c\n\x05\x04\x02\x02\x04\x03\x12\x03\x15\x19\x1a\n\
|
||||
\x0b\n\x04\x04\x02\x02\x05\x12\x03\x16\x04\x16\n\x0c\n\x05\x04\x02\x02\
|
||||
\x05\x05\x12\x03\x16\x04\t\n\x0c\n\x05\x04\x02\x02\x05\x01\x12\x03\x16\n\
|
||||
\x11\n\x0c\n\x05\x04\x02\x02\x05\x03\x12\x03\x16\x14\x15\n\x0b\n\x04\x04\
|
||||
\x02\x02\x06\x12\x03\x17\x04\x20\n\x0c\n\x05\x04\x02\x02\x06\x06\x12\x03\
|
||||
\x17\x04\x10\n\x0c\n\x05\x04\x02\x02\x06\x01\x12\x03\x17\x11\x1b\n\x0c\n\
|
||||
\x05\x04\x02\x02\x06\x03\x12\x03\x17\x1e\x1f\n\x0b\n\x04\x04\x02\x02\x07\
|
||||
\x12\x03\x18\x04\x1c\n\x0c\n\x05\x04\x02\x02\x07\x05\x12\x03\x18\x04\t\n\
|
||||
\x0c\n\x05\x04\x02\x02\x07\x01\x12\x03\x18\n\x17\n\x0c\n\x05\x04\x02\x02\
|
||||
\x07\x03\x12\x03\x18\x1a\x1b\n\x0b\n\x04\x04\x02\x02\x08\x12\x03\x19\x04\
|
||||
\x1a\n\x0c\n\x05\x04\x02\x02\x08\x05\x12\x03\x19\x04\t\n\x0c\n\x05\x04\
|
||||
\x02\x02\x08\x01\x12\x03\x19\n\x15\n\x0c\n\x05\x04\x02\x02\x08\x03\x12\
|
||||
\x03\x19\x18\x19\n\n\n\x02\x04\x03\x12\x04\x1b\0\x1d\x01\n\n\n\x03\x04\
|
||||
\x03\x01\x12\x03\x1b\x08\x14\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x1c\x04\
|
||||
\x1c\n\x0c\n\x05\x04\x03\x02\0\x04\x12\x03\x1c\x04\x0c\n\x0c\n\x05\x04\
|
||||
\x03\x02\0\x06\x12\x03\x1c\r\x11\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\
|
||||
\x1c\x12\x17\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x1c\x1a\x1b\n\n\n\x02\
|
||||
\x05\0\x12\x04\x1e\0!\x01\n\n\n\x03\x05\0\x01\x12\x03\x1e\x05\r\n\x0b\n\
|
||||
\x04\x05\0\x02\0\x12\x03\x1f\x04\x0e\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\
|
||||
\x1f\x04\t\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x1f\x0c\r\n\x0b\n\x04\x05\
|
||||
\0\x02\x01\x12\x03\x20\x04\x0c\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x20\
|
||||
\x04\x07\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x20\n\x0bb\x06proto3\
|
||||
Type\x12\x1b\n\tview_data\x18\x06\x20\x01(\tR\x08viewData\x12\x17\n\x07v\
|
||||
iew_id\x18\x07\x20\x01(\tR\x06viewId\"\x97\x02\n\x04View\x12\x0e\n\x02id\
|
||||
\x18\x01\x20\x01(\tR\x02id\x12\x20\n\x0cbelong_to_id\x18\x02\x20\x01(\tR\
|
||||
\nbelongToId\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x12\n\x04\
|
||||
desc\x18\x04\x20\x01(\tR\x04desc\x12&\n\tview_type\x18\x05\x20\x01(\x0e2\
|
||||
\t.ViewTypeR\x08viewType\x12\x18\n\x07version\x18\x06\x20\x01(\x03R\x07v\
|
||||
ersion\x12-\n\nbelongings\x18\x07\x20\x01(\x0b2\r.RepeatedViewR\nbelongi\
|
||||
ngs\x12#\n\rmodified_time\x18\x08\x20\x01(\x03R\x0cmodifiedTime\x12\x1f\
|
||||
\n\x0bcreate_time\x18\t\x20\x01(\x03R\ncreateTime\"+\n\x0cRepeatedView\
|
||||
\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.ViewR\x05items*\x1e\n\x08V\
|
||||
iewType\x12\t\n\x05Blank\x10\0\x12\x07\n\x03Doc\x10\x01J\xbf\x0b\n\x06\
|
||||
\x12\x04\0\0#\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\
|
||||
\x04\x02\0\x08\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x19\n\x0b\n\x04\
|
||||
\x04\0\x02\0\x12\x03\x03\x04\x1c\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\
|
||||
\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x17\n\x0c\n\x05\x04\0\
|
||||
\x02\0\x03\x12\x03\x03\x1a\x1b\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\
|
||||
\x14\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\
|
||||
\x02\x01\x01\x12\x03\x04\x0b\x0f\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\
|
||||
\x04\x12\x13\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x14\n\x0c\n\x05\
|
||||
\x04\0\x02\x02\x05\x12\x03\x05\x04\n\n\x0c\n\x05\x04\0\x02\x02\x01\x12\
|
||||
\x03\x05\x0b\x0f\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x12\x13\n\x0b\
|
||||
\n\x04\x04\0\x08\0\x12\x03\x06\x044\n\x0c\n\x05\x04\0\x08\0\x01\x12\x03\
|
||||
\x06\n\x1a\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x1d2\n\x0c\n\x05\x04\0\
|
||||
\x02\x03\x05\x12\x03\x06\x1d#\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06$\
|
||||
-\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x0601\n\x0b\n\x04\x04\0\x02\x04\
|
||||
\x12\x03\x07\x04\x1b\n\x0c\n\x05\x04\0\x02\x04\x06\x12\x03\x07\x04\x0c\n\
|
||||
\x0c\n\x05\x04\0\x02\x04\x01\x12\x03\x07\r\x16\n\x0c\n\x05\x04\0\x02\x04\
|
||||
\x03\x12\x03\x07\x19\x1a\n\n\n\x02\x04\x01\x12\x04\t\0\x11\x01\n\n\n\x03\
|
||||
\x04\x01\x01\x12\x03\t\x08\x18\n\x0b\n\x04\x04\x01\x02\0\x12\x03\n\x04\
|
||||
\x1c\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\x01\
|
||||
\x02\0\x01\x12\x03\n\x0b\x17\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\n\x1a\
|
||||
\x1b\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x0b\x04\x14\n\x0c\n\x05\x04\x01\
|
||||
\x02\x01\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\
|
||||
\x0b\x0b\x0f\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x0b\x12\x13\n\x0b\n\
|
||||
\x04\x04\x01\x02\x02\x12\x03\x0c\x04\x14\n\x0c\n\x05\x04\x01\x02\x02\x05\
|
||||
\x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\x0c\x0b\x0f\n\
|
||||
\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x0c\x12\x13\n\x0b\n\x04\x04\x01\
|
||||
\x02\x03\x12\x03\r\x04\x19\n\x0c\n\x05\x04\x01\x02\x03\x05\x12\x03\r\x04\
|
||||
\n\n\x0c\n\x05\x04\x01\x02\x03\x01\x12\x03\r\x0b\x14\n\x0c\n\x05\x04\x01\
|
||||
\x02\x03\x03\x12\x03\r\x17\x18\n\x0b\n\x04\x04\x01\x02\x04\x12\x03\x0e\
|
||||
\x04\x1b\n\x0c\n\x05\x04\x01\x02\x04\x06\x12\x03\x0e\x04\x0c\n\x0c\n\x05\
|
||||
\x04\x01\x02\x04\x01\x12\x03\x0e\r\x16\n\x0c\n\x05\x04\x01\x02\x04\x03\
|
||||
\x12\x03\x0e\x19\x1a\n\x0b\n\x04\x04\x01\x02\x05\x12\x03\x0f\x04\x19\n\
|
||||
\x0c\n\x05\x04\x01\x02\x05\x05\x12\x03\x0f\x04\n\n\x0c\n\x05\x04\x01\x02\
|
||||
\x05\x01\x12\x03\x0f\x0b\x14\n\x0c\n\x05\x04\x01\x02\x05\x03\x12\x03\x0f\
|
||||
\x17\x18\n\x0b\n\x04\x04\x01\x02\x06\x12\x03\x10\x04\x17\n\x0c\n\x05\x04\
|
||||
\x01\x02\x06\x05\x12\x03\x10\x04\n\n\x0c\n\x05\x04\x01\x02\x06\x01\x12\
|
||||
\x03\x10\x0b\x12\n\x0c\n\x05\x04\x01\x02\x06\x03\x12\x03\x10\x15\x16\n\n\
|
||||
\n\x02\x04\x02\x12\x04\x12\0\x1c\x01\n\n\n\x03\x04\x02\x01\x12\x03\x12\
|
||||
\x08\x0c\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x13\x04\x12\n\x0c\n\x05\x04\
|
||||
\x02\x02\0\x05\x12\x03\x13\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\
|
||||
\x13\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x13\x10\x11\n\x0b\n\x04\
|
||||
\x04\x02\x02\x01\x12\x03\x14\x04\x1c\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\
|
||||
\x03\x14\x04\n\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\x14\x0b\x17\n\x0c\
|
||||
\n\x05\x04\x02\x02\x01\x03\x12\x03\x14\x1a\x1b\n\x0b\n\x04\x04\x02\x02\
|
||||
\x02\x12\x03\x15\x04\x14\n\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\x15\x04\
|
||||
\n\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\x15\x0b\x0f\n\x0c\n\x05\x04\
|
||||
\x02\x02\x02\x03\x12\x03\x15\x12\x13\n\x0b\n\x04\x04\x02\x02\x03\x12\x03\
|
||||
\x16\x04\x14\n\x0c\n\x05\x04\x02\x02\x03\x05\x12\x03\x16\x04\n\n\x0c\n\
|
||||
\x05\x04\x02\x02\x03\x01\x12\x03\x16\x0b\x0f\n\x0c\n\x05\x04\x02\x02\x03\
|
||||
\x03\x12\x03\x16\x12\x13\n\x0b\n\x04\x04\x02\x02\x04\x12\x03\x17\x04\x1b\
|
||||
\n\x0c\n\x05\x04\x02\x02\x04\x06\x12\x03\x17\x04\x0c\n\x0c\n\x05\x04\x02\
|
||||
\x02\x04\x01\x12\x03\x17\r\x16\n\x0c\n\x05\x04\x02\x02\x04\x03\x12\x03\
|
||||
\x17\x19\x1a\n\x0b\n\x04\x04\x02\x02\x05\x12\x03\x18\x04\x16\n\x0c\n\x05\
|
||||
\x04\x02\x02\x05\x05\x12\x03\x18\x04\t\n\x0c\n\x05\x04\x02\x02\x05\x01\
|
||||
\x12\x03\x18\n\x11\n\x0c\n\x05\x04\x02\x02\x05\x03\x12\x03\x18\x14\x15\n\
|
||||
\x0b\n\x04\x04\x02\x02\x06\x12\x03\x19\x04\x20\n\x0c\n\x05\x04\x02\x02\
|
||||
\x06\x06\x12\x03\x19\x04\x10\n\x0c\n\x05\x04\x02\x02\x06\x01\x12\x03\x19\
|
||||
\x11\x1b\n\x0c\n\x05\x04\x02\x02\x06\x03\x12\x03\x19\x1e\x1f\n\x0b\n\x04\
|
||||
\x04\x02\x02\x07\x12\x03\x1a\x04\x1c\n\x0c\n\x05\x04\x02\x02\x07\x05\x12\
|
||||
\x03\x1a\x04\t\n\x0c\n\x05\x04\x02\x02\x07\x01\x12\x03\x1a\n\x17\n\x0c\n\
|
||||
\x05\x04\x02\x02\x07\x03\x12\x03\x1a\x1a\x1b\n\x0b\n\x04\x04\x02\x02\x08\
|
||||
\x12\x03\x1b\x04\x1a\n\x0c\n\x05\x04\x02\x02\x08\x05\x12\x03\x1b\x04\t\n\
|
||||
\x0c\n\x05\x04\x02\x02\x08\x01\x12\x03\x1b\n\x15\n\x0c\n\x05\x04\x02\x02\
|
||||
\x08\x03\x12\x03\x1b\x18\x19\n\n\n\x02\x04\x03\x12\x04\x1d\0\x1f\x01\n\n\
|
||||
\n\x03\x04\x03\x01\x12\x03\x1d\x08\x14\n\x0b\n\x04\x04\x03\x02\0\x12\x03\
|
||||
\x1e\x04\x1c\n\x0c\n\x05\x04\x03\x02\0\x04\x12\x03\x1e\x04\x0c\n\x0c\n\
|
||||
\x05\x04\x03\x02\0\x06\x12\x03\x1e\r\x11\n\x0c\n\x05\x04\x03\x02\0\x01\
|
||||
\x12\x03\x1e\x12\x17\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x1e\x1a\x1b\n\
|
||||
\n\n\x02\x05\0\x12\x04\x20\0#\x01\n\n\n\x03\x05\0\x01\x12\x03\x20\x05\r\
|
||||
\n\x0b\n\x04\x05\0\x02\0\x12\x03!\x04\x0e\n\x0c\n\x05\x05\0\x02\0\x01\
|
||||
\x12\x03!\x04\t\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03!\x0c\r\n\x0b\n\x04\
|
||||
\x05\0\x02\x01\x12\x03\"\x04\x0c\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\"\
|
||||
\x04\x07\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\"\n\x0bb\x06proto3\
|
||||
";
|
||||
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
||||
|
@ -13,6 +13,8 @@ message CreateViewParams {
|
||||
string desc = 3;
|
||||
string thumbnail = 4;
|
||||
ViewType view_type = 5;
|
||||
string view_data = 6;
|
||||
string view_id = 7;
|
||||
}
|
||||
message View {
|
||||
string id = 1;
|
||||
|
@ -2,7 +2,7 @@ pub mod future;
|
||||
pub mod retry;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn uuid() -> String { uuid::Uuid::new_v4().to_string() }
|
||||
pub fn uuid_string() -> String { uuid::Uuid::new_v4().to_string() }
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn timestamp() -> i64 { chrono::Utc::now().timestamp() }
|
||||
|
Loading…
Reference in New Issue
Block a user