Refactor/crate directory (#1621)

* chore: fix wanrings

* chore: remove protobuf ref in flowy-error-code

* chore: remove protobuf ref in lib-ws

* refactor: remove protobuf trait in flowy http model

* refactor: remove flowy-error-code crate

Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
Nathan.fooo 2022-12-30 11:16:47 +08:00 committed by GitHub
parent aae8259f63
commit aa5f052ecf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
167 changed files with 655 additions and 923 deletions

View File

@ -43,7 +43,6 @@ CRATE_TYPE = "staticlib"
LIB_EXT = "a" LIB_EXT = "a"
APP_ENVIRONMENT = "local" APP_ENVIRONMENT = "local"
FLUTTER_FLOWY_SDK_PATH = "app_flowy/packages/flowy_sdk" FLUTTER_FLOWY_SDK_PATH = "app_flowy/packages/flowy_sdk"
PROTOBUF_DERIVE_CACHE = "../shared-lib/flowy-derive/src/derive_cache/derive_cache.rs"
# Test default config # Test default config
TEST_CRATE_TYPE = "cdylib" TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "dylib" TEST_LIB_EXT = "dylib"

View File

@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:easy_localization/easy_localization.dart' import 'package:easy_localization/easy_localization.dart'
show StringTranslateExtension; show StringTranslateExtension;
import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';

View File

@ -1,6 +1,6 @@
import 'package:app_flowy/user/application/auth_service.dart'; import 'package:app_flowy/user/application/auth_service.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB; import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -20,24 +20,34 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
); );
}, },
emailChanged: (EmailChanged value) async { emailChanged: (EmailChanged value) async {
emit(state.copyWith(email: value.email, emailError: none(), successOrFail: none())); emit(state.copyWith(
email: value.email, emailError: none(), successOrFail: none()));
}, },
passwordChanged: (PasswordChanged value) async { passwordChanged: (PasswordChanged value) async {
emit(state.copyWith(password: value.password, passwordError: none(), successOrFail: none())); emit(state.copyWith(
password: value.password,
passwordError: none(),
successOrFail: none()));
}, },
); );
}); });
} }
Future<void> _performActionOnSignIn(SignInState state, Emitter<SignInState> emit) async { Future<void> _performActionOnSignIn(
emit(state.copyWith(isSubmitting: true, emailError: none(), passwordError: none(), successOrFail: none())); SignInState state, Emitter<SignInState> emit) async {
emit(state.copyWith(
isSubmitting: true,
emailError: none(),
passwordError: none(),
successOrFail: none()));
final result = await authService.signIn( final result = await authService.signIn(
email: state.email, email: state.email,
password: state.password, password: state.password,
); );
emit(result.fold( emit(result.fold(
(userProfile) => state.copyWith(isSubmitting: false, successOrFail: some(left(userProfile))), (userProfile) => state.copyWith(
isSubmitting: false, successOrFail: some(left(userProfile))),
(error) => stateFromCode(error), (error) => stateFromCode(error),
)); ));
} }
@ -45,18 +55,26 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
SignInState stateFromCode(FlowyError error) { SignInState stateFromCode(FlowyError error) {
switch (ErrorCode.valueOf(error.code)!) { switch (ErrorCode.valueOf(error.code)!) {
case ErrorCode.EmailFormatInvalid: case ErrorCode.EmailFormatInvalid:
return state.copyWith(isSubmitting: false, emailError: some(error.msg), passwordError: none()); return state.copyWith(
isSubmitting: false,
emailError: some(error.msg),
passwordError: none());
case ErrorCode.PasswordFormatInvalid: case ErrorCode.PasswordFormatInvalid:
return state.copyWith(isSubmitting: false, passwordError: some(error.msg), emailError: none()); return state.copyWith(
isSubmitting: false,
passwordError: some(error.msg),
emailError: none());
default: default:
return state.copyWith(isSubmitting: false, successOrFail: some(right(error))); return state.copyWith(
isSubmitting: false, successOrFail: some(right(error)));
} }
} }
} }
@freezed @freezed
class SignInEvent with _$SignInEvent { class SignInEvent with _$SignInEvent {
const factory SignInEvent.signedInWithUserEmailAndPassword() = SignedInWithUserEmailAndPassword; const factory SignInEvent.signedInWithUserEmailAndPassword() =
SignedInWithUserEmailAndPassword;
const factory SignInEvent.emailChanged(String email) = EmailChanged; const factory SignInEvent.emailChanged(String email) = EmailChanged;
const factory SignInEvent.passwordChanged(String password) = PasswordChanged; const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
} }

View File

@ -1,7 +1,7 @@
import 'package:app_flowy/user/application/auth_service.dart'; import 'package:app_flowy/user/application/auth_service.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB; import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -17,11 +17,18 @@ class SignUpBloc extends Bloc<SignUpEvent, SignUpState> {
await event.map(signUpWithUserEmailAndPassword: (e) async { await event.map(signUpWithUserEmailAndPassword: (e) async {
await _performActionOnSignUp(emit); await _performActionOnSignUp(emit);
}, emailChanged: (_EmailChanged value) async { }, emailChanged: (_EmailChanged value) async {
emit(state.copyWith(email: value.email, emailError: none(), successOrFail: none())); emit(state.copyWith(
email: value.email, emailError: none(), successOrFail: none()));
}, passwordChanged: (_PasswordChanged value) async { }, passwordChanged: (_PasswordChanged value) async {
emit(state.copyWith(password: value.password, passwordError: none(), successOrFail: none())); emit(state.copyWith(
password: value.password,
passwordError: none(),
successOrFail: none()));
}, repeatPasswordChanged: (_RepeatPasswordChanged value) async { }, repeatPasswordChanged: (_RepeatPasswordChanged value) async {
emit(state.copyWith(repeatedPassword: value.password, repeatPasswordError: none(), successOrFail: none())); emit(state.copyWith(
repeatedPassword: value.password,
repeatPasswordError: none(),
successOrFail: none()));
}); });
}); });
} }
@ -45,7 +52,8 @@ class SignUpBloc extends Bloc<SignUpEvent, SignUpState> {
if (repeatedPassword == null) { if (repeatedPassword == null) {
emit(state.copyWith( emit(state.copyWith(
isSubmitting: false, isSubmitting: false,
repeatPasswordError: some(LocaleKeys.signUp_repeatPasswordEmptyError.tr()), repeatPasswordError:
some(LocaleKeys.signUp_repeatPasswordEmptyError.tr()),
)); ));
return; return;
} }
@ -53,7 +61,8 @@ class SignUpBloc extends Bloc<SignUpEvent, SignUpState> {
if (password != repeatedPassword) { if (password != repeatedPassword) {
emit(state.copyWith( emit(state.copyWith(
isSubmitting: false, isSubmitting: false,
repeatPasswordError: some(LocaleKeys.signUp_unmatchedPasswordError.tr()), repeatPasswordError:
some(LocaleKeys.signUp_unmatchedPasswordError.tr()),
)); ));
return; return;
} }
@ -97,17 +106,20 @@ class SignUpBloc extends Bloc<SignUpEvent, SignUpState> {
successOrFail: none(), successOrFail: none(),
); );
default: default:
return state.copyWith(isSubmitting: false, successOrFail: some(right(error))); return state.copyWith(
isSubmitting: false, successOrFail: some(right(error)));
} }
} }
} }
@freezed @freezed
class SignUpEvent with _$SignUpEvent { class SignUpEvent with _$SignUpEvent {
const factory SignUpEvent.signUpWithUserEmailAndPassword() = SignUpWithUserEmailAndPassword; const factory SignUpEvent.signUpWithUserEmailAndPassword() =
SignUpWithUserEmailAndPassword;
const factory SignUpEvent.emailChanged(String email) = _EmailChanged; const factory SignUpEvent.emailChanged(String email) = _EmailChanged;
const factory SignUpEvent.passwordChanged(String password) = _PasswordChanged; const factory SignUpEvent.passwordChanged(String password) = _PasswordChanged;
const factory SignUpEvent.repeatPasswordChanged(String password) = _RepeatPasswordChanged; const factory SignUpEvent.repeatPasswordChanged(String password) =
_RepeatPasswordChanged;
} }
@freezed @freezed

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:app_flowy/core/folder_notification.dart'; import 'package:app_flowy/core/folder_notification.dart';
import 'package:app_flowy/core/user_notification.dart'; import 'package:app_flowy/core/user_notification.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'dart:typed_data'; import 'dart:typed_data';

View File

@ -1,6 +1,6 @@
import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/code.pb.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -1,7 +1,7 @@
import 'package:app_flowy/user/application/user_listener.dart'; import 'package:app_flowy/user/application/user_listener.dart';
import 'package:flowy_infra/time/duration.dart'; import 'package:flowy_infra/time/duration.dart';
import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart' import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart'

View File

@ -872,10 +872,13 @@ dependencies = [
"flowy-ast", "flowy-ast",
"flowy-codegen", "flowy-codegen",
"lazy_static", "lazy_static",
"log",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_json", "serde_json",
"syn", "syn",
"tokio",
"trybuild",
"walkdir", "walkdir",
] ]
@ -927,11 +930,11 @@ dependencies = [
name = "flowy-error" name = "flowy-error"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"bytes", "bytes",
"flowy-codegen", "flowy-codegen",
"flowy-database", "flowy-database",
"flowy-derive", "flowy-derive",
"flowy-error-code",
"flowy-sync", "flowy-sync",
"http-flowy", "http-flowy",
"lib-dispatch", "lib-dispatch",
@ -940,16 +943,7 @@ dependencies = [
"protobuf", "protobuf",
"r2d2", "r2d2",
"serde_json", "serde_json",
] "thiserror",
[[package]]
name = "flowy-error-code"
version = "0.1.0"
dependencies = [
"derive_more",
"flowy-codegen",
"flowy-derive",
"protobuf",
] ]
[[package]] [[package]]
@ -1042,11 +1036,10 @@ name = "flowy-http-model"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bytes", "bytes",
"flowy-codegen",
"flowy-derive",
"lib-infra",
"md5", "md5",
"protobuf", "serde",
"serde_json",
"serde_repr",
] ]
[[package]] [[package]]
@ -1441,6 +1434,12 @@ version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]] [[package]]
name = "globset" name = "globset"
version = "0.4.8" version = "0.4.8"
@ -1470,7 +1469,6 @@ name = "grid-rev-model"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bytes", "bytes",
"flowy-error-code",
"indexmap", "indexmap",
"nanoid", "nanoid",
"serde", "serde",
@ -1855,8 +1853,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bytes", "bytes",
"dashmap", "dashmap",
"flowy-codegen",
"flowy-derive",
"futures", "futures",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -1867,6 +1863,9 @@ dependencies = [
"paste", "paste",
"pin-project", "pin-project",
"protobuf", "protobuf",
"serde",
"serde_json",
"serde_repr",
"strum", "strum",
"strum_macros", "strum_macros",
"tokio", "tokio",
@ -3521,6 +3520,21 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "trybuild"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7f408301c7480f9e6294eb779cfc907f54bd901a9660ef24d7f233ed5376485"
dependencies = [
"glob",
"once_cell",
"serde",
"serde_derive",
"serde_json",
"termcolor",
"toml",
]
[[package]] [[package]]
name = "tungstenite" name = "tungstenite"
version = "0.14.0" version = "0.14.0"

View File

@ -16,6 +16,10 @@ members = [
"flowy-revision", "flowy-revision",
"flowy-grid", "flowy-grid",
"flowy-task", "flowy-task",
"flowy-sync",
"flowy-derive",
"flowy-ast",
"flowy-codegen",
] ]
[profile.dev] [profile.dev]

View File

@ -28,7 +28,7 @@ parking_lot = "0.12.1"
lib-dispatch = { path = "../lib-dispatch" } lib-dispatch = { path = "../lib-dispatch" }
flowy-sdk = { path = "../flowy-sdk" } flowy-sdk = { path = "../flowy-sdk" }
dart-notify = { path = "../dart-notify" } dart-notify = { path = "../dart-notify" }
flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-derive = { path = "../flowy-derive" }
[features] [features]
default = ["flowy-sdk/dart", "dart-notify/dart", "flutter"] default = ["flowy-sdk/dart", "dart-notify/dart", "flutter"]
@ -37,7 +37,7 @@ http_sync = ["flowy-sdk/http_sync", "flowy-sdk/use_bunyan"]
openssl_vendored = ["flowy-sdk/openssl_vendored"] openssl_vendored = ["flowy-sdk/openssl_vendored"]
[build-dependencies] [build-dependencies]
flowy-codegen= { path = "../../../shared-lib/flowy-codegen", features = [ flowy-codegen = { path = "../flowy-codegen", features = [
"dart", "dart",
]} ]}

View File

@ -12,11 +12,11 @@ allo-isolate = {version = "^0.1", features = ["catch-unwind",]}
log = "0.4.14" log = "0.4.14"
bytes = { version = "1.0" } bytes = { version = "1.0" }
flowy-derive = {path = "../../../shared-lib/flowy-derive" } flowy-derive = {path = "../flowy-derive" }
lib-dispatch = {path = "../lib-dispatch" } lib-dispatch = {path = "../lib-dispatch" }
[features] [features]
dart = ["flowy-codegen/dart"] dart = ["flowy-codegen/dart"]
[build-dependencies] [build-dependencies]
flowy-codegen= { path = "../../../shared-lib/flowy-codegen"} flowy-codegen = { path = "../flowy-codegen"}

View File

@ -20,7 +20,7 @@ proc-macro2 = "1.0"
flowy-ast = { path = "../flowy-ast" } flowy-ast = { path = "../flowy-ast" }
lazy_static = {version = "1.4.0"} lazy_static = {version = "1.4.0"}
dashmap = "5" dashmap = "5"
flowy-codegen= { path = "../flowy-codegen"} flowy-codegen = { path = "../flowy-codegen"}
serde_json = "1.0" serde_json = "1.0"
walkdir = "2.3.1" walkdir = "2.3.1"

View File

@ -7,9 +7,9 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
flowy-sync = { path = "../../../shared-lib/flowy-sync"} flowy-sync = { path = "../flowy-sync"}
flowy-http-model = { path = "../../../shared-lib/flowy-http-model"} flowy-http-model = { path = "../../../shared-lib/flowy-http-model"}
flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-derive = { path = "../flowy-derive" }
lib-ot = { path = "../../../shared-lib/lib-ot" } lib-ot = { path = "../../../shared-lib/lib-ot" }
lib-ws = { path = "../../../shared-lib/lib-ws" } lib-ws = { path = "../../../shared-lib/lib-ws" }
lib-infra = { path = "../../../shared-lib/lib-infra" } lib-infra = { path = "../../../shared-lib/lib-infra" }
@ -52,7 +52,7 @@ criterion = "0.3"
rand = "0.8.5" rand = "0.8.5"
[build-dependencies] [build-dependencies]
flowy-codegen= { path = "../../../shared-lib/flowy-codegen"} flowy-codegen = { path = "../flowy-codegen"}
[features] [features]

View File

@ -3,7 +3,6 @@ use crate::DocumentUser;
use async_stream::stream; use async_stream::stream;
use bytes::Bytes; use bytes::Bytes;
use flowy_error::FlowyError; use flowy_error::FlowyError;
use flowy_http_model::revision::RevId;
use flowy_revision::RevisionManager; use flowy_revision::RevisionManager;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use lib_ot::core::Transaction; use lib_ot::core::Transaction;
@ -76,10 +75,10 @@ impl DocumentQueue {
} }
#[tracing::instrument(level = "trace", skip(self, transaction, md5), err)] #[tracing::instrument(level = "trace", skip(self, transaction, md5), err)]
async fn save_local_operations(&self, transaction: Transaction, md5: String) -> Result<RevId, FlowyError> { async fn save_local_operations(&self, transaction: Transaction, md5: String) -> Result<i64, FlowyError> {
let bytes = Bytes::from(transaction.to_bytes()?); let bytes = Bytes::from(transaction.to_bytes()?);
let rev_id = self.rev_manager.add_local_revision(bytes, md5).await?; let rev_id = self.rev_manager.add_local_revision(bytes, md5).await?;
Ok(rev_id.into()) Ok(rev_id)
} }
} }

View File

@ -16,13 +16,13 @@ pub mod errors {
pub const TEXT_BLOCK_SYNC_INTERVAL_IN_MILLIS: u64 = 1000; pub const TEXT_BLOCK_SYNC_INTERVAL_IN_MILLIS: u64 = 1000;
use crate::errors::FlowyError; use crate::errors::FlowyError;
use flowy_http_model::document::{CreateDocumentParams, DocumentIdPB, DocumentPayloadPB, ResetDocumentParams}; use flowy_http_model::document::{CreateDocumentParams, DocumentId, DocumentPayload, ResetDocumentParams};
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
pub trait DocumentCloudService: Send + Sync { pub trait DocumentCloudService: Send + Sync {
fn create_document(&self, token: &str, params: CreateDocumentParams) -> FutureResult<(), FlowyError>; fn create_document(&self, token: &str, params: CreateDocumentParams) -> FutureResult<(), FlowyError>;
fn fetch_document(&self, token: &str, params: DocumentIdPB) -> FutureResult<Option<DocumentPayloadPB>, FlowyError>; fn fetch_document(&self, token: &str, params: DocumentId) -> FutureResult<Option<DocumentPayload>, FlowyError>;
fn update_document_content(&self, token: &str, params: ResetDocumentParams) -> FutureResult<(), FlowyError>; fn update_document_content(&self, token: &str, params: ResetDocumentParams) -> FutureResult<(), FlowyError>;
} }

View File

@ -8,7 +8,8 @@ use bytes::Bytes;
use flowy_database::ConnectionPool; use flowy_database::ConnectionPool;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use flowy_http_model::util::md5; use flowy_http_model::util::md5;
use flowy_http_model::{document::DocumentIdPB, revision::Revision, ws_data::ServerRevisionWSData}; use flowy_http_model::ws_data::ServerRevisionWSData;
use flowy_http_model::{document::DocumentId, revision::Revision};
use flowy_revision::{ use flowy_revision::{
PhantomSnapshotPersistence, RevisionCloudService, RevisionManager, RevisionPersistence, PhantomSnapshotPersistence, RevisionCloudService, RevisionManager, RevisionPersistence,
RevisionPersistenceConfiguration, RevisionWebSocket, RevisionPersistenceConfiguration, RevisionWebSocket,
@ -19,7 +20,8 @@ use lib_infra::future::FutureResult;
use lib_infra::ref_map::{RefCountHashMap, RefCountValue}; use lib_infra::ref_map::{RefCountHashMap, RefCountValue};
use lib_ws::WSConnectState; use lib_ws::WSConnectState;
use std::any::Any; use std::any::Any;
use std::{convert::TryInto, sync::Arc}; use std::convert::TryFrom;
use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
pub trait DocumentUser: Send + Sync { pub trait DocumentUser: Send + Sync {
@ -150,10 +152,14 @@ impl DocumentManager {
} }
pub async fn receive_ws_data(&self, data: Bytes) { pub async fn receive_ws_data(&self, data: Bytes) {
let result: Result<ServerRevisionWSData, protobuf::ProtobufError> = data.try_into(); let result: Result<ServerRevisionWSData, serde_json::Error> = ServerRevisionWSData::try_from(data);
match result { match result {
Ok(data) => match self.editor_map.read().await.get(&data.object_id) { Ok(data) => match self.editor_map.read().await.get(&data.object_id) {
None => tracing::error!("Can't find any source handler for {:?}-{:?}", data.object_id, data.ty), None => tracing::error!(
"Can't find any source handler for {:?}-{:?}",
data.object_id,
data.payload
),
Some(handler) => match handler.0.receive_ws_data(data).await { Some(handler) => match handler.0.receive_ws_data(data).await {
Ok(_) => {} Ok(_) => {}
Err(e) => tracing::error!("{}", e), Err(e) => tracing::error!("{}", e),
@ -294,7 +300,7 @@ struct DocumentRevisionCloudService {
impl RevisionCloudService for DocumentRevisionCloudService { impl RevisionCloudService for DocumentRevisionCloudService {
#[tracing::instrument(level = "trace", skip(self))] #[tracing::instrument(level = "trace", skip(self))]
fn fetch_object(&self, user_id: &str, object_id: &str) -> FutureResult<Vec<Revision>, FlowyError> { fn fetch_object(&self, user_id: &str, object_id: &str) -> FutureResult<Vec<Revision>, FlowyError> {
let params: DocumentIdPB = object_id.to_string().into(); let params: DocumentId = object_id.to_string().into();
let server = self.server.clone(); let server = self.server.clone();
let token = self.token.clone(); let token = self.token.clone();

View File

@ -5,7 +5,7 @@ use crate::{errors::FlowyError, DocumentEditor, DocumentUser};
use bytes::Bytes; use bytes::Bytes;
use flowy_database::ConnectionPool; use flowy_database::ConnectionPool;
use flowy_error::{internal_error, FlowyResult}; use flowy_error::{internal_error, FlowyResult};
use flowy_http_model::document::DocumentPayloadPB; use flowy_http_model::document::DocumentPayload;
use flowy_http_model::revision::Revision; use flowy_http_model::revision::Revision;
use flowy_http_model::ws_data::ServerRevisionWSData; use flowy_http_model::ws_data::ServerRevisionWSData;
use flowy_revision::{ use flowy_revision::{
@ -246,14 +246,14 @@ impl DeltaDocumentEditor {
pub struct DeltaDocumentRevisionSerde(); pub struct DeltaDocumentRevisionSerde();
impl RevisionObjectDeserializer for DeltaDocumentRevisionSerde { impl RevisionObjectDeserializer for DeltaDocumentRevisionSerde {
type Output = DocumentPayloadPB; type Output = DocumentPayload;
fn deserialize_revisions(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> { fn deserialize_revisions(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
let (base_rev_id, rev_id) = revisions.last().unwrap().pair_rev_id(); let (base_rev_id, rev_id) = revisions.last().unwrap().pair_rev_id();
let mut delta = make_operations_from_revisions(revisions)?; let mut delta = make_operations_from_revisions(revisions)?;
correct_delta(&mut delta); correct_delta(&mut delta);
Result::<DocumentPayloadPB, FlowyError>::Ok(DocumentPayloadPB { Result::<DocumentPayload, FlowyError>::Ok(DocumentPayload {
doc_id: object_id.to_owned(), doc_id: object_id.to_owned(),
data: delta.json_bytes().to_vec(), data: delta.json_bytes().to_vec(),
rev_id, rev_id,

View File

@ -3,7 +3,6 @@ use crate::DocumentUser;
use async_stream::stream; use async_stream::stream;
use flowy_database::ConnectionPool; use flowy_database::ConnectionPool;
use flowy_error::FlowyError; use flowy_error::FlowyError;
use flowy_http_model::revision::RevId;
use flowy_revision::{RevisionMD5, RevisionManager, TransformOperations}; use flowy_revision::{RevisionMD5, RevisionManager, TransformOperations};
use flowy_sync::{ use flowy_sync::{
client_document::{history::UndoResult, ClientDocument}, client_document::{history::UndoResult, ClientDocument},
@ -176,10 +175,10 @@ impl EditDocumentQueue {
Ok(()) Ok(())
} }
async fn save_local_operations(&self, operations: DeltaTextOperations, md5: String) -> Result<RevId, FlowyError> { async fn save_local_operations(&self, operations: DeltaTextOperations, md5: String) -> Result<i64, FlowyError> {
let bytes = operations.json_bytes(); let bytes = operations.json_bytes();
let rev_id = self.rev_manager.add_local_revision(bytes, md5).await?; let rev_id = self.rev_manager.add_local_revision(bytes, md5).await?;
Ok(rev_id.into()) Ok(rev_id)
} }
} }

View File

@ -3,10 +3,8 @@ use crate::TEXT_BLOCK_SYNC_INTERVAL_IN_MILLIS;
use bytes::Bytes; use bytes::Bytes;
use flowy_database::ConnectionPool; use flowy_database::ConnectionPool;
use flowy_error::{internal_error, FlowyError, FlowyResult}; use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_http_model::{ use flowy_http_model::revision::{Revision, RevisionRange};
revision::{Revision, RevisionRange}, use flowy_http_model::ws_data::{ClientRevisionWSData, NewDocumentUser};
ws_data::{ClientRevisionWSData, NewDocumentUser, ServerRevisionWSDataType},
};
use flowy_revision::*; use flowy_revision::*;
use flowy_sync::errors::CollaborateResult; use flowy_sync::errors::CollaborateResult;
use flowy_sync::util::make_operations_from_revisions; use flowy_sync::util::make_operations_from_revisions;
@ -96,14 +94,14 @@ impl DocumentRevisionWSDataStream {
} }
impl RevisionWSDataStream for DocumentRevisionWSDataStream { impl RevisionWSDataStream for DocumentRevisionWSDataStream {
fn receive_push_revision(&self, bytes: Bytes) -> BoxResultFuture<(), FlowyError> { fn receive_push_revision(&self, revisions: Vec<Revision>) -> BoxResultFuture<(), FlowyError> {
let resolver = self.conflict_controller.clone(); let resolver = self.conflict_controller.clone();
Box::pin(async move { resolver.receive_bytes(bytes).await }) Box::pin(async move { resolver.receive_revisions(revisions).await })
} }
fn receive_ack(&self, id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError> { fn receive_ack(&self, rev_id: i64) -> BoxResultFuture<(), FlowyError> {
let resolver = self.conflict_controller.clone(); let resolver = self.conflict_controller.clone();
Box::pin(async move { resolver.ack_revision(id, ty).await }) Box::pin(async move { resolver.ack_revision(rev_id).await })
} }
fn receive_new_user_connect(&self, _new_user: NewDocumentUser) -> BoxResultFuture<(), FlowyError> { fn receive_new_user_connect(&self, _new_user: NewDocumentUser) -> BoxResultFuture<(), FlowyError> {

View File

@ -126,7 +126,7 @@ impl DeltaRevisionSql {
fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
let state: TextRevisionState = changeset.state.clone().into(); let state: TextRevisionState = changeset.state.clone().into();
let filter = dsl::rev_table let filter = dsl::rev_table
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) .filter(dsl::rev_id.eq(changeset.rev_id))
.filter(dsl::doc_id.eq(changeset.object_id)); .filter(dsl::doc_id.eq(changeset.object_id));
let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?;
tracing::debug!( tracing::debug!(

View File

@ -126,7 +126,7 @@ impl DocumentRevisionSql {
fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
let state: DocumentRevisionState = changeset.state.clone().into(); let state: DocumentRevisionState = changeset.state.clone().into();
let filter = dsl::document_rev_table let filter = dsl::document_rev_table
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) .filter(dsl::rev_id.eq(changeset.rev_id))
.filter(dsl::document_id.eq(changeset.object_id)); .filter(dsl::document_id.eq(changeset.object_id));
let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?;
tracing::debug!( tracing::debug!(

View File

@ -6,8 +6,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-derive = { path = "../flowy-derive" }
flowy-error-code = { path = "../../../shared-lib/flowy-error-code"}
lib-dispatch = { path = "../lib-dispatch" } lib-dispatch = { path = "../lib-dispatch" }
protobuf = {version = "2.20.0"} protobuf = {version = "2.20.0"}
bytes = "1.0" bytes = "1.0"
@ -15,7 +14,7 @@ anyhow = "1.0"
thiserror = "1.0" thiserror = "1.0"
flowy-sync = { path = "../../../shared-lib/flowy-sync", optional = true} flowy-sync = { path = "../flowy-sync", optional = true}
lib-ot = { path = "../../../shared-lib/lib-ot", optional = true} lib-ot = { path = "../../../shared-lib/lib-ot", optional = true}
serde_json = {version = "1.0", optional = true} serde_json = {version = "1.0", optional = true}
http-flowy = { git = "https://github.com/AppFlowy-IO/AppFlowy-Server", optional = true} http-flowy = { git = "https://github.com/AppFlowy-IO/AppFlowy-Server", optional = true}
@ -29,7 +28,7 @@ ot = ["lib-ot"]
serde = ["serde_json"] serde = ["serde_json"]
http_server = ["http-flowy"] http_server = ["http-flowy"]
db = ["flowy-database", "lib-sqlite", "r2d2"] db = ["flowy-database", "lib-sqlite", "r2d2"]
dart = ["flowy-error-code/dart", "flowy-codegen/dart"] dart = ["flowy-codegen/dart"]
[build-dependencies] [build-dependencies]
flowy-codegen= { path = "../../../shared-lib/flowy-codegen"} flowy-codegen = { path = "../flowy-codegen"}

View File

@ -1,3 +1,3 @@
# Check out the FlowyConfig (located in flowy_toml.rs) for more details. # Check out the FlowyConfig (located in flowy_toml.rs) for more details.
proto_input = ["src/errors.rs",] proto_input = ["src/errors.rs","src/code.rs"]

View File

@ -1,155 +1,166 @@
use crate::protobuf::ErrorCode as ProtoBufErrorCode;
use flowy_derive::ProtoBuf_Enum; use flowy_derive::ProtoBuf_Enum;
use protobuf::ProtobufEnum;
use std::convert::{TryFrom, TryInto};
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Clone, ProtoBuf_Enum, PartialEq, Eq, Error)] #[derive(Debug, Clone, PartialEq, Eq, Error, ProtoBuf_Enum)]
pub enum ErrorCode { pub enum ErrorCode {
#[error("Internal error")] #[error("Internal error")]
Internal = 0, Internal = 0,
#[error("UserUnauthorized")] #[error("Unauthorized user")]
UserUnauthorized = 2, UserUnauthorized = 2,
#[error("RecordNotFound")] #[error("Record not found")]
RecordNotFound = 3, RecordNotFound = 3,
#[error("User id is empty")] #[error("User id is empty")]
UserIdIsEmpty = 4, UserIdIsEmpty = 4,
#[error("Workspace name can not be empty or whitespace")] #[error("Workspace name can not be empty or whitespace")]
WorkspaceNameInvalid = 100, WorkspaceNameInvalid = 5,
#[error("Workspace id can not be empty or whitespace")] #[error("Workspace id can not be empty or whitespace")]
WorkspaceIdInvalid = 101, WorkspaceIdInvalid = 6,
#[error("Color style of the App is invalid")] #[error("Color style of the App is invalid")]
AppColorStyleInvalid = 102, AppColorStyleInvalid = 7,
#[error("Workspace desc is invalid")] #[error("Workspace desc is invalid")]
WorkspaceDescTooLong = 103, WorkspaceDescTooLong = 8,
#[error("Workspace description too long")] #[error("Workspace description too long")]
WorkspaceNameTooLong = 104, WorkspaceNameTooLong = 9,
#[error("App id can not be empty or whitespace")] #[error("App id can not be empty or whitespace")]
AppIdInvalid = 110, AppIdInvalid = 10,
#[error("App name can not be empty or whitespace")] #[error("App name can not be empty or whitespace")]
AppNameInvalid = 111, AppNameInvalid = 11,
#[error("View name can not be empty or whitespace")] #[error("View name can not be empty or whitespace")]
ViewNameInvalid = 120, ViewNameInvalid = 12,
#[error("Thumbnail of the view is invalid")] #[error("Thumbnail of the view is invalid")]
ViewThumbnailInvalid = 121, ViewThumbnailInvalid = 13,
#[error("View id can not be empty or whitespace")] #[error("View id can not be empty or whitespace")]
ViewIdInvalid = 122, ViewIdInvalid = 14,
#[error("View desc too long")] #[error("View desc too long")]
ViewDescTooLong = 123, ViewDescTooLong = 15,
#[error("View data is invalid")] #[error("View data is invalid")]
ViewDataInvalid = 124, ViewDataInvalid = 16,
#[error("View name too long")] #[error("View name too long")]
ViewNameTooLong = 125, ViewNameTooLong = 17,
#[error("Connection error")] #[error("Http server connection error")]
ConnectError = 200, HttpServerConnectError = 18,
#[error("Email can not be empty or whitespace")] #[error("Email can not be empty or whitespace")]
EmailIsEmpty = 300, EmailIsEmpty = 19,
#[error("Email format is not valid")] #[error("Email format is not valid")]
EmailFormatInvalid = 301, EmailFormatInvalid = 20,
#[error("Email already exists")] #[error("Email already exists")]
EmailAlreadyExists = 302, EmailAlreadyExists = 21,
#[error("Password can not be empty or whitespace")] #[error("Password can not be empty or whitespace")]
PasswordIsEmpty = 303, PasswordIsEmpty = 22,
#[error("Password format too long")] #[error("Password format too long")]
PasswordTooLong = 304, PasswordTooLong = 23,
#[error("Password contains forbidden characters.")] #[error("Password contains forbidden characters.")]
PasswordContainsForbidCharacters = 305, PasswordContainsForbidCharacters = 24,
#[error("Password should contain a minimum of 6 characters with 1 special 1 letter and 1 numeric")] #[error("Password should contain a minimum of 6 characters with 1 special 1 letter and 1 numeric")]
PasswordFormatInvalid = 306, PasswordFormatInvalid = 25,
#[error("Password not match")] #[error("Password not match")]
PasswordNotMatch = 307, PasswordNotMatch = 26,
#[error("User name is too long")] #[error("User name is too long")]
UserNameTooLong = 308, UserNameTooLong = 27,
#[error("User name contain forbidden characters")] #[error("User name contain forbidden characters")]
UserNameContainForbiddenCharacters = 309, UserNameContainForbiddenCharacters = 28,
#[error("User name can not be empty or whitespace")] #[error("User name can not be empty or whitespace")]
UserNameIsEmpty = 310, UserNameIsEmpty = 29,
#[error("user id is empty or whitespace")] #[error("user id is empty or whitespace")]
UserIdInvalid = 311, UserIdInvalid = 30,
#[error("User not exist")] #[error("User not exist")]
UserNotExist = 312, UserNotExist = 31,
#[error("Text is too long")] #[error("Text is too long")]
TextTooLong = 400, TextTooLong = 32,
#[error("Grid id is empty")] #[error("Grid id is empty")]
GridIdIsEmpty = 410, GridIdIsEmpty = 33,
#[error("Grid view id is empty")] #[error("Grid view id is empty")]
GridViewIdIsEmpty = 411, GridViewIdIsEmpty = 34,
#[error("Grid block id is empty")] #[error("Grid block id is empty")]
BlockIdIsEmpty = 420, BlockIdIsEmpty = 35,
#[error("Row id is empty")] #[error("Row id is empty")]
RowIdIsEmpty = 430, RowIdIsEmpty = 36,
#[error("Select option id is empty")] #[error("Select option id is empty")]
OptionIdIsEmpty = 431, OptionIdIsEmpty = 37,
#[error("Field id is empty")] #[error("Field id is empty")]
FieldIdIsEmpty = 440, FieldIdIsEmpty = 38,
#[error("Field doesn't exist")] #[error("Field doesn't exist")]
FieldDoesNotExist = 441, FieldDoesNotExist = 39,
#[error("The name of the option should not be empty")] #[error("The name of the option should not be empty")]
SelectOptionNameIsEmpty = 442, SelectOptionNameIsEmpty = 40,
#[error("Field not exists")] #[error("Field not exists")]
FieldNotExists = 443, FieldNotExists = 41,
#[error("The operation in this field is invalid")] #[error("The operation in this field is invalid")]
FieldInvalidOperation = 444, FieldInvalidOperation = 42,
#[error("Filter id is empty")] #[error("Filter id is empty")]
FilterIdIsEmpty = 445, FilterIdIsEmpty = 43,
#[error("Field is not exist")] #[error("Field is not exist")]
FieldRecordNotFound = 446, FieldRecordNotFound = 44,
#[error("Field's type-option data should not be empty")] #[error("Field's type-option data should not be empty")]
TypeOptionDataIsEmpty = 450, TypeOptionDataIsEmpty = 45,
#[error("Group id is empty")] #[error("Group id is empty")]
GroupIdIsEmpty = 460, GroupIdIsEmpty = 46,
#[error("Invalid date time format")] #[error("Invalid date time format")]
InvalidDateTimeFormat = 500, InvalidDateTimeFormat = 47,
#[error("The input string is empty or contains invalid characters")] #[error("The input string is empty or contains invalid characters")]
UnexpectedEmptyString = 999, UnexpectedEmptyString = 48,
#[error("Invalid data")] #[error("Invalid data")]
InvalidData = 1000, InvalidData = 49,
#[error("Serde")] #[error("Serde")]
Serde = 1001, Serde = 50,
#[error("Protobuf serde")] #[error("Protobuf serde")]
ProtobufSerde = 1002, ProtobufSerde = 51,
#[error("Out of bounds")] #[error("Out of bounds")]
OutOfBounds = 10001, OutOfBounds = 52,
} }
impl ErrorCode { impl ErrorCode {
pub fn value(&self) -> i32 { pub fn value(&self) -> i32 {
let code: ProtoBufErrorCode = self.clone().try_into().unwrap(); self.clone() as i32
code.value()
}
pub fn from_i32(value: i32) -> Self {
match ProtoBufErrorCode::from_i32(value) {
None => ErrorCode::Internal,
Some(code) => ErrorCode::try_from(&code).unwrap(),
}
} }
} }

View File

@ -1,7 +1,7 @@
use crate::ErrorCode;
use anyhow::Result; use anyhow::Result;
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_error_code::ErrorCode;
use lib_dispatch::prelude::{AFPluginEventResponse, ResponseBuilder}; use lib_dispatch::prelude::{AFPluginEventResponse, ResponseBuilder};
use std::{convert::TryInto, fmt::Debug}; use std::{convert::TryInto, fmt::Debug};
use thiserror::Error; use thiserror::Error;
@ -30,7 +30,7 @@ macro_rules! static_flowy_error {
impl FlowyError { impl FlowyError {
pub fn new(code: ErrorCode, msg: &str) -> Self { pub fn new(code: ErrorCode, msg: &str) -> Self {
Self { Self {
code: code.value(), code: code.value() as i32,
msg: msg.to_owned(), msg: msg.to_owned(),
} }
} }
@ -53,7 +53,7 @@ impl FlowyError {
static_flowy_error!(view_desc, ErrorCode::ViewDescTooLong); static_flowy_error!(view_desc, ErrorCode::ViewDescTooLong);
static_flowy_error!(view_data, ErrorCode::ViewDataInvalid); static_flowy_error!(view_data, ErrorCode::ViewDataInvalid);
static_flowy_error!(unauthorized, ErrorCode::UserUnauthorized); static_flowy_error!(unauthorized, ErrorCode::UserUnauthorized);
static_flowy_error!(connection, ErrorCode::ConnectError); static_flowy_error!(connection, ErrorCode::HttpServerConnectError);
static_flowy_error!(email_empty, ErrorCode::EmailIsEmpty); static_flowy_error!(email_empty, ErrorCode::EmailIsEmpty);
static_flowy_error!(email_format, ErrorCode::EmailFormatInvalid); static_flowy_error!(email_format, ErrorCode::EmailFormatInvalid);
static_flowy_error!(email_exist, ErrorCode::EmailAlreadyExists); static_flowy_error!(email_exist, ErrorCode::EmailAlreadyExists);
@ -77,7 +77,7 @@ impl FlowyError {
impl std::convert::From<ErrorCode> for FlowyError { impl std::convert::From<ErrorCode> for FlowyError {
fn from(code: ErrorCode) -> Self { fn from(code: ErrorCode) -> Self {
FlowyError { FlowyError {
code: code.value(), code: code.value() as i32,
msg: format!("{}", code), msg: format!("{}", code),
} }
} }

View File

@ -1,5 +1,4 @@
use crate::FlowyError; use crate::{ErrorCode, FlowyError};
use flowy_error_code::ErrorCode;
use http_flowy::errors::{ErrorCode as ServerErrorCode, ServerError}; use http_flowy::errors::{ErrorCode as ServerErrorCode, ServerError};
impl std::convert::From<ServerError> for FlowyError { impl std::convert::From<ServerError> for FlowyError {
@ -15,7 +14,7 @@ fn server_error_to_flowy_error(code: ServerErrorCode) -> ErrorCode {
ServerErrorCode::PasswordNotMatch => ErrorCode::PasswordNotMatch, ServerErrorCode::PasswordNotMatch => ErrorCode::PasswordNotMatch,
ServerErrorCode::RecordNotFound => ErrorCode::RecordNotFound, ServerErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
ServerErrorCode::ConnectRefused | ServerErrorCode::ConnectTimeout | ServerErrorCode::ConnectClose => { ServerErrorCode::ConnectRefused | ServerErrorCode::ConnectTimeout | ServerErrorCode::ConnectClose => {
ErrorCode::ConnectError ErrorCode::HttpServerConnectError
} }
_ => ErrorCode::Internal, _ => ErrorCode::Internal,
} }

View File

@ -1,6 +1,7 @@
mod code;
mod errors; mod errors;
mod ext; mod ext;
pub mod protobuf; pub mod protobuf;
pub use code::*;
pub use errors::*; pub use errors::*;
pub use flowy_error_code::ErrorCode;

View File

@ -7,9 +7,9 @@ edition = "2018"
[dependencies] [dependencies]
folder-rev-model = { path = "../../../shared-lib/folder-rev-model" } folder-rev-model = { path = "../../../shared-lib/folder-rev-model" }
flowy-sync = { path = "../../../shared-lib/flowy-sync" } flowy-sync = { path = "../flowy-sync"}
flowy-http-model = { path = "../../../shared-lib/flowy-http-model" } flowy-http-model = { path = "../../../shared-lib/flowy-http-model" }
flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-derive = { path = "../flowy-derive" }
lib-ot = { path = "../../../shared-lib/lib-ot" } lib-ot = { path = "../../../shared-lib/lib-ot" }
lib-infra = { path = "../../../shared-lib/lib-infra" } lib-infra = { path = "../../../shared-lib/lib-infra" }
@ -35,14 +35,14 @@ serde = { version = "1.0", features = ["derive"] }
tracing = { version = "0.1", features = ["log"] } tracing = { version = "0.1", features = ["log"] }
bytes = { version = "1.0" } bytes = { version = "1.0" }
unicode-segmentation = "1.8" unicode-segmentation = "1.8"
serde_json = "1.0"
[dev-dependencies] [dev-dependencies]
serde_json = "1.0"
flowy-folder = { path = "../flowy-folder", features = ["flowy_unit_test"]} flowy-folder = { path = "../flowy-folder", features = ["flowy_unit_test"]}
flowy-test = { path = "../flowy-test" } flowy-test = { path = "../flowy-test" }
[build-dependencies] [build-dependencies]
flowy-codegen= { path = "../../../shared-lib/flowy-codegen"} flowy-codegen = { path = "../flowy-codegen"}
[features] [features]

View File

@ -26,7 +26,8 @@ use crate::services::clear_current_workspace;
use crate::services::persistence::rev_sqlite::SQLiteFolderRevisionPersistence; use crate::services::persistence::rev_sqlite::SQLiteFolderRevisionPersistence;
use flowy_http_model::ws_data::ServerRevisionWSData; use flowy_http_model::ws_data::ServerRevisionWSData;
use flowy_sync::client_folder::FolderPad; use flowy_sync::client_folder::FolderPad;
use std::{collections::HashMap, convert::TryInto, fmt::Formatter, sync::Arc}; use std::convert::TryFrom;
use std::{collections::HashMap, fmt::Formatter, sync::Arc};
use tokio::sync::RwLock as TokioRwLock; use tokio::sync::RwLock as TokioRwLock;
lazy_static! { lazy_static! {
static ref INIT_FOLDER_FLAG: TokioRwLock<HashMap<String, bool>> = TokioRwLock::new(HashMap::new()); static ref INIT_FOLDER_FLAG: TokioRwLock<HashMap<String, bool>> = TokioRwLock::new(HashMap::new());
@ -139,7 +140,7 @@ impl FolderManager {
// } // }
pub async fn did_receive_ws_data(&self, data: Bytes) { pub async fn did_receive_ws_data(&self, data: Bytes) {
let result: Result<ServerRevisionWSData, protobuf::ProtobufError> = data.try_into(); let result = ServerRevisionWSData::try_from(data);
match result { match result {
Ok(data) => match self.folder_editor.read().await.clone() { Ok(data) => match self.folder_editor.read().await.clone() {
None => {} None => {}

View File

@ -124,7 +124,7 @@ impl FolderRevisionSql {
fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
let state: TextRevisionState = changeset.state.clone().into(); let state: TextRevisionState = changeset.state.clone().into();
let filter = dsl::rev_table let filter = dsl::rev_table
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) .filter(dsl::rev_id.eq(changeset.rev_id))
.filter(dsl::doc_id.eq(changeset.object_id)); .filter(dsl::doc_id.eq(changeset.object_id));
let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?;
tracing::debug!( tracing::debug!(

View File

@ -16,7 +16,7 @@ use crate::{
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_database::kv::KV; use flowy_database::kv::KV;
use flowy_http_model::document::DocumentIdPB; use flowy_http_model::document::DocumentId;
use folder_rev_model::{gen_view_id, ViewRevision}; use folder_rev_model::{gen_view_id, ViewRevision};
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use std::{collections::HashSet, sync::Arc}; use std::{collections::HashSet, sync::Arc};
@ -201,7 +201,7 @@ impl ViewController {
} }
#[tracing::instrument(level = "debug", skip(self,params), fields(doc_id = %params.value), err)] #[tracing::instrument(level = "debug", skip(self,params), fields(doc_id = %params.value), err)]
pub(crate) async fn move_view_to_trash(&self, params: DocumentIdPB) -> Result<(), FlowyError> { pub(crate) async fn move_view_to_trash(&self, params: DocumentId) -> Result<(), FlowyError> {
let view_id = params.value; let view_id = params.value;
if let Some(latest_view_id) = KV::get_str(LATEST_VIEW_ID) { if let Some(latest_view_id) = KV::get_str(LATEST_VIEW_ID) {
if latest_view_id == view_id { if latest_view_id == view_id {

View File

@ -3,7 +3,7 @@ use bytes::Bytes;
use flowy_database::ConnectionPool; use flowy_database::ConnectionPool;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_http_model::revision::{Revision, RevisionRange}; use flowy_http_model::revision::{Revision, RevisionRange};
use flowy_http_model::ws_data::{ClientRevisionWSData, NewDocumentUser, ServerRevisionWSDataType}; use flowy_http_model::ws_data::{ClientRevisionWSData, NewDocumentUser};
use flowy_revision::*; use flowy_revision::*;
use flowy_sync::client_folder::FolderPad; use flowy_sync::client_folder::FolderPad;
use flowy_sync::server_folder::FolderOperations; use flowy_sync::server_folder::FolderOperations;
@ -130,14 +130,14 @@ impl FolderRevisionWSDataStream {
} }
impl RevisionWSDataStream for FolderRevisionWSDataStream { impl RevisionWSDataStream for FolderRevisionWSDataStream {
fn receive_push_revision(&self, bytes: Bytes) -> BoxResultFuture<(), FlowyError> { fn receive_push_revision(&self, revisions: Vec<Revision>) -> BoxResultFuture<(), FlowyError> {
let resolver = self.conflict_controller.clone(); let resolver = self.conflict_controller.clone();
Box::pin(async move { resolver.receive_bytes(bytes).await }) Box::pin(async move { resolver.receive_revisions(revisions).await })
} }
fn receive_ack(&self, id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError> { fn receive_ack(&self, rev_id: i64) -> BoxResultFuture<(), FlowyError> {
let resolver = self.conflict_controller.clone(); let resolver = self.conflict_controller.clone();
Box::pin(async move { resolver.ack_revision(id, ty).await }) Box::pin(async move { resolver.ack_revision(rev_id).await })
} }
fn receive_new_user_connect(&self, _new_user: NewDocumentUser) -> BoxResultFuture<(), FlowyError> { fn receive_new_user_connect(&self, _new_user: NewDocumentUser) -> BoxResultFuture<(), FlowyError> {

View File

@ -15,8 +15,6 @@ use flowy_folder::entities::{
}; };
use flowy_folder::event_map::FolderEvent::*; use flowy_folder::event_map::FolderEvent::*;
use flowy_folder::{errors::ErrorCode, services::folder_editor::FolderEditor}; use flowy_folder::{errors::ErrorCode, services::folder_editor::FolderEditor};
use flowy_http_model::document::DocumentPayloadPB;
use flowy_revision::disk::RevisionState; use flowy_revision::disk::RevisionState;
use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS; use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
use flowy_test::{event_builder::*, FlowySDKTest}; use flowy_test::{event_builder::*, FlowySDKTest};
@ -413,17 +411,6 @@ pub async fn delete_view(sdk: &FlowySDKTest, view_ids: Vec<String>) {
.await; .await;
} }
#[allow(dead_code)]
pub async fn set_latest_view(sdk: &FlowySDKTest, view_id: &str) -> DocumentPayloadPB {
let view_id: ViewIdPB = view_id.into();
FolderEventBuilder::new(sdk.clone())
.event(SetLatestView)
.payload(view_id)
.async_send()
.await
.parse::<DocumentPayloadPB>()
}
pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrashPB { pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrashPB {
FolderEventBuilder::new(sdk.clone()) FolderEventBuilder::new(sdk.clone())
.event(ReadTrash) .event(ReadTrash)

View File

@ -11,11 +11,11 @@ dart-notify = { path = "../dart-notify" }
flowy-revision = { path = "../flowy-revision" } flowy-revision = { path = "../flowy-revision" }
flowy-task= { path = "../flowy-task" } flowy-task= { path = "../flowy-task" }
flowy-error = { path = "../flowy-error", features = ["db"]} flowy-error = { path = "../flowy-error", features = ["db"]}
flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-derive = { path = "../flowy-derive" }
lib-ot = { path = "../../../shared-lib/lib-ot" } lib-ot = { path = "../../../shared-lib/lib-ot" }
lib-infra = { path = "../../../shared-lib/lib-infra" } lib-infra = { path = "../../../shared-lib/lib-infra" }
grid-rev-model = { path = "../../../shared-lib/grid-rev-model" } grid-rev-model = { path = "../../../shared-lib/grid-rev-model" }
flowy-sync = { path = "../../../shared-lib/flowy-sync" } flowy-sync = { path = "../flowy-sync"}
flowy-http-model = { path = "../../../shared-lib/flowy-http-model" } flowy-http-model = { path = "../../../shared-lib/flowy-http-model" }
flowy-database = { path = "../flowy-database" } flowy-database = { path = "../flowy-database" }
anyhow = "1.0" anyhow = "1.0"
@ -52,9 +52,7 @@ flowy-test = { path = "../flowy-test" }
flowy-grid = { path = "../flowy-grid", features = ["flowy_unit_test"]} flowy-grid = { path = "../flowy-grid", features = ["flowy_unit_test"]}
[build-dependencies] [build-dependencies]
flowy-codegen= { path = "../../../shared-lib/flowy-codegen"} flowy-codegen = { path = "../flowy-codegen"}
[features] [features]
default = [] default = []

View File

@ -7,8 +7,8 @@ use crate::services::cell::{
use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper; use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper;
use crate::services::field::{ use crate::services::field::{
CheckboxCellData, ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB, StrCellData, CheckboxCellData, ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB, TypeOption,
TypeOption, TypeOptionCellData, TypeOptionTransform, TypeOptionCellData, TypeOptionTransform,
}; };
use bytes::Bytes; use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum};

View File

@ -2,7 +2,7 @@
mod tests { mod tests {
use crate::entities::FieldType; use crate::entities::FieldType;
use crate::services::cell::stringify_cell_data; use crate::services::cell::stringify_cell_data;
use crate::services::cell::CellDataDecoder;
use crate::services::field::FieldBuilder; use crate::services::field::FieldBuilder;
use crate::services::field::*; use crate::services::field::*;

View File

@ -124,7 +124,7 @@ impl GridMetaRevisionSql {
fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
let state: GridBlockRevisionState = changeset.state.clone().into(); let state: GridBlockRevisionState = changeset.state.clone().into();
let filter = dsl::grid_meta_rev_table let filter = dsl::grid_meta_rev_table
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) .filter(dsl::rev_id.eq(changeset.rev_id))
.filter(dsl::object_id.eq(changeset.object_id)); .filter(dsl::object_id.eq(changeset.object_id));
let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?;
tracing::debug!( tracing::debug!(

View File

@ -123,7 +123,7 @@ impl GridRevisionSql {
fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
let state: GridRevisionState = changeset.state.clone().into(); let state: GridRevisionState = changeset.state.clone().into();
let filter = dsl::grid_rev_table let filter = dsl::grid_rev_table
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) .filter(dsl::rev_id.eq(changeset.rev_id))
.filter(dsl::object_id.eq(changeset.object_id)); .filter(dsl::object_id.eq(changeset.object_id));
let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?;
tracing::debug!( tracing::debug!(

View File

@ -123,7 +123,7 @@ impl GridViewRevisionSql {
fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> { fn update(changeset: RevisionChangeset, conn: &SqliteConnection) -> Result<(), FlowyError> {
let state: GridViewRevisionState = changeset.state.clone().into(); let state: GridViewRevisionState = changeset.state.clone().into();
let filter = dsl::grid_view_rev_table let filter = dsl::grid_view_rev_table
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref())) .filter(dsl::rev_id.eq(changeset.rev_id))
.filter(dsl::object_id.eq(changeset.object_id)); .filter(dsl::object_id.eq(changeset.object_id));
let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?; let _ = update(filter).set(dsl::state.eq(state)).execute(conn)?;
tracing::debug!( tracing::debug!(

View File

@ -8,8 +8,8 @@ edition = "2018"
[dependencies] [dependencies]
lib-dispatch = { path = "../lib-dispatch" } lib-dispatch = { path = "../lib-dispatch" }
flowy-error = { path = "../flowy-error", features = ["collaboration", "http_server"] } flowy-error = { path = "../flowy-error", features = ["collaboration", "http_server"] }
flowy-derive = { path = "../../../shared-lib/flowy-derive" } flowy-derive = { path = "../flowy-derive" }
flowy-sync = { path = "../../../shared-lib/flowy-sync"} flowy-sync = { path = "../flowy-sync"}
flowy-http-model = { path = "../../../shared-lib/flowy-http-model"} flowy-http-model = { path = "../../../shared-lib/flowy-http-model"}
folder-rev-model = { path = "../../../shared-lib/folder-rev-model"} folder-rev-model = { path = "../../../shared-lib/folder-rev-model"}
flowy-folder = { path = "../flowy-folder" } flowy-folder = { path = "../flowy-folder" }
@ -48,4 +48,4 @@ dart = [
] ]
[build-dependencies] [build-dependencies]
flowy-codegen = { path = "../../../shared-lib/flowy-codegen"} flowy-codegen = { path = "../flowy-codegen"}

View File

@ -4,7 +4,7 @@ use crate::{
}; };
use flowy_document::DocumentCloudService; use flowy_document::DocumentCloudService;
use flowy_error::FlowyError; use flowy_error::FlowyError;
use flowy_http_model::document::{CreateDocumentParams, DocumentIdPB, DocumentPayloadPB, ResetDocumentParams}; use flowy_http_model::document::{CreateDocumentParams, DocumentId, DocumentPayload, ResetDocumentParams};
use http_flowy::response::FlowyResponse; use http_flowy::response::FlowyResponse;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use lib_infra::future::FutureResult; use lib_infra::future::FutureResult;
@ -27,7 +27,7 @@ impl DocumentCloudService for DocumentCloudServiceImpl {
FutureResult::new(async move { create_document_request(&token, params, &url).await }) FutureResult::new(async move { create_document_request(&token, params, &url).await })
} }
fn fetch_document(&self, token: &str, params: DocumentIdPB) -> FutureResult<Option<DocumentPayloadPB>, FlowyError> { fn fetch_document(&self, token: &str, params: DocumentId) -> FutureResult<Option<DocumentPayload>, FlowyError> {
let token = token.to_owned(); let token = token.to_owned();
let url = self.config.doc_url(); let url = self.config.doc_url();
FutureResult::new(async move { read_document_request(&token, params, &url).await }) FutureResult::new(async move { read_document_request(&token, params, &url).await })
@ -44,7 +44,7 @@ pub async fn create_document_request(token: &str, params: CreateDocumentParams,
let _ = request_builder() let _ = request_builder()
.post(url) .post(url)
.header(HEADER_TOKEN, token) .header(HEADER_TOKEN, token)
.protobuf(params)? .json(params)?
.send() .send()
.await?; .await?;
Ok(()) Ok(())
@ -52,14 +52,14 @@ pub async fn create_document_request(token: &str, params: CreateDocumentParams,
pub async fn read_document_request( pub async fn read_document_request(
token: &str, token: &str,
params: DocumentIdPB, params: DocumentId,
url: &str, url: &str,
) -> Result<Option<DocumentPayloadPB>, FlowyError> { ) -> Result<Option<DocumentPayload>, FlowyError> {
let doc = request_builder() let doc = request_builder()
.get(url) .get(url)
.header(HEADER_TOKEN, token) .header(HEADER_TOKEN, token)
.protobuf(params)? .json(params)?
.option_response() .option_json_response()
.await?; .await?;
Ok(doc) Ok(doc)
@ -69,7 +69,7 @@ pub async fn reset_doc_request(token: &str, params: ResetDocumentParams, url: &s
let _ = request_builder() let _ = request_builder()
.patch(url) .patch(url)
.header(HEADER_TOKEN, token) .header(HEADER_TOKEN, token)
.protobuf(params)? .json(params)?
.send() .send()
.await?; .await?;
Ok(()) Ok(())

View File

@ -1,6 +1,6 @@
use flowy_http_model::document::DocumentPayloadPB; use flowy_http_model::document::DocumentPayload;
use flowy_http_model::folder::FolderInfo; use flowy_http_model::folder::FolderInfo;
use flowy_http_model::revision::{RepeatedRevision, Revision}; use flowy_http_model::revision::Revision;
use flowy_sync::{ use flowy_sync::{
errors::CollaborateError, errors::CollaborateError,
server_document::*, server_document::*,
@ -16,18 +16,14 @@ use std::{
// For the moment, we use memory to cache the data, it will be implemented with // For the moment, we use memory to cache the data, it will be implemented with
// other storage. Like the Firestore,Dropbox.etc. // other storage. Like the Firestore,Dropbox.etc.
pub trait RevisionCloudStorage: Send + Sync { pub trait RevisionCloudStorage: Send + Sync {
fn set_revisions(&self, repeated_revision: RepeatedRevision) -> BoxResultFuture<(), CollaborateError>; fn set_revisions(&self, revisions: Vec<Revision>) -> BoxResultFuture<(), CollaborateError>;
fn get_revisions( fn get_revisions(
&self, &self,
object_id: &str, object_id: &str,
rev_ids: Option<Vec<i64>>, rev_ids: Option<Vec<i64>>,
) -> BoxResultFuture<RepeatedRevision, CollaborateError>; ) -> BoxResultFuture<Vec<Revision>, CollaborateError>;
fn reset_object( fn reset_object(&self, object_id: &str, revisions: Vec<Revision>) -> BoxResultFuture<(), CollaborateError>;
&self,
object_id: &str,
repeated_revision: RepeatedRevision,
) -> BoxResultFuture<(), CollaborateError>;
} }
pub(crate) struct LocalDocumentCloudPersistence { pub(crate) struct LocalDocumentCloudPersistence {
@ -53,8 +49,8 @@ impl FolderCloudPersistence for LocalDocumentCloudPersistence {
let storage = self.storage.clone(); let storage = self.storage.clone();
let folder_id = folder_id.to_owned(); let folder_id = folder_id.to_owned();
Box::pin(async move { Box::pin(async move {
let repeated_revision = storage.get_revisions(&folder_id, None).await?; let revisions = storage.get_revisions(&folder_id, None).await?;
match make_folder_from_revisions_pb(&folder_id, repeated_revision)? { match make_folder_from_revisions_pb(&folder_id, revisions)? {
Some(folder_info) => Ok(folder_info), Some(folder_info) => Ok(folder_info),
None => Err(CollaborateError::record_not_found()), None => Err(CollaborateError::record_not_found()),
} }
@ -65,20 +61,20 @@ impl FolderCloudPersistence for LocalDocumentCloudPersistence {
&self, &self,
_user_id: &str, _user_id: &str,
folder_id: &str, folder_id: &str,
repeated_revision: RepeatedRevision, revisions: Vec<Revision>,
) -> BoxResultFuture<Option<FolderInfo>, CollaborateError> { ) -> BoxResultFuture<Option<FolderInfo>, CollaborateError> {
let folder_id = folder_id.to_owned(); let folder_id = folder_id.to_owned();
let storage = self.storage.clone(); let storage = self.storage.clone();
Box::pin(async move { Box::pin(async move {
let _ = storage.set_revisions(repeated_revision.clone()).await?; let _ = storage.set_revisions(revisions.clone()).await?;
make_folder_from_revisions_pb(&folder_id, repeated_revision) make_folder_from_revisions_pb(&folder_id, revisions)
}) })
} }
fn save_folder_revisions(&self, repeated_revision: RepeatedRevision) -> BoxResultFuture<(), CollaborateError> { fn save_folder_revisions(&self, revisions: Vec<Revision>) -> BoxResultFuture<(), CollaborateError> {
let storage = self.storage.clone(); let storage = self.storage.clone();
Box::pin(async move { Box::pin(async move {
let _ = storage.set_revisions(repeated_revision).await?; let _ = storage.set_revisions(revisions).await?;
Ok(()) Ok(())
}) })
} }
@ -90,28 +86,21 @@ impl FolderCloudPersistence for LocalDocumentCloudPersistence {
) -> BoxResultFuture<Vec<Revision>, CollaborateError> { ) -> BoxResultFuture<Vec<Revision>, CollaborateError> {
let folder_id = folder_id.to_owned(); let folder_id = folder_id.to_owned();
let storage = self.storage.clone(); let storage = self.storage.clone();
Box::pin(async move { Box::pin(async move { storage.get_revisions(&folder_id, rev_ids).await })
let repeated_revision = storage.get_revisions(&folder_id, rev_ids).await?;
Ok(repeated_revision.into_inner())
})
} }
fn reset_folder( fn reset_folder(&self, folder_id: &str, revisions: Vec<Revision>) -> BoxResultFuture<(), CollaborateError> {
&self,
folder_id: &str,
repeated_revision: RepeatedRevision,
) -> BoxResultFuture<(), CollaborateError> {
let storage = self.storage.clone(); let storage = self.storage.clone();
let folder_id = folder_id.to_owned(); let folder_id = folder_id.to_owned();
Box::pin(async move { Box::pin(async move {
let _ = storage.reset_object(&folder_id, repeated_revision).await?; let _ = storage.reset_object(&folder_id, revisions).await?;
Ok(()) Ok(())
}) })
} }
} }
impl DocumentCloudPersistence for LocalDocumentCloudPersistence { impl DocumentCloudPersistence for LocalDocumentCloudPersistence {
fn read_document(&self, doc_id: &str) -> BoxResultFuture<DocumentPayloadPB, CollaborateError> { fn read_document(&self, doc_id: &str) -> BoxResultFuture<DocumentPayload, CollaborateError> {
let storage = self.storage.clone(); let storage = self.storage.clone();
let doc_id = doc_id.to_owned(); let doc_id = doc_id.to_owned();
Box::pin(async move { Box::pin(async move {
@ -126,13 +115,13 @@ impl DocumentCloudPersistence for LocalDocumentCloudPersistence {
fn create_document( fn create_document(
&self, &self,
doc_id: &str, doc_id: &str,
repeated_revision: RepeatedRevision, revisions: Vec<Revision>,
) -> BoxResultFuture<Option<DocumentPayloadPB>, CollaborateError> { ) -> BoxResultFuture<Option<DocumentPayload>, CollaborateError> {
let doc_id = doc_id.to_owned(); let doc_id = doc_id.to_owned();
let storage = self.storage.clone(); let storage = self.storage.clone();
Box::pin(async move { Box::pin(async move {
let _ = storage.set_revisions(repeated_revision.clone()).await?; let _ = storage.set_revisions(revisions.clone()).await?;
make_document_from_revision_pbs(&doc_id, repeated_revision) make_document_from_revision_pbs(&doc_id, revisions)
}) })
} }
@ -143,21 +132,18 @@ impl DocumentCloudPersistence for LocalDocumentCloudPersistence {
) -> BoxResultFuture<Vec<Revision>, CollaborateError> { ) -> BoxResultFuture<Vec<Revision>, CollaborateError> {
let doc_id = doc_id.to_owned(); let doc_id = doc_id.to_owned();
let storage = self.storage.clone(); let storage = self.storage.clone();
Box::pin(async move { Box::pin(async move { storage.get_revisions(&doc_id, rev_ids).await })
let repeated_revision = storage.get_revisions(&doc_id, rev_ids).await?;
Ok(repeated_revision.into_inner())
})
} }
fn save_document_revisions(&self, repeated_revision: RepeatedRevision) -> BoxResultFuture<(), CollaborateError> { fn save_document_revisions(&self, revisions: Vec<Revision>) -> BoxResultFuture<(), CollaborateError> {
let storage = self.storage.clone(); let storage = self.storage.clone();
Box::pin(async move { Box::pin(async move {
let _ = storage.set_revisions(repeated_revision).await?; let _ = storage.set_revisions(revisions).await?;
Ok(()) Ok(())
}) })
} }
fn reset_document(&self, doc_id: &str, revisions: RepeatedRevision) -> BoxResultFuture<(), CollaborateError> { fn reset_document(&self, doc_id: &str, revisions: Vec<Revision>) -> BoxResultFuture<(), CollaborateError> {
let storage = self.storage.clone(); let storage = self.storage.clone();
let doc_id = doc_id.to_owned(); let doc_id = doc_id.to_owned();
Box::pin(async move { Box::pin(async move {
@ -170,26 +156,19 @@ impl DocumentCloudPersistence for LocalDocumentCloudPersistence {
#[derive(Default)] #[derive(Default)]
struct MemoryDocumentCloudStorage {} struct MemoryDocumentCloudStorage {}
impl RevisionCloudStorage for MemoryDocumentCloudStorage { impl RevisionCloudStorage for MemoryDocumentCloudStorage {
fn set_revisions(&self, _repeated_revision: RepeatedRevision) -> BoxResultFuture<(), CollaborateError> { fn set_revisions(&self, _revisions: Vec<Revision>) -> BoxResultFuture<(), CollaborateError> {
Box::pin(async move { Ok(()) }) Box::pin(async move { Ok(()) })
} }
fn get_revisions( fn get_revisions(
&self, &self,
_doc_id: &str, _object_id: &str,
_rev_ids: Option<Vec<i64>>, _rev_ids: Option<Vec<i64>>,
) -> BoxResultFuture<RepeatedRevision, CollaborateError> { ) -> BoxResultFuture<Vec<Revision>, CollaborateError> {
Box::pin(async move { Box::pin(async move { Ok(vec![]) })
let repeated_revisions = RepeatedRevision::default();
Ok(repeated_revisions)
})
} }
fn reset_object( fn reset_object(&self, _object_id: &str, _revisions: Vec<Revision>) -> BoxResultFuture<(), CollaborateError> {
&self,
_doc_id: &str,
_repeated_revision: RepeatedRevision,
) -> BoxResultFuture<(), CollaborateError> {
Box::pin(async move { Ok(()) }) Box::pin(async move { Ok(()) })
} }
} }

View File

@ -131,7 +131,7 @@ impl LocalWebSocketRunner {
tracing::trace!( tracing::trace!(
"[LocalFolderServer] receive: {}:{}-{:?} ", "[LocalFolderServer] receive: {}:{}-{:?} ",
client_data.object_id, client_data.object_id,
client_data.id(), client_data.rev_id,
client_data.ty, client_data.ty,
); );
let client_ws_sender = self.client_ws_sender.clone(); let client_ws_sender = self.client_ws_sender.clone();
@ -141,19 +141,12 @@ impl LocalWebSocketRunner {
channel: WSChannel::Folder, channel: WSChannel::Folder,
}); });
let ty = client_data.ty.clone(); let ty = client_data.ty.clone();
let document_client_data: ClientRevisionWSDataPB = client_data.try_into().unwrap();
match ty { match ty {
ClientRevisionWSDataType::ClientPushRev => { ClientRevisionWSDataType::ClientPushRev => {
let _ = self let _ = self.folder_manager.handle_client_revisions(user, client_data).await?;
.folder_manager
.handle_client_revisions(user, document_client_data)
.await?;
} }
ClientRevisionWSDataType::ClientPing => { ClientRevisionWSDataType::ClientPing => {
let _ = self let _ = self.folder_manager.handle_client_ping(user, client_data).await?;
.folder_manager
.handle_client_ping(user, document_client_data)
.await?;
} }
} }
Ok(()) Ok(())
@ -167,7 +160,7 @@ impl LocalWebSocketRunner {
tracing::trace!( tracing::trace!(
"[LocalDocumentServer] receive: {}:{}-{:?} ", "[LocalDocumentServer] receive: {}:{}-{:?} ",
client_data.object_id, client_data.object_id,
client_data.id(), client_data.rev_id,
client_data.ty, client_data.ty,
); );
let client_ws_sender = self.client_ws_sender.clone(); let client_ws_sender = self.client_ws_sender.clone();
@ -177,16 +170,12 @@ impl LocalWebSocketRunner {
channel: WSChannel::Document, channel: WSChannel::Document,
}); });
let ty = client_data.ty.clone(); let ty = client_data.ty.clone();
let document_client_data: ClientRevisionWSDataPB = client_data.try_into().unwrap();
match ty { match ty {
ClientRevisionWSDataType::ClientPushRev => { ClientRevisionWSDataType::ClientPushRev => {
let _ = self let _ = self.doc_manager.handle_client_revisions(user, client_data).await?;
.doc_manager
.handle_client_revisions(user, document_client_data)
.await?;
} }
ClientRevisionWSDataType::ClientPing => { ClientRevisionWSDataType::ClientPing => {
let _ = self.doc_manager.handle_client_ping(user, document_client_data).await?; let _ = self.doc_manager.handle_client_ping(user, client_data).await?;
} }
} }
Ok(()) Ok(())
@ -253,8 +242,7 @@ use flowy_folder::entities::{
view::{CreateViewParams, RepeatedViewIdPB, UpdateViewParams, ViewIdPB}, view::{CreateViewParams, RepeatedViewIdPB, UpdateViewParams, ViewIdPB},
workspace::{CreateWorkspaceParams, UpdateWorkspaceParams, WorkspaceIdPB}, workspace::{CreateWorkspaceParams, UpdateWorkspaceParams, WorkspaceIdPB},
}; };
use flowy_http_model::document::{CreateDocumentParams, DocumentIdPB, DocumentPayloadPB, ResetDocumentParams}; use flowy_http_model::document::{CreateDocumentParams, DocumentId, DocumentPayload, ResetDocumentParams};
use flowy_http_model::protobuf::ClientRevisionWSData as ClientRevisionWSDataPB;
use flowy_http_model::ws_data::{ClientRevisionWSData, ClientRevisionWSDataType}; use flowy_http_model::ws_data::{ClientRevisionWSData, ClientRevisionWSDataType};
use flowy_user::entities::{ use flowy_user::entities::{
SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfilePB, SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfilePB,
@ -414,11 +402,7 @@ impl DocumentCloudService for LocalServer {
FutureResult::new(async { Ok(()) }) FutureResult::new(async { Ok(()) })
} }
fn fetch_document( fn fetch_document(&self, _token: &str, _params: DocumentId) -> FutureResult<Option<DocumentPayload>, FlowyError> {
&self,
_token: &str,
_params: DocumentIdPB,
) -> FutureResult<Option<DocumentPayloadPB>, FlowyError> {
FutureResult::new(async { Ok(None) }) FutureResult::new(async { Ok(None) })
} }

View File

@ -88,6 +88,14 @@ impl HttpRequestBuilder {
self.bytes(body) self.bytes(body)
} }
pub fn json<T>(self, body: T) -> Result<Self, ServerError>
where
T: serde::Serialize,
{
let bytes = Bytes::from(serde_json::to_vec(&body)?);
self.bytes(bytes)
}
pub fn bytes(mut self, body: Bytes) -> Result<Self, ServerError> { pub fn bytes(mut self, body: Bytes) -> Result<Self, ServerError> {
self.body = Some(body); self.body = Some(body);
Ok(self) Ok(self)
@ -109,7 +117,8 @@ impl HttpRequestBuilder {
} }
} }
pub async fn option_response<T>(self) -> Result<Option<T>, ServerError> #[allow(dead_code)]
pub async fn option_protobuf_response<T>(self) -> Result<Option<T>, ServerError>
where where
T: TryFrom<Bytes, Error = ProtobufError>, T: TryFrom<Bytes, Error = ProtobufError>,
{ {
@ -126,6 +135,23 @@ impl HttpRequestBuilder {
} }
} }
pub async fn option_json_response<T>(self) -> Result<Option<T>, ServerError>
where
T: serde::de::DeserializeOwned + 'static,
{
let result = self.inner_send().await;
match result {
Ok(builder) => match builder.response {
None => Err(unexpected_empty_payload(&builder.url)),
Some(data) => Ok(Some(serde_json::from_slice(&data)?)),
},
Err(error) => match error.is_record_not_found() {
true => Ok(None),
false => Err(error),
},
}
}
fn token(&self) -> Option<String> { fn token(&self) -> Option<String> {
match self.headers.get(HEADER_TOKEN) { match self.headers.get(HEADER_TOKEN) {
None => None, None => None,

View File

@ -1,5 +1,5 @@
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_http_model::revision::{RevId, Revision, RevisionRange}; use flowy_http_model::revision::{Revision, RevisionRange};
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
@ -106,7 +106,7 @@ impl SyncRecord {
pub struct RevisionChangeset { pub struct RevisionChangeset {
pub object_id: String, pub object_id: String,
pub rev_id: RevId, pub rev_id: i64,
pub state: RevisionState, pub state: RevisionState,
} }

View File

@ -1,12 +1,9 @@
use crate::{RevisionMD5, RevisionManager}; use crate::{RevisionMD5, RevisionManager};
use bytes::Bytes; use bytes::Bytes;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_http_model::{ use flowy_http_model::revision::{Revision, RevisionRange};
revision::{RepeatedRevision, Revision, RevisionRange},
ws_data::ServerRevisionWSDataType,
};
use lib_infra::future::BoxResultFuture; use lib_infra::future::BoxResultFuture;
use std::{convert::TryFrom, sync::Arc}; use std::sync::Arc;
pub struct TransformOperations<Operations> { pub struct TransformOperations<Operations> {
pub client_operations: Operations, pub client_operations: Operations,
@ -36,7 +33,7 @@ where
pub trait ConflictRevisionSink: Send + Sync + 'static { pub trait ConflictRevisionSink: Send + Sync + 'static {
fn send(&self, revisions: Vec<Revision>) -> BoxResultFuture<(), FlowyError>; fn send(&self, revisions: Vec<Revision>) -> BoxResultFuture<(), FlowyError>;
fn ack(&self, rev_id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError>; fn ack(&self, rev_id: i64) -> BoxResultFuture<(), FlowyError>;
} }
pub struct ConflictController<Operations, Connection> pub struct ConflictController<Operations, Connection>
@ -75,13 +72,12 @@ where
Operations: OperationsSerializer + OperationsDeserializer<Operations> + Clone + Send + Sync, Operations: OperationsSerializer + OperationsDeserializer<Operations> + Clone + Send + Sync,
Connection: Send + Sync + 'static, Connection: Send + Sync + 'static,
{ {
pub async fn receive_bytes(&self, bytes: Bytes) -> FlowyResult<()> { pub async fn receive_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<()> {
let repeated_revision = RepeatedRevision::try_from(bytes)?; if revisions.is_empty() {
if repeated_revision.is_empty() {
return Ok(()); return Ok(());
} }
match self.handle_revision(repeated_revision).await? { match self.handle_revision(revisions).await? {
None => {} None => {}
Some(server_revision) => { Some(server_revision) => {
self.rev_sink.send(vec![server_revision]).await?; self.rev_sink.send(vec![server_revision]).await?;
@ -90,8 +86,8 @@ where
Ok(()) Ok(())
} }
pub async fn ack_revision(&self, rev_id: String, ty: ServerRevisionWSDataType) -> FlowyResult<()> { pub async fn ack_revision(&self, rev_id: i64) -> FlowyResult<()> {
let _ = self.rev_sink.ack(rev_id, ty).await?; let _ = self.rev_sink.ack(rev_id).await?;
Ok(()) Ok(())
} }
@ -101,8 +97,7 @@ where
Ok(()) Ok(())
} }
async fn handle_revision(&self, repeated_revision: RepeatedRevision) -> FlowyResult<Option<Revision>> { async fn handle_revision(&self, mut revisions: Vec<Revision>) -> FlowyResult<Option<Revision>> {
let mut revisions = repeated_revision.into_inner();
let first_revision = revisions.first().unwrap(); let first_revision = revisions.first().unwrap();
if let Some(local_revision) = self.rev_manager.get_revision(first_revision.rev_id).await { if let Some(local_revision) = self.rev_manager.get_revision(first_revision.rev_id).await {
if local_revision.md5 == first_revision.md5 { if local_revision.md5 == first_revision.md5 {

View File

@ -1,15 +1,13 @@
use crate::ConflictRevisionSink; use crate::ConflictRevisionSink;
use async_stream::stream; use async_stream::stream;
use bytes::Bytes;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_http_model::{ use flowy_http_model::revision::{Revision, RevisionRange};
revision::{RevId, Revision, RevisionRange}, use flowy_http_model::ws_data::{ClientRevisionWSData, NewDocumentUser, ServerRevisionWSData, WSRevisionPayload};
ws_data::{ClientRevisionWSData, NewDocumentUser, ServerRevisionWSData, ServerRevisionWSDataType},
};
use futures_util::{future::BoxFuture, stream::StreamExt}; use futures_util::{future::BoxFuture, stream::StreamExt};
use lib_infra::future::{BoxResultFuture, FutureResult}; use lib_infra::future::{BoxResultFuture, FutureResult};
use lib_ws::WSConnectState; use lib_ws::WSConnectState;
use std::{collections::VecDeque, convert::TryFrom, fmt::Formatter, sync::Arc}; use std::{collections::VecDeque, fmt::Formatter, sync::Arc};
use tokio::{ use tokio::{
sync::{ sync::{
broadcast, mpsc, broadcast, mpsc,
@ -21,8 +19,8 @@ use tokio::{
// The consumer consumes the messages pushed by the web socket. // The consumer consumes the messages pushed by the web socket.
pub trait RevisionWSDataStream: Send + Sync { pub trait RevisionWSDataStream: Send + Sync {
fn receive_push_revision(&self, bytes: Bytes) -> BoxResultFuture<(), FlowyError>; fn receive_push_revision(&self, revisions: Vec<Revision>) -> BoxResultFuture<(), FlowyError>;
fn receive_ack(&self, id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError>; fn receive_ack(&self, rev_id: i64) -> BoxResultFuture<(), FlowyError>;
fn receive_new_user_connect(&self, new_user: NewDocumentUser) -> BoxResultFuture<(), FlowyError>; fn receive_new_user_connect(&self, new_user: NewDocumentUser) -> BoxResultFuture<(), FlowyError>;
fn pull_revisions_in_range(&self, range: RevisionRange) -> BoxResultFuture<(), FlowyError>; fn pull_revisions_in_range(&self, range: RevisionRange) -> BoxResultFuture<(), FlowyError>;
} }
@ -214,26 +212,22 @@ impl RevisionWSStream {
} }
async fn handle_message(&self, msg: ServerRevisionWSData) -> FlowyResult<()> { async fn handle_message(&self, msg: ServerRevisionWSData) -> FlowyResult<()> {
let ServerRevisionWSData { object_id, ty, data } = msg; let ServerRevisionWSData { object_id, payload } = msg;
let bytes = Bytes::from(data); match payload {
match ty { WSRevisionPayload::ServerPushRev { revisions } => {
ServerRevisionWSDataType::ServerPushRev => { tracing::trace!("[{}]: new push revision: {}", self, object_id);
tracing::trace!("[{}]: new push revision: {}:{:?}", self, object_id, ty); let _ = self.consumer.receive_push_revision(revisions).await?;
let _ = self.consumer.receive_push_revision(bytes).await?;
} }
ServerRevisionWSDataType::ServerPullRev => { WSRevisionPayload::ServerPullRev { range } => {
let range = RevisionRange::try_from(bytes)?; tracing::trace!("[{}]: new pull: {}:{:?}", self, object_id, range);
tracing::trace!("[{}]: new pull: {}:{}-{:?}", self, object_id, range, ty);
let _ = self.consumer.pull_revisions_in_range(range).await?; let _ = self.consumer.pull_revisions_in_range(range).await?;
} }
ServerRevisionWSDataType::ServerAck => { WSRevisionPayload::ServerAck { rev_id } => {
let rev_id = RevId::try_from(bytes).unwrap().value; tracing::trace!("[{}]: new ack: {}:{}", self, object_id, rev_id);
tracing::trace!("[{}]: new ack: {}:{}-{:?}", self, object_id, rev_id, ty); let _ = self.consumer.receive_ack(rev_id).await;
let _ = self.consumer.receive_ack(rev_id.to_string(), ty).await;
} }
ServerRevisionWSDataType::UserConnect => { WSRevisionPayload::UserConnect { user } => {
let new_user = NewDocumentUser::try_from(bytes)?; let _ = self.consumer.receive_new_user_connect(user).await;
let _ = self.consumer.receive_new_user_connect(new_user).await;
} }
} }
Ok(()) Ok(())
@ -309,7 +303,7 @@ impl RevisionWSSink {
Ok(()) Ok(())
} }
Some(data) => { Some(data) => {
tracing::trace!("[{}]: send {}:{}-{:?}", self, data.object_id, data.id(), data.ty); tracing::trace!("[{}]: send {}:{}-{:?}", self, data.object_id, data.rev_id, data.ty);
self.rev_web_socket.send(data).await self.rev_web_socket.send(data).await
} }
} }
@ -397,18 +391,17 @@ impl WSDataProvider {
data data
} }
pub async fn ack_data(&self, id: String, _ty: ServerRevisionWSDataType) -> FlowyResult<()> { pub async fn ack_data(&self, rev_id: i64) -> FlowyResult<()> {
let source = self.current_source.read().await.clone(); let source = self.current_source.read().await.clone();
match source { match source {
Source::Custom => { Source::Custom => {
let should_pop = match self.rev_ws_data_list.read().await.front() { let should_pop = match self.rev_ws_data_list.read().await.front() {
None => false, None => false,
Some(val) => { Some(val) => {
let expected_id = val.id(); if val.rev_id == rev_id {
if expected_id == id {
true true
} else { } else {
tracing::error!("The front element's {} is not equal to the {}", expected_id, id); tracing::error!("The front element's {} is not equal to the {}", val.rev_id, rev_id);
false false
} }
} }
@ -419,9 +412,6 @@ impl WSDataProvider {
Ok(()) Ok(())
} }
Source::Revision => { Source::Revision => {
let rev_id = id.parse::<i64>().map_err(|e| {
FlowyError::internal().context(format!("Parse {} rev_id from {} failed. {}", self.object_id, id, e))
})?;
let _ = self.data_source.ack_revision(rev_id).await?; let _ = self.data_source.ack_revision(rev_id).await?;
Ok::<(), FlowyError>(()) Ok::<(), FlowyError>(())
} }
@ -439,8 +429,8 @@ impl ConflictRevisionSink for Arc<WSDataProvider> {
}) })
} }
fn ack(&self, rev_id: String, ty: ServerRevisionWSDataType) -> BoxResultFuture<(), FlowyError> { fn ack(&self, rev_id: i64) -> BoxResultFuture<(), FlowyError> {
let sink = self.clone(); let sink = self.clone();
Box::pin(async move { sink.ack_data(rev_id, ty).await }) Box::pin(async move { sink.ack_data(rev_id).await })
} }
} }

View File

@ -205,7 +205,7 @@ impl RevisionDiskCache<RevisionConnectionMock> for RevisionDiskCacheMock {
.records .records
.write() .write()
.iter_mut() .iter_mut()
.find(|record| record.revision.rev_id == *changeset.rev_id.as_ref()) .find(|record| record.revision.rev_id == changeset.rev_id)
{ {
record.state = changeset.state; record.state = changeset.state;
} }

View File

@ -6,12 +6,12 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
lib-ot = { path = "../lib-ot" } lib-ot = { path = "../../../shared-lib/lib-ot" }
lib-infra = { path = "../lib-infra" } lib-infra = { path = "../../../shared-lib/lib-infra" }
flowy-derive = { path = "../flowy-derive" } flowy-derive = { path = "../flowy-derive" }
folder-rev-model = { path = "../folder-rev-model" } folder-rev-model = { path = "../../../shared-lib/folder-rev-model" }
grid-rev-model = { path = "../grid-rev-model" } grid-rev-model = { path = "../../../shared-lib/grid-rev-model" }
flowy-http-model= { path = "../flowy-http-model" } flowy-http-model = { path = "../../../shared-lib/flowy-http-model" }
protobuf = {version = "2.18.0"} protobuf = {version = "2.18.0"}
bytes = "1.0" bytes = "1.0"
log = "0.4.14" log = "0.4.14"

Some files were not shown because too many files have changed in this diff Show More