send connect message when open the document

This commit is contained in:
appflowy 2021-12-19 21:10:50 +08:00
parent 987a246c80
commit 77be3e9c71
17 changed files with 481 additions and 370 deletions

View File

@ -3,16 +3,19 @@ use crate::{
doc::editor::ServerDocUser,
util::{md5, parse_from_bytes},
},
web_socket::{entities::Socket, WsClientData, WsUser},
web_socket::WsClientData,
};
use actix_rt::task::spawn_blocking;
use actix_web::web::Data;
use async_stream::stream;
use backend_service::errors::{internal_error, Result, ServerError};
use flowy_collaboration::{
core::sync::ServerDocManager,
core::sync::{RevisionUser, ServerDocManager, SyncResponse},
entities::ws::DocumentWSDataBuilder,
protobuf::{DocumentWSData, DocumentWSDataType},
};
use flowy_collaboration::protobuf::NewDocumentUser;
use futures::stream::StreamExt;
use lib_ot::protobuf::Revision;
use sqlx::PgPool;
@ -66,7 +69,7 @@ impl DocWsActor {
}
}
async fn handle_client_data(&self, client_data: WsClientData, pool: Data<PgPool>) -> Result<()> {
async fn handle_client_data(&self, client_data: WsClientData, pg_pool: Data<PgPool>) -> Result<()> {
let WsClientData { user, socket, data } = client_data;
let document_data = spawn_blocking(move || {
let document_data: DocumentWSData = parse_from_bytes(&data)?;
@ -75,23 +78,29 @@ impl DocWsActor {
.await
.map_err(internal_error)??;
let data = document_data.data;
match document_data.ty {
DocumentWSDataType::Acked => Ok(()),
DocumentWSDataType::PushRev => self.apply_pushed_rev(user, socket, data, pool).await,
let user = Arc::new(ServerDocUser { user, socket, pg_pool });
match &document_data.ty {
DocumentWSDataType::Ack => Ok(()),
DocumentWSDataType::PushRev => self.handle_pushed_rev(user, document_data.data).await,
DocumentWSDataType::PullRev => Ok(()),
DocumentWSDataType::UserConnect => Ok(()),
DocumentWSDataType::UserConnect => self.handle_user_connect(user, document_data).await,
}
}
async fn apply_pushed_rev(
&self,
user: Arc<WsUser>,
socket: Socket,
data: Vec<u8>,
pg_pool: Data<PgPool>,
) -> Result<()> {
async fn handle_user_connect(&self, user: Arc<ServerDocUser>, document_data: DocumentWSData) -> Result<()> {
let id = document_data.id.clone();
let new_user = spawn_blocking(move || parse_from_bytes::<NewDocumentUser>(&document_data.data))
.await
.map_err(internal_error)??;
user.recv(SyncResponse::Ack(DocumentWSDataBuilder::build_ack_message(
&new_user.doc_id,
&id,
)));
Ok(())
}
async fn handle_pushed_rev(&self, user: Arc<ServerDocUser>, data: Vec<u8>) -> Result<()> {
let mut revision_pb = spawn_blocking(move || {
let revision: Revision = parse_from_bytes(&data)?;
let _ = verify_md5(&revision)?;
@ -110,7 +119,6 @@ impl DocWsActor {
Some(handler) => handler,
};
let user = Arc::new(ServerDocUser { user, socket, pg_pool });
handler.apply_revision(user, revision).await.map_err(internal_error)?;
Ok(())
}

View File

@ -14,22 +14,12 @@ import 'ws.pbenum.dart';
export 'ws.pbenum.dart';
enum DocumentWSData_OneOfId {
id,
notSet
}
class DocumentWSData extends $pb.GeneratedMessage {
static const $core.Map<$core.int, DocumentWSData_OneOfId> _DocumentWSData_OneOfIdByTag = {
4 : DocumentWSData_OneOfId.id,
0 : DocumentWSData_OneOfId.notSet
};
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DocumentWSData', createEmptyInstance: create)
..oo(0, [4])
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'docId')
..e<DocumentWSDataType>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ty', $pb.PbFieldType.OE, defaultOrMaker: DocumentWSDataType.Acked, valueOf: DocumentWSDataType.valueOf, enumValues: DocumentWSDataType.values)
..e<DocumentWSDataType>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ty', $pb.PbFieldType.OE, defaultOrMaker: DocumentWSDataType.Ack, valueOf: DocumentWSDataType.valueOf, enumValues: DocumentWSDataType.values)
..a<$core.List<$core.int>>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY)
..aInt64(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
..hasRequiredFields = false
;
@ -38,7 +28,7 @@ class DocumentWSData extends $pb.GeneratedMessage {
$core.String? docId,
DocumentWSDataType? ty,
$core.List<$core.int>? data,
$fixnum.Int64? id,
$core.String? id,
}) {
final _result = create();
if (docId != null) {
@ -76,9 +66,6 @@ class DocumentWSData extends $pb.GeneratedMessage {
static DocumentWSData getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DocumentWSData>(create);
static DocumentWSData? _defaultInstance;
DocumentWSData_OneOfId whichOneOfId() => _DocumentWSData_OneOfIdByTag[$_whichOneof(0)]!;
void clearOneOfId() => clearField($_whichOneof(0));
@$pb.TagNumber(1)
$core.String get docId => $_getSZ(0);
@$pb.TagNumber(1)
@ -107,25 +94,25 @@ class DocumentWSData extends $pb.GeneratedMessage {
void clearData() => clearField(3);
@$pb.TagNumber(4)
$fixnum.Int64 get id => $_getI64(3);
$core.String get id => $_getSZ(3);
@$pb.TagNumber(4)
set id($fixnum.Int64 v) { $_setInt64(3, v); }
set id($core.String v) { $_setString(3, v); }
@$pb.TagNumber(4)
$core.bool hasId() => $_has(3);
@$pb.TagNumber(4)
void clearId() => clearField(4);
}
class DocumentConnected extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DocumentConnected', createEmptyInstance: create)
class NewDocumentUser extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'NewDocumentUser', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'userId')
..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'docId')
..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'revId')
..hasRequiredFields = false
;
DocumentConnected._() : super();
factory DocumentConnected({
NewDocumentUser._() : super();
factory NewDocumentUser({
$core.String? userId,
$core.String? docId,
$fixnum.Int64? revId,
@ -142,26 +129,26 @@ class DocumentConnected extends $pb.GeneratedMessage {
}
return _result;
}
factory DocumentConnected.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory DocumentConnected.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
factory NewDocumentUser.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory NewDocumentUser.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
DocumentConnected clone() => DocumentConnected()..mergeFromMessage(this);
NewDocumentUser clone() => NewDocumentUser()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
DocumentConnected copyWith(void Function(DocumentConnected) updates) => super.copyWith((message) => updates(message as DocumentConnected)) as DocumentConnected; // ignore: deprecated_member_use
NewDocumentUser copyWith(void Function(NewDocumentUser) updates) => super.copyWith((message) => updates(message as NewDocumentUser)) as NewDocumentUser; // ignore: deprecated_member_use
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static DocumentConnected create() => DocumentConnected._();
DocumentConnected createEmptyInstance() => create();
static $pb.PbList<DocumentConnected> createRepeated() => $pb.PbList<DocumentConnected>();
static NewDocumentUser create() => NewDocumentUser._();
NewDocumentUser createEmptyInstance() => create();
static $pb.PbList<NewDocumentUser> createRepeated() => $pb.PbList<NewDocumentUser>();
@$core.pragma('dart2js:noInline')
static DocumentConnected getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DocumentConnected>(create);
static DocumentConnected? _defaultInstance;
static NewDocumentUser getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<NewDocumentUser>(create);
static NewDocumentUser? _defaultInstance;
@$pb.TagNumber(1)
$core.String get userId => $_getSZ(0);

View File

@ -10,13 +10,13 @@ import 'dart:core' as $core;
import 'package:protobuf/protobuf.dart' as $pb;
class DocumentWSDataType extends $pb.ProtobufEnum {
static const DocumentWSDataType Acked = DocumentWSDataType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Acked');
static const DocumentWSDataType Ack = DocumentWSDataType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Ack');
static const DocumentWSDataType PushRev = DocumentWSDataType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PushRev');
static const DocumentWSDataType PullRev = DocumentWSDataType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PullRev');
static const DocumentWSDataType UserConnect = DocumentWSDataType._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserConnect');
static const $core.List<DocumentWSDataType> values = <DocumentWSDataType> [
Acked,
Ack,
PushRev,
PullRev,
UserConnect,

View File

@ -12,7 +12,7 @@ import 'dart:typed_data' as $typed_data;
const DocumentWSDataType$json = const {
'1': 'DocumentWSDataType',
'2': const [
const {'1': 'Acked', '2': 0},
const {'1': 'Ack', '2': 0},
const {'1': 'PushRev', '2': 1},
const {'1': 'PullRev', '2': 2},
const {'1': 'UserConnect', '2': 3},
@ -20,7 +20,7 @@ const DocumentWSDataType$json = const {
};
/// Descriptor for `DocumentWSDataType`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List documentWSDataTypeDescriptor = $convert.base64Decode('ChJEb2N1bWVudFdTRGF0YVR5cGUSCQoFQWNrZWQQABILCgdQdXNoUmV2EAESCwoHUHVsbFJldhACEg8KC1VzZXJDb25uZWN0EAM=');
final $typed_data.Uint8List documentWSDataTypeDescriptor = $convert.base64Decode('ChJEb2N1bWVudFdTRGF0YVR5cGUSBwoDQWNrEAASCwoHUHVzaFJldhABEgsKB1B1bGxSZXYQAhIPCgtVc2VyQ29ubmVjdBAD');
@$core.Deprecated('Use documentWSDataDescriptor instead')
const DocumentWSData$json = const {
'1': 'DocumentWSData',
@ -28,18 +28,15 @@ const DocumentWSData$json = const {
const {'1': 'doc_id', '3': 1, '4': 1, '5': 9, '10': 'docId'},
const {'1': 'ty', '3': 2, '4': 1, '5': 14, '6': '.DocumentWSDataType', '10': 'ty'},
const {'1': 'data', '3': 3, '4': 1, '5': 12, '10': 'data'},
const {'1': 'id', '3': 4, '4': 1, '5': 3, '9': 0, '10': 'id'},
],
'8': const [
const {'1': 'one_of_id'},
const {'1': 'id', '3': 4, '4': 1, '5': 9, '10': 'id'},
],
};
/// Descriptor for `DocumentWSData`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List documentWSDataDescriptor = $convert.base64Decode('Cg5Eb2N1bWVudFdTRGF0YRIVCgZkb2NfaWQYASABKAlSBWRvY0lkEiMKAnR5GAIgASgOMhMuRG9jdW1lbnRXU0RhdGFUeXBlUgJ0eRISCgRkYXRhGAMgASgMUgRkYXRhEhAKAmlkGAQgASgDSABSAmlkQgsKCW9uZV9vZl9pZA==');
@$core.Deprecated('Use documentConnectedDescriptor instead')
const DocumentConnected$json = const {
'1': 'DocumentConnected',
final $typed_data.Uint8List documentWSDataDescriptor = $convert.base64Decode('Cg5Eb2N1bWVudFdTRGF0YRIVCgZkb2NfaWQYASABKAlSBWRvY0lkEiMKAnR5GAIgASgOMhMuRG9jdW1lbnRXU0RhdGFUeXBlUgJ0eRISCgRkYXRhGAMgASgMUgRkYXRhEg4KAmlkGAQgASgJUgJpZA==');
@$core.Deprecated('Use newDocumentUserDescriptor instead')
const NewDocumentUser$json = const {
'1': 'NewDocumentUser',
'2': const [
const {'1': 'user_id', '3': 1, '4': 1, '5': 9, '10': 'userId'},
const {'1': 'doc_id', '3': 2, '4': 1, '5': 9, '10': 'docId'},
@ -47,5 +44,5 @@ const DocumentConnected$json = const {
],
};
/// Descriptor for `DocumentConnected`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List documentConnectedDescriptor = $convert.base64Decode('ChFEb2N1bWVudENvbm5lY3RlZBIXCgd1c2VyX2lkGAEgASgJUgZ1c2VySWQSFQoGZG9jX2lkGAIgASgJUgVkb2NJZBIVCgZyZXZfaWQYAyABKANSBXJldklk');
/// Descriptor for `NewDocumentUser`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List newDocumentUserDescriptor = $convert.base64Decode('Cg9OZXdEb2N1bWVudFVzZXISFwoHdXNlcl9pZBgBIAEoCVIGdXNlcklkEhUKBmRvY19pZBgCIAEoCVIFZG9jSWQSFQoGcmV2X2lkGAMgASgDUgVyZXZJZA==');

View File

@ -4,7 +4,7 @@ use flowy_collaboration::{
core::document::history::UndoResult,
entities::{
doc::DocDelta,
ws::{DocumentConnected, DocumentWSData, DocumentWSDataType, WsDocumentDataBuilder},
ws::{DocumentWSData, DocumentWSDataBuilder, DocumentWSDataType, NewDocumentUser},
},
errors::CollaborateResult,
};
@ -42,27 +42,32 @@ impl ClientDocEditor {
let doc_id = doc_id.to_string();
let user_id = user.user_id()?;
let rev_manager = Arc::new(rev_manager);
let sink_data_provider = Arc::new(RwLock::new(VecDeque::new()));
let data_provider = Arc::new(DocumentSinkDataProviderAdapter {
rev_manager: rev_manager.clone(),
data_provider: sink_data_provider.clone(),
});
let stream_consumer = Arc::new(DocumentWebSocketSteamConsumerAdapter {
let combined_sink = Arc::new(CombinedSink::new(rev_manager.clone()));
let ws_stream_consumer = Arc::new(DocumentWebSocketSteamConsumerAdapter {
doc_id: doc_id.clone(),
editor_cmd_sender: editor_cmd_sender.clone(),
rev_manager: rev_manager.clone(),
user: user.clone(),
sink_data_provider: sink_data_provider.clone(),
combined_sink: combined_sink.clone(),
});
let editor_ws = Arc::new(EditorWebSocket::new(&doc_id, ws, data_provider, stream_consumer));
notify_user_conn(&user_id, &doc_id, rev_manager.clone(), sink_data_provider.clone()).await;
let ws_stream_provider = Arc::new(DocumentWSSinkDataProviderAdapter(combined_sink.clone()));
let editor_ws = Arc::new(EditorWebSocket::new(
&doc_id,
ws,
ws_stream_provider,
ws_stream_consumer,
));
//
notify_user_conn(&user_id, &doc_id, rev_manager.clone(), combined_sink.clone()).await;
//
listen_document_ws_state(
&user_id,
&doc_id,
editor_ws.scribe_state(),
rev_manager.clone(),
sink_data_provider,
combined_sink,
);
let editor = Arc::new(Self {
@ -210,78 +215,12 @@ fn spawn_edit_queue(doc_id: &str, delta: RichTextDelta, _pool: Arc<ConnectionPoo
sender
}
struct DocumentWebSocketSteamConsumerAdapter {
doc_id: String,
editor_cmd_sender: UnboundedSender<EditorCommand>,
rev_manager: Arc<RevisionManager>,
user: Arc<dyn DocumentUser>,
sink_data_provider: SinkDataProvider,
}
impl DocumentWebSocketSteamConsumer for DocumentWebSocketSteamConsumerAdapter {
fn receive_push_revision(&self, bytes: Bytes) -> FutureResult<(), FlowyError> {
let user = self.user.clone();
let rev_manager = self.rev_manager.clone();
let edit_cmd_tx = self.editor_cmd_sender.clone();
let sink_data_provider = self.sink_data_provider.clone();
let doc_id = self.doc_id.clone();
FutureResult::new(async move {
let user_id = user.user_id()?;
if let Some(revision) = handle_push_rev(&doc_id, &user_id, edit_cmd_tx, rev_manager, bytes).await? {
sink_data_provider.write().await.push_back(revision.into());
}
Ok(())
})
}
fn receive_ack_revision(&self, rev_id: i64) -> FutureResult<(), FlowyError> {
let rev_manager = self.rev_manager.clone();
FutureResult::new(async move {
let _ = rev_manager.ack_revision(rev_id).await?;
Ok(())
})
}
fn send_revision_in_range(&self, range: RevisionRange) -> FutureResult<(), FlowyError> {
let rev_manager = self.rev_manager.clone();
let sink_data_provider = self.sink_data_provider.clone();
FutureResult::new(async move {
let revision = rev_manager.mk_revisions(range).await?;
sink_data_provider.write().await.push_back(revision.into());
Ok(())
})
}
}
async fn notify_user_conn(
user_id: &str,
doc_id: &str,
rev_manager: Arc<RevisionManager>,
sink_data_provider: SinkDataProvider,
) {
let need_notify = match sink_data_provider.read().await.front() {
None => true,
Some(data) => data.ty != DocumentWSDataType::UserConnect,
};
if need_notify {
let document_conn = DocumentConnected {
user_id: user_id.to_owned(),
doc_id: doc_id.to_owned(),
rev_id: rev_manager.latest_rev_id(),
};
let data = WsDocumentDataBuilder::build_document_conn_message(doc_id, document_conn);
sink_data_provider.write().await.push_front(data);
}
}
fn listen_document_ws_state(
user_id: &str,
doc_id: &str,
mut subscriber: broadcast::Receiver<WSConnectState>,
rev_manager: Arc<RevisionManager>,
sink_data_provider: SinkDataProvider,
sink_data_provider: Arc<CombinedSink>,
) {
let user_id = user_id.to_owned();
let doc_id = doc_id.to_owned();
@ -301,38 +240,79 @@ fn listen_document_ws_state(
});
}
type SinkDataProvider = Arc<RwLock<VecDeque<DocumentWSData>>>;
struct DocumentSinkDataProviderAdapter {
async fn notify_user_conn(
user_id: &str,
doc_id: &str,
rev_manager: Arc<RevisionManager>,
data_provider: SinkDataProvider,
combined_sink: Arc<CombinedSink>,
) {
let need_notify = match combined_sink.front().await {
None => true,
Some(data) => data.ty != DocumentWSDataType::UserConnect,
};
if need_notify {
let new_connect = NewDocumentUser {
user_id: user_id.to_owned(),
doc_id: doc_id.to_owned(),
rev_id: rev_manager.latest_rev_id(),
};
let data = DocumentWSDataBuilder::build_new_document_user_message(doc_id, new_connect);
combined_sink.push_front(data).await;
}
}
impl DocumentSinkDataProvider for DocumentSinkDataProviderAdapter {
fn next(&self) -> FutureResult<Option<DocumentWSData>, FlowyError> {
let rev_manager = self.rev_manager.clone();
let data_provider = self.data_provider.clone();
struct DocumentWebSocketSteamConsumerAdapter {
doc_id: String,
editor_cmd_sender: UnboundedSender<EditorCommand>,
rev_manager: Arc<RevisionManager>,
user: Arc<dyn DocumentUser>,
combined_sink: Arc<CombinedSink>,
}
impl DocumentWSSteamConsumer for DocumentWebSocketSteamConsumerAdapter {
fn receive_push_revision(&self, bytes: Bytes) -> FutureResult<(), FlowyError> {
let user = self.user.clone();
let rev_manager = self.rev_manager.clone();
let edit_cmd_tx = self.editor_cmd_sender.clone();
let combined_sink = self.combined_sink.clone();
let doc_id = self.doc_id.clone();
FutureResult::new(async move {
if data_provider.read().await.is_empty() {
match rev_manager.next_sync_revision().await? {
Some(rev) => {
tracing::debug!("[DocumentSinkDataProvider]: {}:{:?}", rev.doc_id, rev.rev_id);
Ok(Some(rev.into()))
},
None => Ok(None),
}
} else {
match data_provider.read().await.front() {
None => Ok(None),
Some(data) => {
tracing::debug!("[DocumentSinkDataProvider]: {}:{:?}", data.doc_id, data.ty);
Ok(Some(data.clone()))
},
}
let user_id = user.user_id()?;
if let Some(revision) = handle_push_rev(&doc_id, &user_id, edit_cmd_tx, rev_manager, bytes).await? {
combined_sink.push_back(revision.into()).await;
}
Ok(())
})
}
fn receive_ack(&self, id: String, ty: DocumentWSDataType) -> FutureResult<(), FlowyError> {
let combined_sink = self.combined_sink.clone();
FutureResult::new(async move { combined_sink.ack(id, ty).await })
}
fn receive_new_user_connect(&self, _new_user: NewDocumentUser) -> FutureResult<(), FlowyError> {
FutureResult::new(async move { Ok(()) })
}
fn send_revision_in_range(&self, range: RevisionRange) -> FutureResult<(), FlowyError> {
let rev_manager = self.rev_manager.clone();
let combined_sink = self.combined_sink.clone();
FutureResult::new(async move {
let revision = rev_manager.mk_revisions(range).await?;
combined_sink.push_back(revision.into()).await;
Ok(())
})
}
}
struct DocumentWSSinkDataProviderAdapter(Arc<CombinedSink>);
impl DocumentWSSinkDataProvider for DocumentWSSinkDataProviderAdapter {
fn next(&self) -> FutureResult<Option<DocumentWSData>, FlowyError> {
let combined_sink = self.0.clone();
FutureResult::new(async move { combined_sink.next().await })
}
}
#[tracing::instrument(level = "debug", skip(edit_cmd_tx, rev_manager, bytes))]
@ -396,6 +376,101 @@ pub(crate) async fn handle_push_rev(
)))
}
#[derive(Clone)]
enum SourceType {
Shared,
Revision,
}
#[derive(Clone)]
struct CombinedSink {
shared: Arc<RwLock<VecDeque<DocumentWSData>>>,
rev_manager: Arc<RevisionManager>,
source_ty: Arc<RwLock<SourceType>>,
}
impl CombinedSink {
fn new(rev_manager: Arc<RevisionManager>) -> Self {
CombinedSink {
shared: Arc::new(RwLock::new(VecDeque::new())),
rev_manager,
source_ty: Arc::new(RwLock::new(SourceType::Shared)),
}
}
// FIXME: return Option<&DocumentWSData> would be better
async fn front(&self) -> Option<DocumentWSData> { self.shared.read().await.front().cloned() }
async fn push_front(&self, data: DocumentWSData) { self.shared.write().await.push_front(data); }
async fn push_back(&self, data: DocumentWSData) { self.shared.write().await.push_back(data); }
async fn next(&self) -> FlowyResult<Option<DocumentWSData>> {
let source_ty = self.source_ty.read().await.clone();
match source_ty {
SourceType::Shared => match self.shared.read().await.front() {
None => {
*self.source_ty.write().await = SourceType::Revision;
Ok(None)
},
Some(data) => {
tracing::debug!("[DocumentSinkDataProvider]: {}:{:?}", data.doc_id, data.ty);
Ok(Some(data.clone()))
},
},
SourceType::Revision => {
if !self.shared.read().await.is_empty() {
*self.source_ty.write().await = SourceType::Shared;
return Ok(None);
}
match self.rev_manager.next_sync_revision().await? {
Some(rev) => {
tracing::debug!("[DocumentSinkDataProvider]: {}:{:?}", rev.doc_id, rev.rev_id);
Ok(Some(rev.into()))
},
None => Ok(None),
}
},
}
}
async fn ack(&self, id: String, _ty: DocumentWSDataType) -> FlowyResult<()> {
// let _ = self.rev_manager.ack_revision(id).await?;
let source_ty = self.source_ty.read().await.clone();
match source_ty {
SourceType::Shared => {
let should_pop = match self.shared.read().await.front() {
None => false,
Some(val) => {
if val.id == id {
true
} else {
tracing::error!("The front element's {} is not equal to the {}", val.id, id);
false
}
},
};
if should_pop {
let _ = self.shared.write().await.pop_front();
}
},
SourceType::Revision => {
match id.parse::<i64>() {
Ok(rev_id) => {
let _ = self.rev_manager.ack_revision(rev_id).await?;
},
Err(e) => {
tracing::error!("Parse rev_id from {} failed. {}", id, e);
},
};
},
}
Ok(())
}
}
#[cfg(feature = "flowy_unit_test")]
impl ClientDocEditor {
pub async fn doc_json(&self) -> FlowyResult<String> {

View File

@ -1,11 +1,11 @@
use crate::services::doc::{DocumentWebSocket, DocumentWsHandler, SYNC_INTERVAL_IN_MILLIS};
use async_stream::stream;
use bytes::Bytes;
use flowy_collaboration::entities::ws::{DocumentWSData, DocumentWSDataType};
use flowy_collaboration::entities::ws::{DocumentWSData, DocumentWSDataType, NewDocumentUser};
use flowy_error::{internal_error, FlowyError, FlowyResult};
use futures::stream::StreamExt;
use lib_infra::future::FutureResult;
use lib_ot::revision::{RevId, RevisionRange};
use lib_ot::revision::RevisionRange;
use lib_ws::WSConnectState;
use std::{convert::TryFrom, sync::Arc};
use tokio::{
@ -20,8 +20,8 @@ use tokio::{
pub(crate) struct EditorWebSocket {
doc_id: String,
data_provider: Arc<dyn DocumentSinkDataProvider>,
stream_consumer: Arc<dyn DocumentWebSocketSteamConsumer>,
data_provider: Arc<dyn DocumentWSSinkDataProvider>,
stream_consumer: Arc<dyn DocumentWSSteamConsumer>,
ws: Arc<dyn DocumentWebSocket>,
ws_msg_tx: UnboundedSender<DocumentWSData>,
ws_msg_rx: Option<UnboundedReceiver<DocumentWSData>>,
@ -33,8 +33,8 @@ impl EditorWebSocket {
pub(crate) fn new(
doc_id: &str,
ws: Arc<dyn DocumentWebSocket>,
data_provider: Arc<dyn DocumentSinkDataProvider>,
stream_consumer: Arc<dyn DocumentWebSocketSteamConsumer>,
data_provider: Arc<dyn DocumentWSSinkDataProvider>,
stream_consumer: Arc<dyn DocumentWSSteamConsumer>,
) -> Self {
let (ws_msg_tx, ws_msg_rx) = mpsc::unbounded_channel();
let (stop_sync_tx, _) = tokio::sync::broadcast::channel(2);
@ -97,15 +97,16 @@ impl DocumentWsHandler for EditorWebSocket {
}
}
pub trait DocumentWebSocketSteamConsumer: Send + Sync {
pub trait DocumentWSSteamConsumer: Send + Sync {
fn receive_push_revision(&self, bytes: Bytes) -> FutureResult<(), FlowyError>;
fn receive_ack_revision(&self, rev_id: i64) -> FutureResult<(), FlowyError>;
fn receive_ack(&self, id: String, ty: DocumentWSDataType) -> FutureResult<(), FlowyError>;
fn receive_new_user_connect(&self, new_user: NewDocumentUser) -> FutureResult<(), FlowyError>;
fn send_revision_in_range(&self, range: RevisionRange) -> FutureResult<(), FlowyError>;
}
pub(crate) struct DocumentWebSocketStream {
doc_id: String,
consumer: Arc<dyn DocumentWebSocketSteamConsumer>,
consumer: Arc<dyn DocumentWSSteamConsumer>,
ws_msg_rx: Option<mpsc::UnboundedReceiver<DocumentWSData>>,
stop_rx: Option<SinkStopRx>,
}
@ -113,7 +114,7 @@ pub(crate) struct DocumentWebSocketStream {
impl DocumentWebSocketStream {
pub(crate) fn new(
doc_id: &str,
consumer: Arc<dyn DocumentWebSocketSteamConsumer>,
consumer: Arc<dyn DocumentWSSteamConsumer>,
ws_msg_rx: mpsc::UnboundedReceiver<DocumentWSData>,
stop_rx: SinkStopRx,
) -> Self {
@ -166,7 +167,7 @@ impl DocumentWebSocketStream {
doc_id: _,
ty,
data,
id: _,
id,
} = msg;
let bytes = spawn_blocking(move || Bytes::from(data))
.await
@ -181,11 +182,13 @@ impl DocumentWebSocketStream {
let range = RevisionRange::try_from(bytes)?;
let _ = self.consumer.send_revision_in_range(range).await?;
},
DocumentWSDataType::Acked => {
let rev_id = RevId::try_from(bytes)?;
let _ = self.consumer.receive_ack_revision(rev_id.into()).await;
DocumentWSDataType::Ack => {
// let rev_id = RevId::try_from(bytes)?;
let _ = self.consumer.receive_ack(id, ty).await;
},
DocumentWSDataType::UserConnect => {
let new_user = NewDocumentUser::try_from(bytes)?;
let _ = self.consumer.receive_new_user_connect(new_user).await;
// Notify the user that someone has connected to this document
},
}
@ -198,12 +201,12 @@ pub(crate) type Tick = ();
pub(crate) type SinkStopRx = broadcast::Receiver<()>;
pub(crate) type SinkStopTx = broadcast::Sender<()>;
pub trait DocumentSinkDataProvider: Send + Sync {
pub trait DocumentWSSinkDataProvider: Send + Sync {
fn next(&self) -> FutureResult<Option<DocumentWSData>, FlowyError>;
}
pub(crate) struct DocumentWebSocketSink {
provider: Arc<dyn DocumentSinkDataProvider>,
provider: Arc<dyn DocumentWSSinkDataProvider>,
ws_sender: Arc<dyn DocumentWebSocket>,
stop_rx: Option<SinkStopRx>,
doc_id: String,
@ -212,7 +215,7 @@ pub(crate) struct DocumentWebSocketSink {
impl DocumentWebSocketSink {
pub(crate) fn new(
doc_id: &str,
provider: Arc<dyn DocumentSinkDataProvider>,
provider: Arc<dyn DocumentWSSinkDataProvider>,
ws_sender: Arc<dyn DocumentWebSocket>,
stop_rx: SinkStopRx,
) -> Self {

View File

@ -1,24 +1,27 @@
use crate::{
errors::FlowyError,
services::doc::revision::{
cache::{
disk::{Persistence, RevisionDiskCache},
memory::{RevisionMemoryCache, RevisionMemoryCacheDelegate},
sync::RevisionSyncSeq,
},
RevisionRecord,
services::doc::revision::cache::{
disk::{Persistence, RevisionDiskCache},
memory::{RevisionMemoryCache, RevisionMemoryCacheDelegate},
},
sql_tables::{RevChangeset, RevTableState},
};
use dashmap::DashMap;
use flowy_database::ConnectionPool;
use flowy_error::{internal_error, FlowyResult};
use lib_infra::future::FutureResult;
use lib_ot::revision::{RevState, Revision, RevisionRange};
use std::sync::{
atomic::{AtomicI64, Ordering::SeqCst},
Arc,
use lib_ot::{
errors::OTError,
revision::{RevState, Revision, RevisionRange},
};
use tokio::task::spawn_blocking;
use std::{
collections::VecDeque,
sync::{
atomic::{AtomicI64, Ordering::SeqCst},
Arc,
},
};
use tokio::{sync::RwLock, task::spawn_blocking};
type DocRevisionDiskCache = dyn RevisionDiskCache<Error = FlowyError>;
@ -157,9 +160,79 @@ impl RevisionMemoryCacheDelegate for Arc<Persistence> {
}
}
#[cfg(feature = "flowy_unit_test")]
impl RevisionCache {
pub fn disk_cache(&self) -> Arc<DocRevisionDiskCache> { self.disk_cache.clone() }
pub fn memory_cache(&self) -> Arc<RevisionSyncSeq> { self.sync_seq.clone() }
#[derive(Clone)]
pub struct RevisionRecord {
pub revision: Revision,
pub state: RevState,
}
impl RevisionRecord {
pub fn ack(&mut self) { self.state = RevState::Acked; }
}
struct RevisionSyncSeq {
revs_map: Arc<DashMap<i64, RevisionRecord>>,
local_revs: Arc<RwLock<VecDeque<i64>>>,
}
impl std::default::Default for RevisionSyncSeq {
fn default() -> Self {
let local_revs = Arc::new(RwLock::new(VecDeque::new()));
RevisionSyncSeq {
revs_map: Arc::new(DashMap::new()),
local_revs,
}
}
}
impl RevisionSyncSeq {
fn new() -> Self { RevisionSyncSeq::default() }
async fn add_revision(&self, record: RevisionRecord) -> Result<(), OTError> {
// The last revision's rev_id must be greater than the new one.
if let Some(rev_id) = self.local_revs.read().await.back() {
if *rev_id >= record.revision.rev_id {
return Err(OTError::revision_id_conflict()
.context(format!("The new revision's id must be greater than {}", rev_id)));
}
}
self.local_revs.write().await.push_back(record.revision.rev_id);
self.revs_map.insert(record.revision.rev_id, record);
Ok(())
}
async fn ack_revision(&self, rev_id: &i64) -> FlowyResult<()> {
if let Some(pop_rev_id) = self.next_sync_rev_id().await {
if &pop_rev_id != rev_id {
let desc = format!(
"The ack rev_id:{} is not equal to the current rev_id:{}",
rev_id, pop_rev_id
);
// tracing::error!("{}", desc);
return Err(FlowyError::internal().context(desc));
}
tracing::debug!("pop revision {}", pop_rev_id);
self.revs_map.remove(&pop_rev_id);
let _ = self.local_revs.write().await.pop_front();
}
Ok(())
}
async fn next_sync_revision(&self) -> Option<(i64, RevisionRecord)> {
match self.local_revs.read().await.front() {
None => None,
Some(rev_id) => self.revs_map.get(rev_id).map(|r| (*r.key(), r.value().clone())),
}
}
async fn next_sync_rev_id(&self) -> Option<i64> { self.local_revs.read().await.front().copied() }
}
#[cfg(feature = "flowy_unit_test")]
impl RevisionSyncSeq {
#[allow(dead_code)]
pub fn revs_map(&self) -> Arc<DashMap<i64, RevisionRecord>> { self.revs_map.clone() }
#[allow(dead_code)]
pub fn pending_revs(&self) -> Arc<RwLock<VecDeque<i64>>> { self.local_revs.clone() }
}

View File

@ -2,8 +2,5 @@
mod cache;
mod disk;
mod memory;
mod model;
mod sync;
pub use cache::*;
pub use model::*;

View File

@ -1,15 +0,0 @@
use lib_ot::revision::{RevState, Revision};
use tokio::sync::broadcast;
pub type RevIdReceiver = broadcast::Receiver<i64>;
pub type RevIdSender = broadcast::Sender<i64>;
#[derive(Clone)]
pub struct RevisionRecord {
pub revision: Revision,
pub state: RevState,
}
impl RevisionRecord {
pub fn ack(&mut self) { self.state = RevState::Acked; }
}

View File

@ -5,7 +5,7 @@ use flowy_collaboration::{
core::sync::{RevisionUser, ServerDocManager, ServerDocPersistence, SyncResponse},
entities::{
doc::Doc,
ws::{DocumentWSData, DocumentWSDataType},
ws::{DocumentWSData, DocumentWSDataBuilder, DocumentWSDataType, NewDocumentUser},
},
errors::CollaborateError,
Revision,
@ -111,7 +111,7 @@ impl MockDocServer {
async fn handle_ws_data(&self, ws_data: DocumentWSData) -> mpsc::Receiver<WSMessage> {
let bytes = Bytes::from(ws_data.data);
match ws_data.ty {
DocumentWSDataType::Acked => {
DocumentWSDataType::Ack => {
unimplemented!()
},
DocumentWSDataType::PushRev => {
@ -133,7 +133,16 @@ impl MockDocServer {
unimplemented!()
},
DocumentWSDataType::UserConnect => {
unimplemented!()
let new_user = NewDocumentUser::try_from(bytes).unwrap();
let (tx, rx) = mpsc::channel(1);
let data = DocumentWSDataBuilder::build_ack_message(&new_user.doc_id, &ws_data.id);
let user = Arc::new(MockDocUser {
user_id: new_user.user_id,
tx,
}) as Arc<dyn RevisionUser>;
user.recv(SyncResponse::Ack(data));
rx
},
}
}

View File

@ -46,8 +46,6 @@ impl EditorTest {
async fn run_script(&mut self, script: EditorScript) {
let rev_manager = self.editor.rev_manager();
let cache = rev_manager.revision_cache();
let _memory_cache = cache.memory_cache();
let _disk_cache = cache.disk_cache();
let doc_id = self.editor.doc_id.clone();
let _user_id = self.sdk.user_session.user_id().unwrap();
let ws_manager = self.sdk.ws_manager.clone();

View File

@ -1,6 +1,6 @@
use crate::{
core::document::Document,
entities::ws::{DocumentWSData, WsDocumentDataBuilder},
entities::ws::{DocumentWSData, DocumentWSDataBuilder},
};
use lib_ot::{
core::OperationTransformable,
@ -59,9 +59,9 @@ impl RevisionSynchronizer {
if server_base_rev_id == revision.base_rev_id || server_rev_id == revision.rev_id {
// The rev is in the right order, just compose it.
let _ = self.compose_revision(&revision)?;
user.recv(SyncResponse::Ack(WsDocumentDataBuilder::build_acked_message(
user.recv(SyncResponse::Ack(DocumentWSDataBuilder::build_ack_message(
&revision.doc_id,
revision.rev_id,
&revision.rev_id.to_string(),
)));
let rev_id = revision.rev_id;
let doc_id = self.doc_id.clone();
@ -78,14 +78,14 @@ impl RevisionSynchronizer {
start: server_rev_id,
end: revision.rev_id,
};
let msg = WsDocumentDataBuilder::build_push_pull_message(&self.doc_id, range);
let msg = DocumentWSDataBuilder::build_push_pull_message(&self.doc_id, range);
user.recv(SyncResponse::Pull(msg));
}
},
Ordering::Equal => {
// Do nothing
log::warn!("Applied revision rev_id is the same as cur_rev_id");
let data = WsDocumentDataBuilder::build_acked_message(&revision.doc_id, revision.rev_id);
let data = DocumentWSDataBuilder::build_ack_message(&revision.doc_id, &revision.rev_id.to_string());
user.recv(SyncResponse::Ack(data));
},
Ordering::Greater => {
@ -93,7 +93,7 @@ impl RevisionSynchronizer {
// send the prime delta to the client. Client should compose the this prime
// delta.
let cli_revision = self.transform_revision(&revision)?;
let data = WsDocumentDataBuilder::build_push_rev_message(&self.doc_id, cli_revision);
let data = DocumentWSDataBuilder::build_push_rev_message(&self.doc_id, cli_revision);
user.recv(SyncResponse::Push(data));
},
}

View File

@ -1,13 +1,14 @@
use crate::errors::CollaborateError;
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use lib_ot::revision::{RevId, Revision, RevisionRange};
use lib_infra::uuid;
use lib_ot::revision::{Revision, RevisionRange};
use std::convert::{TryFrom, TryInto};
#[derive(Debug, Clone, ProtoBuf_Enum, Eq, PartialEq, Hash)]
pub enum DocumentWSDataType {
// The frontend receives the Acked means the backend has accepted the revision
Acked = 0,
Ack = 0,
// The frontend receives the PushRev event means the backend is pushing the new revision to frontend
PushRev = 1,
// The fronted receives the PullRev event means the backend try to pull the revision from frontend
@ -25,7 +26,7 @@ impl DocumentWSDataType {
}
impl std::default::Default for DocumentWSDataType {
fn default() -> Self { DocumentWSDataType::Acked }
fn default() -> Self { DocumentWSDataType::Ack }
}
#[derive(ProtoBuf, Default, Debug, Clone)]
@ -39,8 +40,8 @@ pub struct DocumentWSData {
#[pb(index = 3)]
pub data: Vec<u8>,
#[pb(index = 4, one_of)]
pub id: Option<i64>,
#[pb(index = 4)]
pub id: String,
}
impl std::convert::From<Revision> for DocumentWSData {
@ -52,13 +53,13 @@ impl std::convert::From<Revision> for DocumentWSData {
doc_id,
ty: DocumentWSDataType::PushRev,
data: bytes.to_vec(),
id: Some(rev_id),
id: rev_id.to_string(),
}
}
}
pub struct WsDocumentDataBuilder();
impl WsDocumentDataBuilder {
pub struct DocumentWSDataBuilder();
impl DocumentWSDataBuilder {
// DocumentWSDataType::PushRev -> Revision
pub fn build_push_rev_message(doc_id: &str, revision: Revision) -> DocumentWSData {
let rev_id = revision.rev_id;
@ -67,7 +68,7 @@ impl WsDocumentDataBuilder {
doc_id: doc_id.to_string(),
ty: DocumentWSDataType::PushRev,
data: bytes.to_vec(),
id: Some(rev_id),
id: rev_id.to_string(),
}
}
@ -78,39 +79,35 @@ impl WsDocumentDataBuilder {
doc_id: doc_id.to_string(),
ty: DocumentWSDataType::PullRev,
data: bytes.to_vec(),
id: None,
id: uuid(),
}
}
// DocumentWSDataType::Acked -> RevId
pub fn build_acked_message(doc_id: &str, rev_id: i64) -> DocumentWSData {
let cloned_rev_id = rev_id;
let rev_id: RevId = rev_id.into();
let bytes: Bytes = rev_id.try_into().unwrap();
// DocumentWSDataType::Ack -> RevId
pub fn build_ack_message(doc_id: &str, id: &str) -> DocumentWSData {
DocumentWSData {
doc_id: doc_id.to_string(),
ty: DocumentWSDataType::Acked,
data: bytes.to_vec(),
id: Some(cloned_rev_id),
ty: DocumentWSDataType::Ack,
data: vec![],
id: id.to_string(),
}
}
// DocumentWSDataType::UserConnect -> DocumentConnected
pub fn build_document_conn_message(doc_id: &str, document_conn: DocumentConnected) -> DocumentWSData {
let rev_id = document_conn.rev_id;
let bytes: Bytes = document_conn.try_into().unwrap();
pub fn build_new_document_user_message(doc_id: &str, new_document_user: NewDocumentUser) -> DocumentWSData {
let id = new_document_user.user_id.clone();
let bytes: Bytes = new_document_user.try_into().unwrap();
DocumentWSData {
doc_id: doc_id.to_string(),
ty: DocumentWSDataType::UserConnect,
data: bytes.to_vec(),
id: Some(rev_id),
id,
}
}
}
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct DocumentConnected {
pub struct NewDocumentUser {
#[pb(index = 1)]
pub user_id: String,

View File

@ -29,8 +29,7 @@ pub struct DocumentWSData {
pub doc_id: ::std::string::String,
pub ty: DocumentWSDataType,
pub data: ::std::vec::Vec<u8>,
// message oneof groups
pub one_of_id: ::std::option::Option<DocumentWSData_oneof_one_of_id>,
pub id: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
@ -42,11 +41,6 @@ impl<'a> ::std::default::Default for &'a DocumentWSData {
}
}
#[derive(Clone,PartialEq,Debug)]
pub enum DocumentWSData_oneof_one_of_id {
id(i64),
}
impl DocumentWSData {
pub fn new() -> DocumentWSData {
::std::default::Default::default()
@ -85,7 +79,7 @@ impl DocumentWSData {
self.ty
}
pub fn clear_ty(&mut self) {
self.ty = DocumentWSDataType::Acked;
self.ty = DocumentWSDataType::Ack;
}
// Param is passed by value, moved
@ -119,29 +113,30 @@ impl DocumentWSData {
::std::mem::replace(&mut self.data, ::std::vec::Vec::new())
}
// int64 id = 4;
// string id = 4;
pub fn get_id(&self) -> i64 {
match self.one_of_id {
::std::option::Option::Some(DocumentWSData_oneof_one_of_id::id(v)) => v,
_ => 0,
}
pub fn get_id(&self) -> &str {
&self.id
}
pub fn clear_id(&mut self) {
self.one_of_id = ::std::option::Option::None;
}
pub fn has_id(&self) -> bool {
match self.one_of_id {
::std::option::Option::Some(DocumentWSData_oneof_one_of_id::id(..)) => true,
_ => false,
}
self.id.clear();
}
// Param is passed by value, moved
pub fn set_id(&mut self, v: i64) {
self.one_of_id = ::std::option::Option::Some(DocumentWSData_oneof_one_of_id::id(v))
pub fn set_id(&mut self, v: ::std::string::String) {
self.id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_id(&mut self) -> &mut ::std::string::String {
&mut self.id
}
// Take field
pub fn take_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.id, ::std::string::String::new())
}
}
@ -164,10 +159,7 @@ impl ::protobuf::Message for DocumentWSData {
::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?;
},
4 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
self.one_of_id = ::std::option::Option::Some(DocumentWSData_oneof_one_of_id::id(is.read_int64()?));
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@ -184,18 +176,14 @@ impl ::protobuf::Message for DocumentWSData {
if !self.doc_id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.doc_id);
}
if self.ty != DocumentWSDataType::Acked {
if self.ty != DocumentWSDataType::Ack {
my_size += ::protobuf::rt::enum_size(2, self.ty);
}
if !self.data.is_empty() {
my_size += ::protobuf::rt::bytes_size(3, &self.data);
}
if let ::std::option::Option::Some(ref v) = self.one_of_id {
match v {
&DocumentWSData_oneof_one_of_id::id(v) => {
my_size += ::protobuf::rt::value_size(4, v, ::protobuf::wire_format::WireTypeVarint);
},
};
if !self.id.is_empty() {
my_size += ::protobuf::rt::string_size(4, &self.id);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
@ -206,18 +194,14 @@ impl ::protobuf::Message for DocumentWSData {
if !self.doc_id.is_empty() {
os.write_string(1, &self.doc_id)?;
}
if self.ty != DocumentWSDataType::Acked {
if self.ty != DocumentWSDataType::Ack {
os.write_enum(2, ::protobuf::ProtobufEnum::value(&self.ty))?;
}
if !self.data.is_empty() {
os.write_bytes(3, &self.data)?;
}
if let ::std::option::Option::Some(ref v) = self.one_of_id {
match v {
&DocumentWSData_oneof_one_of_id::id(v) => {
os.write_int64(4, v)?;
},
};
if !self.id.is_empty() {
os.write_string(4, &self.id)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
@ -272,10 +256,10 @@ impl ::protobuf::Message for DocumentWSData {
|m: &DocumentWSData| { &m.data },
|m: &mut DocumentWSData| { &mut m.data },
));
fields.push(::protobuf::reflect::accessor::make_singular_i64_accessor::<_>(
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"id",
DocumentWSData::has_id,
DocumentWSData::get_id,
|m: &DocumentWSData| { &m.id },
|m: &mut DocumentWSData| { &mut m.id },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<DocumentWSData>(
"DocumentWSData",
@ -294,9 +278,9 @@ impl ::protobuf::Message for DocumentWSData {
impl ::protobuf::Clear for DocumentWSData {
fn clear(&mut self) {
self.doc_id.clear();
self.ty = DocumentWSDataType::Acked;
self.ty = DocumentWSDataType::Ack;
self.data.clear();
self.one_of_id = ::std::option::Option::None;
self.id.clear();
self.unknown_fields.clear();
}
}
@ -314,7 +298,7 @@ impl ::protobuf::reflect::ProtobufValue for DocumentWSData {
}
#[derive(PartialEq,Clone,Default)]
pub struct DocumentConnected {
pub struct NewDocumentUser {
// message fields
pub user_id: ::std::string::String,
pub doc_id: ::std::string::String,
@ -324,14 +308,14 @@ pub struct DocumentConnected {
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a DocumentConnected {
fn default() -> &'a DocumentConnected {
<DocumentConnected as ::protobuf::Message>::default_instance()
impl<'a> ::std::default::Default for &'a NewDocumentUser {
fn default() -> &'a NewDocumentUser {
<NewDocumentUser as ::protobuf::Message>::default_instance()
}
}
impl DocumentConnected {
pub fn new() -> DocumentConnected {
impl NewDocumentUser {
pub fn new() -> NewDocumentUser {
::std::default::Default::default()
}
@ -403,7 +387,7 @@ impl DocumentConnected {
}
}
impl ::protobuf::Message for DocumentConnected {
impl ::protobuf::Message for NewDocumentUser {
fn is_initialized(&self) -> bool {
true
}
@ -491,8 +475,8 @@ impl ::protobuf::Message for DocumentConnected {
Self::descriptor_static()
}
fn new() -> DocumentConnected {
DocumentConnected::new()
fn new() -> NewDocumentUser {
NewDocumentUser::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@ -501,34 +485,34 @@ impl ::protobuf::Message for DocumentConnected {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"user_id",
|m: &DocumentConnected| { &m.user_id },
|m: &mut DocumentConnected| { &mut m.user_id },
|m: &NewDocumentUser| { &m.user_id },
|m: &mut NewDocumentUser| { &mut m.user_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"doc_id",
|m: &DocumentConnected| { &m.doc_id },
|m: &mut DocumentConnected| { &mut m.doc_id },
|m: &NewDocumentUser| { &m.doc_id },
|m: &mut NewDocumentUser| { &mut m.doc_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"rev_id",
|m: &DocumentConnected| { &m.rev_id },
|m: &mut DocumentConnected| { &mut m.rev_id },
|m: &NewDocumentUser| { &m.rev_id },
|m: &mut NewDocumentUser| { &mut m.rev_id },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<DocumentConnected>(
"DocumentConnected",
::protobuf::reflect::MessageDescriptor::new_pb_name::<NewDocumentUser>(
"NewDocumentUser",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static DocumentConnected {
static instance: ::protobuf::rt::LazyV2<DocumentConnected> = ::protobuf::rt::LazyV2::INIT;
instance.get(DocumentConnected::new)
fn default_instance() -> &'static NewDocumentUser {
static instance: ::protobuf::rt::LazyV2<NewDocumentUser> = ::protobuf::rt::LazyV2::INIT;
instance.get(NewDocumentUser::new)
}
}
impl ::protobuf::Clear for DocumentConnected {
impl ::protobuf::Clear for NewDocumentUser {
fn clear(&mut self) {
self.user_id.clear();
self.doc_id.clear();
@ -537,13 +521,13 @@ impl ::protobuf::Clear for DocumentConnected {
}
}
impl ::std::fmt::Debug for DocumentConnected {
impl ::std::fmt::Debug for NewDocumentUser {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for DocumentConnected {
impl ::protobuf::reflect::ProtobufValue for NewDocumentUser {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
@ -551,7 +535,7 @@ impl ::protobuf::reflect::ProtobufValue for DocumentConnected {
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum DocumentWSDataType {
Acked = 0,
Ack = 0,
PushRev = 1,
PullRev = 2,
UserConnect = 3,
@ -564,7 +548,7 @@ impl ::protobuf::ProtobufEnum for DocumentWSDataType {
fn from_i32(value: i32) -> ::std::option::Option<DocumentWSDataType> {
match value {
0 => ::std::option::Option::Some(DocumentWSDataType::Acked),
0 => ::std::option::Option::Some(DocumentWSDataType::Ack),
1 => ::std::option::Option::Some(DocumentWSDataType::PushRev),
2 => ::std::option::Option::Some(DocumentWSDataType::PullRev),
3 => ::std::option::Option::Some(DocumentWSDataType::UserConnect),
@ -574,7 +558,7 @@ impl ::protobuf::ProtobufEnum for DocumentWSDataType {
fn values() -> &'static [Self] {
static values: &'static [DocumentWSDataType] = &[
DocumentWSDataType::Acked,
DocumentWSDataType::Ack,
DocumentWSDataType::PushRev,
DocumentWSDataType::PullRev,
DocumentWSDataType::UserConnect,
@ -595,7 +579,7 @@ impl ::std::marker::Copy for DocumentWSDataType {
impl ::std::default::Default for DocumentWSDataType {
fn default() -> Self {
DocumentWSDataType::Acked
DocumentWSDataType::Ack
}
}
@ -606,47 +590,46 @@ impl ::protobuf::reflect::ProtobufValue for DocumentWSDataType {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x08ws.proto\"\x7f\n\x0eDocumentWSData\x12\x15\n\x06doc_id\x18\x01\x20\
\n\x08ws.proto\"p\n\x0eDocumentWSData\x12\x15\n\x06doc_id\x18\x01\x20\
\x01(\tR\x05docId\x12#\n\x02ty\x18\x02\x20\x01(\x0e2\x13.DocumentWSDataT\
ypeR\x02ty\x12\x12\n\x04data\x18\x03\x20\x01(\x0cR\x04data\x12\x10\n\x02\
id\x18\x04\x20\x01(\x03H\0R\x02idB\x0b\n\tone_of_id\"Z\n\x11DocumentConn\
ected\x12\x17\n\x07user_id\x18\x01\x20\x01(\tR\x06userId\x12\x15\n\x06do\
c_id\x18\x02\x20\x01(\tR\x05docId\x12\x15\n\x06rev_id\x18\x03\x20\x01(\
\x03R\x05revId*J\n\x12DocumentWSDataType\x12\t\n\x05Acked\x10\0\x12\x0b\
\n\x07PushRev\x10\x01\x12\x0b\n\x07PullRev\x10\x02\x12\x0f\n\x0bUserConn\
ect\x10\x03J\x9a\x05\n\x06\x12\x04\0\0\x12\x01\n\x08\n\x01\x0c\x12\x03\0\
\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x07\x01\n\n\n\x03\x04\0\x01\x12\x03\
\x02\x08\x16\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\
\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\
\x0b\x11\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x14\x15\n\x0b\n\x04\x04\
\0\x02\x01\x12\x03\x04\x04\x1e\n\x0c\n\x05\x04\0\x02\x01\x06\x12\x03\x04\
\x04\x16\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x17\x19\n\x0c\n\x05\
\x04\0\x02\x01\x03\x12\x03\x04\x1c\x1d\n\x0b\n\x04\x04\0\x02\x02\x12\x03\
\x05\x04\x13\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x04\t\n\x0c\n\x05\
\x04\0\x02\x02\x01\x12\x03\x05\n\x0e\n\x0c\n\x05\x04\0\x02\x02\x03\x12\
\x03\x05\x11\x12\n\x0b\n\x04\x04\0\x08\0\x12\x03\x06\x04%\n\x0c\n\x05\
\x04\0\x08\0\x01\x12\x03\x06\n\x13\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\
\x16#\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x06\x16\x1b\n\x0c\n\x05\x04\
\0\x02\x03\x01\x12\x03\x06\x1c\x1e\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\
\x06!\"\n\n\n\x02\x04\x01\x12\x04\x08\0\x0c\x01\n\n\n\x03\x04\x01\x01\
\x12\x03\x08\x08\x19\n\x0b\n\x04\x04\x01\x02\0\x12\x03\t\x04\x17\n\x0c\n\
\x05\x04\x01\x02\0\x05\x12\x03\t\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\
\x03\t\x0b\x12\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\t\x15\x16\n\x0b\n\
\x04\x04\x01\x02\x01\x12\x03\n\x04\x16\n\x0c\n\x05\x04\x01\x02\x01\x05\
\x12\x03\n\x04\n\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\n\x0b\x11\n\x0c\
\n\x05\x04\x01\x02\x01\x03\x12\x03\n\x14\x15\n\x0b\n\x04\x04\x01\x02\x02\
\x12\x03\x0b\x04\x15\n\x0c\n\x05\x04\x01\x02\x02\x05\x12\x03\x0b\x04\t\n\
\x0c\n\x05\x04\x01\x02\x02\x01\x12\x03\x0b\n\x10\n\x0c\n\x05\x04\x01\x02\
\x02\x03\x12\x03\x0b\x13\x14\n\n\n\x02\x05\0\x12\x04\r\0\x12\x01\n\n\n\
\x03\x05\0\x01\x12\x03\r\x05\x17\n\x0b\n\x04\x05\0\x02\0\x12\x03\x0e\x04\
\x0e\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x0e\x04\t\n\x0c\n\x05\x05\0\x02\
\0\x02\x12\x03\x0e\x0c\r\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x0f\x04\x10\n\
\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x0f\x04\x0b\n\x0c\n\x05\x05\0\x02\
\x01\x02\x12\x03\x0f\x0e\x0f\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x10\x04\
\x10\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x10\x04\x0b\n\x0c\n\x05\x05\0\
\x02\x02\x02\x12\x03\x10\x0e\x0f\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x11\
\x04\x14\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x11\x04\x0f\n\x0c\n\x05\
\x05\0\x02\x03\x02\x12\x03\x11\x12\x13b\x06proto3\
ypeR\x02ty\x12\x12\n\x04data\x18\x03\x20\x01(\x0cR\x04data\x12\x0e\n\x02\
id\x18\x04\x20\x01(\tR\x02id\"X\n\x0fNewDocumentUser\x12\x17\n\x07user_i\
d\x18\x01\x20\x01(\tR\x06userId\x12\x15\n\x06doc_id\x18\x02\x20\x01(\tR\
\x05docId\x12\x15\n\x06rev_id\x18\x03\x20\x01(\x03R\x05revId*H\n\x12Docu\
mentWSDataType\x12\x07\n\x03Ack\x10\0\x12\x0b\n\x07PushRev\x10\x01\x12\
\x0b\n\x07PullRev\x10\x02\x12\x0f\n\x0bUserConnect\x10\x03J\xff\x04\n\
\x06\x12\x04\0\0\x12\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\
\x12\x04\x02\0\x07\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x16\n\x0b\n\
\x04\x04\0\x02\0\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\
\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0b\x11\n\x0c\n\x05\
\x04\0\x02\0\x03\x12\x03\x03\x14\x15\n\x0b\n\x04\x04\0\x02\x01\x12\x03\
\x04\x04\x1e\n\x0c\n\x05\x04\0\x02\x01\x06\x12\x03\x04\x04\x16\n\x0c\n\
\x05\x04\0\x02\x01\x01\x12\x03\x04\x17\x19\n\x0c\n\x05\x04\0\x02\x01\x03\
\x12\x03\x04\x1c\x1d\n\x0b\n\x04\x04\0\x02\x02\x12\x03\x05\x04\x13\n\x0c\
\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x04\t\n\x0c\n\x05\x04\0\x02\x02\x01\
\x12\x03\x05\n\x0e\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x11\x12\n\
\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x04\x12\n\x0c\n\x05\x04\0\x02\x03\
\x05\x12\x03\x06\x04\n\n\x0c\n\x05\x04\0\x02\x03\x01\x12\x03\x06\x0b\r\n\
\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06\x10\x11\n\n\n\x02\x04\x01\x12\
\x04\x08\0\x0c\x01\n\n\n\x03\x04\x01\x01\x12\x03\x08\x08\x17\n\x0b\n\x04\
\x04\x01\x02\0\x12\x03\t\x04\x17\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\t\
\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\t\x0b\x12\n\x0c\n\x05\x04\
\x01\x02\0\x03\x12\x03\t\x15\x16\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\n\
\x04\x16\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\n\x04\n\n\x0c\n\x05\x04\
\x01\x02\x01\x01\x12\x03\n\x0b\x11\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\
\x03\n\x14\x15\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\x0b\x04\x15\n\x0c\n\
\x05\x04\x01\x02\x02\x05\x12\x03\x0b\x04\t\n\x0c\n\x05\x04\x01\x02\x02\
\x01\x12\x03\x0b\n\x10\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\x0b\x13\
\x14\n\n\n\x02\x05\0\x12\x04\r\0\x12\x01\n\n\n\x03\x05\0\x01\x12\x03\r\
\x05\x17\n\x0b\n\x04\x05\0\x02\0\x12\x03\x0e\x04\x0c\n\x0c\n\x05\x05\0\
\x02\0\x01\x12\x03\x0e\x04\x07\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x0e\n\
\x0b\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x0f\x04\x10\n\x0c\n\x05\x05\0\x02\
\x01\x01\x12\x03\x0f\x04\x0b\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x0f\
\x0e\x0f\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x10\x04\x10\n\x0c\n\x05\x05\0\
\x02\x02\x01\x12\x03\x10\x04\x0b\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\
\x10\x0e\x0f\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x11\x04\x14\n\x0c\n\x05\
\x05\0\x02\x03\x01\x12\x03\x11\x04\x0f\n\x0c\n\x05\x05\0\x02\x03\x02\x12\
\x03\x11\x12\x13b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -4,15 +4,15 @@ message DocumentWSData {
string doc_id = 1;
DocumentWSDataType ty = 2;
bytes data = 3;
oneof one_of_id { int64 id = 4; };
string id = 4;
}
message DocumentConnected {
message NewDocumentUser {
string user_id = 1;
string doc_id = 2;
int64 rev_id = 3;
}
enum DocumentWSDataType {
Acked = 0;
Ack = 0;
PushRev = 1;
PullRev = 2;
UserConnect = 3;

View File

@ -61,7 +61,7 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
| "NewDocUser"
| "DocIdentifier"
| "DocumentWSData"
| "DocumentConnected"
| "NewDocumentUser"
| "WSError"
| "WSMessage"
| "Revision"

View File

@ -3,7 +3,6 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use std::convert::TryInto;
use tokio_tungstenite::tungstenite::Message as TokioMessage;
// Opti: using four bytes of the data to represent the source
#[derive(ProtoBuf, Debug, Clone, Default)]
pub struct WSMessage {
#[pb(index = 1)]