mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
rename files
This commit is contained in:
@ -8,7 +8,7 @@ use flowy_document::protobuf::{Doc, Revision};
|
||||
use flowy_net::errors::{internal_error, Result as DocResult, ServerError};
|
||||
use futures::stream::StreamExt;
|
||||
use sqlx::PgPool;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{atomic::Ordering::SeqCst, Arc};
|
||||
use tokio::{
|
||||
sync::{mpsc, oneshot},
|
||||
task::spawn_blocking,
|
||||
@ -35,6 +35,9 @@ pub enum EditMsg {
|
||||
DocumentJson {
|
||||
ret: oneshot::Sender<DocResult<String>>,
|
||||
},
|
||||
DocumentRevId {
|
||||
ret: oneshot::Sender<DocResult<i64>>,
|
||||
},
|
||||
NewDocUser {
|
||||
user: Arc<WsUser>,
|
||||
socket: Socket,
|
||||
@ -97,6 +100,10 @@ impl EditDocActor {
|
||||
.map_err(internal_error);
|
||||
let _ = ret.send(json);
|
||||
},
|
||||
EditMsg::DocumentRevId { ret } => {
|
||||
let edit_context = self.edit_doc.clone();
|
||||
let _ = ret.send(Ok(edit_context.rev_id.load(SeqCst)));
|
||||
},
|
||||
EditMsg::NewDocUser {
|
||||
user,
|
||||
socket,
|
||||
|
@ -32,8 +32,8 @@ use std::{
|
||||
};
|
||||
|
||||
pub struct ServerEditDoc {
|
||||
doc_id: String,
|
||||
rev_id: AtomicI64,
|
||||
pub doc_id: String,
|
||||
pub rev_id: AtomicI64,
|
||||
document: Arc<RwLock<Document>>,
|
||||
users: DashMap<String, EditUser>,
|
||||
}
|
||||
@ -121,7 +121,7 @@ impl ServerEditDoc {
|
||||
// The client document is outdated. Transform the client revision delta and then
|
||||
// send the prime delta to the client. Client should compose the this prime
|
||||
// delta.
|
||||
let cli_revision = self.transform_client_revision(&revision)?;
|
||||
let cli_revision = self.transform_revision(&revision)?;
|
||||
let ws_cli_revision = mk_push_rev_ws_message(&self.doc_id, cli_revision);
|
||||
user.socket.do_send(ws_cli_revision).map_err(internal_error)?;
|
||||
},
|
||||
@ -137,8 +137,16 @@ impl ServerEditDoc {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transform_client_revision(&self, revision: &Revision) -> Result<Revision, ServerError> {
|
||||
let (cli_prime, server_prime) = self.transform(&revision.delta_data).map_err(internal_error)?;
|
||||
#[tracing::instrument(level = "debug", skip(self, revision))]
|
||||
fn transform_revision(&self, revision: &Revision) -> Result<Revision, ServerError> {
|
||||
let cli_delta = Delta::from_bytes(&revision.delta_data).map_err(internal_error)?;
|
||||
let (cli_prime, server_prime) = self
|
||||
.document
|
||||
.read()
|
||||
.delta()
|
||||
.transform(&cli_delta)
|
||||
.map_err(internal_error)?;
|
||||
|
||||
let _ = self.compose_delta(server_prime)?;
|
||||
let cli_revision = self.mk_revision(revision.rev_id, cli_prime);
|
||||
Ok(cli_revision)
|
||||
@ -159,19 +167,14 @@ impl ServerEditDoc {
|
||||
revision
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self, delta_data))]
|
||||
fn transform(&self, delta_data: &Vec<u8>) -> Result<(Delta, Delta), OTError> {
|
||||
log::debug!("Document: {}", self.document.read().to_json());
|
||||
let doc_delta = self.document.read().delta().clone();
|
||||
let cli_delta = Delta::from_bytes(delta_data)?;
|
||||
|
||||
log::debug!("Compose delta: {}", cli_delta);
|
||||
let (cli_prime, server_prime) = doc_delta.transform(&cli_delta)?;
|
||||
|
||||
Ok((cli_prime, server_prime))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self), err)]
|
||||
#[tracing::instrument(
|
||||
level = "debug",
|
||||
skip(self, delta),
|
||||
fields(
|
||||
delta = %delta.to_json(),
|
||||
result,
|
||||
)
|
||||
)]
|
||||
fn compose_delta(&self, delta: Delta) -> Result<(), ServerError> {
|
||||
// Opti: push each revision into queue and process it one by one.
|
||||
match self.document.try_write_for(Duration::from_millis(300)) {
|
||||
@ -180,7 +183,7 @@ impl ServerEditDoc {
|
||||
},
|
||||
Some(mut write_guard) => {
|
||||
let _ = write_guard.compose_delta(&delta).map_err(internal_error)?;
|
||||
log::debug!("Document: {}", write_guard.to_json());
|
||||
tracing::Span::current().record("result", &write_guard.to_json().as_str());
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
|
@ -18,7 +18,6 @@ impl DocHandle {
|
||||
let (sender, receiver) = mpsc::channel(100);
|
||||
let actor = EditDocActor::new(receiver, doc, pg_pool)?;
|
||||
tokio::task::spawn(actor.run());
|
||||
|
||||
Ok(Self { sender })
|
||||
}
|
||||
|
||||
@ -58,6 +57,12 @@ impl DocHandle {
|
||||
self.send(msg, rx).await?
|
||||
}
|
||||
|
||||
pub async fn rev_id(&self) -> DocResult<i64> {
|
||||
let (ret, rx) = oneshot::channel();
|
||||
let msg = EditMsg::DocumentRevId { ret };
|
||||
self.send(msg, rx).await?
|
||||
}
|
||||
|
||||
pub(crate) async fn send<T>(&self, msg: EditMsg, rx: oneshot::Receiver<T>) -> DocResult<T> {
|
||||
let _ = self.sender.send(msg).await.map_err(internal_error)?;
|
||||
let result = rx.await?;
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::document::helper::{DocScript, DocumentTest};
|
||||
use flowy_document::services::doc::{Document, FlowyDoc};
|
||||
use flowy_ot::core::{Attribute, Interval};
|
||||
|
||||
#[rustfmt::skip]
|
||||
// ┌─────────┐ ┌─────────┐
|
||||
@ -16,15 +17,33 @@ use flowy_document::services::doc::{Document, FlowyDoc};
|
||||
// └──────────────────────────┘ │ │ └──────────────────────┘
|
||||
// │ │
|
||||
#[actix_rt::test]
|
||||
async fn delta_sync_after_ws_connection() {
|
||||
async fn delta_sync_while_editing() {
|
||||
let test = DocumentTest::new().await;
|
||||
test.run_scripts(vec![
|
||||
DocScript::ConnectWs,
|
||||
DocScript::OpenDoc,
|
||||
DocScript::SendText(0, "abc"),
|
||||
DocScript::SendText(3, "123"),
|
||||
DocScript::InsertText(0, "abc"),
|
||||
DocScript::InsertText(3, "123"),
|
||||
DocScript::AssertClient(r#"[{"insert":"abc123\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"abc123\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"abc123\n"}]"#, 2),
|
||||
])
|
||||
.await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn delta_sync_while_editing_with_attribute() {
|
||||
let test = DocumentTest::new().await;
|
||||
test.run_scripts(vec![
|
||||
DocScript::ConnectWs,
|
||||
DocScript::OpenDoc,
|
||||
DocScript::InsertText(0, "abc"),
|
||||
DocScript::FormatText(Interval::new(0, 3), Attribute::Bold(true)),
|
||||
DocScript::AssertClient(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"\n"}]"#, 2),
|
||||
DocScript::InsertText(3, "efg"),
|
||||
DocScript::FormatText(Interval::new(3, 5), Attribute::Italic(true)),
|
||||
DocScript::AssertClient(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"ef","attributes":{"bold":true,"italic":true}},{"insert":"g","attributes":{"bold":true}},{"insert":"\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"abc","attributes":{"bold":true}},{"insert":"ef","attributes":{"bold":true,"italic":true}},{"insert":"g","attributes":{"bold":true}},{"insert":"\n"}]"#, 4),
|
||||
])
|
||||
.await;
|
||||
}
|
||||
@ -54,7 +73,7 @@ async fn delta_sync_with_http_request() {
|
||||
DocScript::SetServerDocument(json, 3),
|
||||
DocScript::OpenDoc,
|
||||
DocScript::AssertClient(r#"[{"insert":"123456\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"123456\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"123456\n"}]"#, 3),
|
||||
])
|
||||
.await;
|
||||
}
|
||||
@ -116,10 +135,10 @@ async fn delta_sync_while_local_rev_less_than_server_rev() {
|
||||
test.run_scripts(vec![
|
||||
DocScript::OpenDoc,
|
||||
DocScript::SetServerDocument(json, 3),
|
||||
DocScript::SendText(0, "abc"),
|
||||
DocScript::InsertText(0, "abc"),
|
||||
DocScript::ConnectWs,
|
||||
DocScript::AssertClient(r#"[{"insert":"abc\n123\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"abc\n123\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"abc\n123\n"}]"#, 4),
|
||||
])
|
||||
.await;
|
||||
}
|
||||
@ -160,11 +179,11 @@ async fn delta_sync_while_local_rev_greater_than_server_rev() {
|
||||
DocScript::SetServerDocument(json, 1),
|
||||
DocScript::OpenDoc,
|
||||
DocScript::AssertClient(r#"[{"insert":"123\n"}]"#),
|
||||
DocScript::SendText(3, "abc"),
|
||||
DocScript::SendText(6, "efg"),
|
||||
DocScript::InsertText(3, "abc"),
|
||||
DocScript::InsertText(6, "efg"),
|
||||
DocScript::ConnectWs,
|
||||
DocScript::AssertClient(r#"[{"insert":"123abcefg\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"123abcefg\n"}]"#),
|
||||
DocScript::AssertServer(r#"[{"insert":"123abcefg\n"}]"#, 3),
|
||||
])
|
||||
.await;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use flowy_user::services::user::UserSession;
|
||||
use crate::helper::{spawn_server, TestServer};
|
||||
use flowy_document::protobuf::UpdateDocParams;
|
||||
|
||||
use flowy_ot::core::{Attribute, Interval};
|
||||
use parking_lot::RwLock;
|
||||
use serde::__private::Formatter;
|
||||
|
||||
@ -25,27 +26,14 @@ pub struct DocumentTest {
|
||||
#[derive(Clone)]
|
||||
pub enum DocScript {
|
||||
ConnectWs,
|
||||
SendText(usize, &'static str),
|
||||
InsertText(usize, &'static str),
|
||||
FormatText(Interval, Attribute),
|
||||
AssertClient(&'static str),
|
||||
AssertServer(&'static str),
|
||||
AssertServer(&'static str, i64),
|
||||
SetServerDocument(String, i64), // delta_json, rev_id
|
||||
OpenDoc,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DocScript {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let name = match self {
|
||||
DocScript::ConnectWs => "ConnectWs",
|
||||
DocScript::SendText(_, _) => "SendText",
|
||||
DocScript::AssertClient(_) => "AssertClient",
|
||||
DocScript::AssertServer(_) => "AssertServer",
|
||||
DocScript::SetServerDocument(_, _) => "SetServerDocument",
|
||||
DocScript::OpenDoc => "OpenDoc",
|
||||
};
|
||||
f.write_str(&format!("******** {} *********", name))
|
||||
}
|
||||
}
|
||||
|
||||
impl DocumentTest {
|
||||
pub async fn new() -> Self {
|
||||
let server = spawn_server().await;
|
||||
@ -122,19 +110,28 @@ async fn run_scripts(context: Arc<RwLock<ScriptContext>>, scripts: Vec<DocScript
|
||||
DocScript::OpenDoc => {
|
||||
context.write().open_doc().await;
|
||||
},
|
||||
DocScript::SendText(index, s) => {
|
||||
DocScript::InsertText(index, s) => {
|
||||
context.read().client_edit_context().insert(index, s).await.unwrap();
|
||||
},
|
||||
DocScript::FormatText(interval, attribute) => {
|
||||
context
|
||||
.read()
|
||||
.client_edit_context()
|
||||
.format(interval, attribute)
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
DocScript::AssertClient(s) => {
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
let json = context.read().client_edit_context().doc_json().await.unwrap();
|
||||
assert_eq(s, &json);
|
||||
},
|
||||
DocScript::AssertServer(s) => {
|
||||
DocScript::AssertServer(s, rev_id) => {
|
||||
sleep(Duration::from_millis(100)).await;
|
||||
let pg_pool = context.read().pool.clone();
|
||||
let doc_manager = context.read().doc_manager.clone();
|
||||
let edit_doc = doc_manager.get(&doc_id, pg_pool).await.unwrap().unwrap();
|
||||
assert_eq!(edit_doc.rev_id().await.unwrap(), rev_id);
|
||||
let json = edit_doc.document_json().await.unwrap();
|
||||
assert_eq(s, &json);
|
||||
},
|
||||
|
Reference in New Issue
Block a user