feat: config grid editor

This commit is contained in:
appflowy 2022-03-04 21:26:32 +08:00
parent 49807a0b57
commit 9125db7ef0
13 changed files with 222 additions and 128 deletions

View File

@ -1049,12 +1049,12 @@ dependencies = [
name = "flowy-grid"
version = "0.1.0"
dependencies = [
"async-trait",
"bytes",
"chrono",
"dart-notify",
"diesel",
"flowy-collaboration",
"flowy-database",
"flowy-derive",
"flowy-error",
"flowy-grid-data-model",

View File

@ -1 +1,2 @@
-- This file should undo anything in `up.sql`
-- This file should undo anything in `up.sql`
DROP TABLE rev_table;

View File

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE kv_table;

View File

@ -0,0 +1,5 @@
-- Your SQL goes here
CREATE TABLE kv_table (
key TEXT NOT NULL PRIMARY KEY,
value BLOB NOT NULL DEFAULT (x'')
);

View File

@ -6,12 +6,10 @@ pub mod kv;
use lib_sqlite::PoolConfig;
pub use lib_sqlite::{ConnectionPool, DBConnection, Database};
pub mod schema;
#[macro_use]
pub mod macros;
#[macro_use]
extern crate diesel;
#[macro_use]
@ -20,11 +18,11 @@ extern crate diesel_derives;
extern crate diesel_migrations;
pub type Error = diesel::result::Error;
pub mod prelude {
pub use diesel::{query_dsl::*, BelongingToDsl, ExpressionMethods, RunQueryDsl};
pub use super::UserDatabaseConnection;
pub use crate::*;
pub use diesel::SqliteConnection;
pub use diesel::{query_dsl::*, BelongingToDsl, ExpressionMethods, RunQueryDsl};
}
embed_migrations!("../flowy-database/migrations/");

View File

@ -21,6 +21,13 @@ table! {
}
}
table! {
kv_table (key) {
key -> Text,
value -> Binary,
}
}
table! {
rev_table (id) {
id -> Integer,
@ -84,6 +91,7 @@ table! {
allow_tables_to_appear_in_same_query!(
app_table,
doc_table,
kv_table,
rev_table,
trash_table,
user_table,

View File

@ -10,12 +10,13 @@ lib-dispatch = { path = "../lib-dispatch" }
dart-notify = { path = "../dart-notify" }
lib-sqlite = { path = "../lib-sqlite" }
flowy-sync = { path = "../flowy-sync" }
flowy-error = { path = "../flowy-error"}
flowy-error = { path = "../flowy-error", features = ["db"]}
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
lib-ot = { path = "../../../shared-lib/lib-ot" }
lib-infra = { path = "../../../shared-lib/lib-infra" }
flowy-grid-data-model = { path = "../../../shared-lib/flowy-grid-data-model" }
flowy-collaboration = { path = "../../../shared-lib/flowy-collaboration" }
flowy-database = { path = "../flowy-database" }
strum = "0.21"
strum_macros = "0.21"
@ -27,8 +28,8 @@ lazy_static = "1.4.0"
chrono = "0.4.19"
uuid = { version = "0.8", features = ["serde", "v4"] }
bytes = { version = "1.0" }
async-trait = "0.1.52"
diesel = {version = "1.4.8", features = ["sqlite"]}
#diesel_derives = {version = "1.4.1", features = ["sqlite"]}
parking_lot = "0.11"

View File

@ -1,9 +1,9 @@
use crate::services::row_kv::{RowKVPersistence, RowKVTransaction};
use crate::services::kv_persistence::{GridKVPersistence, KVTransaction};
use flowy_collaboration::client_grid::{GridChange, GridPad};
use flowy_collaboration::entities::revision::Revision;
use flowy_collaboration::util::make_delta_from_revisions;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{GridId, RawRow};
use flowy_grid_data_model::entities::{Field, GridId, RawRow};
use flowy_sync::{
RevisionCloudService, RevisionCompact, RevisionManager, RevisionObjectBuilder, RevisionPersistence,
RevisionWebSocket, RevisionWebSocketManager,
@ -19,7 +19,7 @@ pub struct ClientGridEditor {
grid_id: GridId,
grid: Arc<RwLock<GridPad>>,
rev_manager: Arc<RevisionManager>,
kv: Arc<RowKVPersistence>,
kv: Arc<GridKVPersistence>,
}
impl ClientGridEditor {
@ -39,7 +39,7 @@ impl ClientGridEditor {
rev_manager.load::<GridPadBuilder, GridRevisionCompact>(cloud).await?,
));
let rev_manager = Arc::new(rev_manager);
let kv = Arc::new(RowKVPersistence::new(pool));
let kv = Arc::new(GridKVPersistence::new(pool));
let user_id = user_id.to_owned();
let grid_id = grid_id.to_owned();
@ -53,18 +53,30 @@ impl ClientGridEditor {
}
pub async fn create_row(&self, row: RawRow) -> FlowyResult<()> {
let _ = self
.modify(|grid| {
let change = grid.create_row(&row)?;
Ok(change)
})
.await?;
let _ = self.modify(|grid| Ok(grid.create_row(&row)?)).await?;
let _ = self.kv.set(row)?;
Ok(())
}
pub async fn modify<F>(&self, f: F) -> FlowyResult<()>
pub async fn delete_rows(&self, ids: Vec<String>) -> FlowyResult<()> {
let _ = self.modify(|grid| Ok(grid.delete_rows(&ids)?)).await?;
// let _ = self.kv.batch_delete(ids)?;
Ok(())
}
pub async fn create_field(&mut self, field: Field) -> FlowyResult<()> {
let _ = self.modify(|grid| Ok(grid.create_field(&field)?)).await?;
let _ = self.kv.set(field)?;
Ok(())
}
pub async fn delete_field(&mut self, field_id: &str) -> FlowyResult<()> {
let _ = self.modify(|grid| Ok(grid.delete_field(field_id)?)).await?;
// let _ = self.kv.remove(field_id)?;
Ok(())
}
async fn modify<F>(&self, f: F) -> FlowyResult<()>
where
F: FnOnce(&mut GridPad) -> FlowyResult<Option<GridChange>>,
{

View File

@ -0,0 +1,167 @@
use ::diesel::{query_dsl::*, ExpressionMethods};
use bytes::Bytes;
use diesel::SqliteConnection;
use flowy_database::{
prelude::*,
schema::{kv_table, kv_table::dsl},
};
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::{Field, RawRow};
use lib_infra::future::{BoxResultFuture, FutureResult};
use lib_sqlite::{ConnectionManager, ConnectionPool};
use std::sync::Arc;
#[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
#[table_name = "kv_table"]
#[primary_key(key)]
pub struct KeyValue {
key: String,
value: Vec<u8>,
}
pub trait KVTransaction {
fn get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, key: &str) -> FlowyResult<Option<T>>;
fn set<T: Into<KeyValue>>(&self, value: T) -> FlowyResult<()>;
fn remove(&self, key: &str) -> FlowyResult<()>;
fn batch_get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, keys: Vec<String>) -> FlowyResult<Vec<T>>;
fn batch_set<T: Into<KeyValue>>(&self, values: Vec<T>) -> FlowyResult<()>;
fn batch_remove(&self, keys: Vec<String>) -> FlowyResult<()>;
}
pub struct GridKVPersistence {
pool: Arc<ConnectionPool>,
}
impl GridKVPersistence {
pub fn new(pool: Arc<ConnectionPool>) -> Self {
Self { pool }
}
pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
where
F: for<'a> FnOnce(SqliteTransaction<'a>) -> FlowyResult<O>,
{
let conn = self.pool.get()?;
conn.immediate_transaction::<_, FlowyError, _>(|| {
let sql_transaction = SqliteTransaction { conn: &conn };
f(sql_transaction)
})
}
}
impl KVTransaction for GridKVPersistence {
fn get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, key: &str) -> FlowyResult<Option<T>> {
self.begin_transaction(|transaction| transaction.get(key))
}
fn set<T: Into<KeyValue>>(&self, value: T) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.set(value))
}
fn remove(&self, key: &str) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.remove(key))
}
fn batch_get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, keys: Vec<String>) -> FlowyResult<Vec<T>> {
self.begin_transaction(|transaction| transaction.batch_get(keys))
}
fn batch_set<T: Into<KeyValue>>(&self, values: Vec<T>) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.batch_set(values))
}
fn batch_remove(&self, keys: Vec<String>) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.batch_remove(keys))
}
}
pub struct SqliteTransaction<'a> {
conn: &'a SqliteConnection,
}
impl<'a> KVTransaction for SqliteTransaction<'a> {
fn get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, key: &str) -> FlowyResult<Option<T>> {
let item = dsl::kv_table
.filter(kv_table::key.eq(key))
.first::<KeyValue>(self.conn)?;
let value: T = item.try_into()?;
Ok(Some(value))
}
fn set<T: Into<KeyValue>>(&self, value: T) -> FlowyResult<()> {
let item: KeyValue = value.into();
let _ = diesel::replace_into(kv_table::table).values(&item).execute(self.conn)?;
Ok(())
}
fn remove(&self, key: &str) -> FlowyResult<()> {
let sql = dsl::kv_table.filter(kv_table::key.eq(key));
let _ = diesel::delete(sql).execute(self.conn)?;
Ok(())
}
fn batch_get<T: TryFrom<KeyValue, Error = FlowyError>>(&self, keys: Vec<String>) -> FlowyResult<Vec<T>> {
let items = dsl::kv_table
.filter(kv_table::key.eq_any(&keys))
.load::<KeyValue>(self.conn)?;
let mut values = vec![];
for item in items {
let value: T = item.try_into()?;
values.push(value);
}
Ok(values)
}
fn batch_set<T: Into<KeyValue>>(&self, values: Vec<T>) -> FlowyResult<()> {
let items = values.into_iter().map(|value| value.into()).collect::<Vec<KeyValue>>();
let _ = diesel::replace_into(kv_table::table)
.values(&items)
.execute(self.conn)?;
Ok(())
}
fn batch_remove(&self, keys: Vec<String>) -> FlowyResult<()> {
let sql = dsl::kv_table.filter(kv_table::key.eq_any(keys));
let _ = diesel::delete(sql).execute(self.conn)?;
Ok(())
}
}
impl std::convert::From<RawRow> for KeyValue {
fn from(row: RawRow) -> Self {
let key = row.id.clone();
let bytes: Bytes = row.try_into().unwrap();
let value = bytes.to_vec();
KeyValue { key, value }
}
}
impl std::convert::TryInto<RawRow> for KeyValue {
type Error = FlowyError;
fn try_into(self) -> Result<RawRow, Self::Error> {
let bytes = Bytes::from(self.value);
RawRow::try_from(bytes)
.map_err(|e| FlowyError::internal().context(format!("Deserialize into raw row failed: {:?}", e)))
}
}
impl std::convert::From<Field> for KeyValue {
fn from(field: Field) -> Self {
let key = field.id.clone();
let bytes: Bytes = field.try_into().unwrap();
let value = bytes.to_vec();
KeyValue { key, value }
}
}
impl std::convert::TryInto<Field> for KeyValue {
type Error = FlowyError;
fn try_into(self) -> Result<Field, Self::Error> {
let bytes = Bytes::from(self.value);
Field::try_from(bytes)
.map_err(|e| FlowyError::internal().context(format!("Deserialize into field failed: {:?}", e)))
}
}

View File

@ -3,6 +3,6 @@ mod util;
pub mod cell_data;
pub mod grid_editor;
mod row_kv;
mod kv_persistence;
pub use stringify::*;

View File

@ -1,95 +0,0 @@
use async_trait::async_trait;
use diesel::SqliteConnection;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::entities::RawRow;
use lib_infra::future::{BoxResultFuture, FutureResult};
use lib_sqlite::{ConnectionManager, ConnectionPool};
use std::sync::Arc;
pub trait RowKVTransaction {
fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>>;
fn set(&self, row: RawRow) -> FlowyResult<()>;
fn remove(&self, row_id: &str) -> FlowyResult<()>;
fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()>;
fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()>;
fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()>;
}
pub struct RowKVPersistence {
pool: Arc<ConnectionPool>,
}
impl RowKVPersistence {
pub fn new(pool: Arc<ConnectionPool>) -> Self {
Self { pool }
}
pub fn begin_transaction<F, O>(&self, f: F) -> FlowyResult<O>
where
F: for<'a> FnOnce(Box<dyn RowKVTransaction + 'a>) -> FlowyResult<O>,
{
let conn = self.pool.get()?;
conn.immediate_transaction::<_, FlowyError, _>(|| {
let sql_transaction = SqliteTransaction { conn: &conn };
f(Box::new(sql_transaction))
})
}
}
impl RowKVTransaction for RowKVPersistence {
fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>> {
self.begin_transaction(|transaction| transaction.get(row_id))
}
fn set(&self, row: RawRow) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.set(row))
}
fn remove(&self, row_id: &str) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.remove(row_id))
}
fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.batch_get(ids))
}
fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.batch_set(rows))
}
fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()> {
self.begin_transaction(|transaction| transaction.batch_delete(ids))
}
}
pub struct SqliteTransaction<'a> {
conn: &'a SqliteConnection,
}
#[async_trait]
impl<'a> RowKVTransaction for SqliteTransaction<'a> {
fn get(&self, row_id: &str) -> FlowyResult<Option<RawRow>> {
todo!()
}
fn set(&self, row: RawRow) -> FlowyResult<()> {
todo!()
}
fn remove(&self, row_id: &str) -> FlowyResult<()> {
todo!()
}
fn batch_get(&self, ids: Vec<String>) -> FlowyResult<()> {
todo!()
}
fn batch_set(&self, rows: Vec<RawRow>) -> FlowyResult<()> {
todo!()
}
fn batch_delete(&self, ids: Vec<String>) -> FlowyResult<()> {
todo!()
}
}

View File

@ -2,8 +2,8 @@ use crate::{
configuration::*,
request::{HttpRequestBuilder, ResponseMiddleware},
};
use flowy_block::BlockCloudService;
use flowy_collaboration::entities::document_info::{BlockId, BlockInfo, CreateBlockParams, ResetBlockParams};
use flowy_document::BlockCloudService;
use flowy_error::FlowyError;
use http_flowy::response::FlowyResponse;
use lazy_static::lazy_static;

View File

@ -54,16 +54,11 @@ impl GridPad {
})
}
pub fn delete_row(&mut self, row_id: &str) -> CollaborateResult<Option<GridChange>> {
self.modify_grid(
|grid| match grid.row_orders.iter().position(|row_order| row_order.row_id == row_id) {
None => Ok(None),
Some(index) => {
grid.row_orders.remove(index);
Ok(Some(()))
}
},
)
pub fn delete_rows(&mut self, row_ids: &Vec<String>) -> CollaborateResult<Option<GridChange>> {
self.modify_grid(|grid| {
grid.row_orders.retain(|row_order| !row_ids.contains(&row_order.row_id));
Ok(Some(()))
})
}
pub fn delete_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChange>> {