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