refactor: row builder

This commit is contained in:
appflowy 2022-07-12 15:49:14 +08:00
parent 6020459f76
commit 284755eccf
11 changed files with 288 additions and 295 deletions

View File

@ -5,6 +5,7 @@ use crate::services::field::select_option::*;
use crate::services::field::{
default_type_option_builder_from_type, type_option_builder_from_json_str, DateChangesetParams, DateChangesetPayload,
};
use crate::services::row::make_row_from_row_rev;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_grid_data_model::revision::FieldRevision;
use flowy_sync::entities::grid::{FieldChangesetParams, GridSettingChangesetParams};
@ -229,10 +230,12 @@ pub(crate) async fn get_row_handler(
) -> DataResult<OptionalRow, FlowyError> {
let params: GridRowId = data.into_inner().try_into()?;
let editor = manager.get_grid_editor(&params.grid_id)?;
let row = OptionalRow {
row: editor.get_row(&params.row_id).await?,
};
data_result(row)
let row = editor
.get_row_rev(&params.row_id)
.await?
.and_then(make_row_from_row_rev);
data_result(OptionalRow { row })
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]

View File

@ -26,8 +26,12 @@ pub trait CellDataOperation<D, C> {
/// SelectOptionCellChangeset,DateCellChangeset. etc.
fn apply_changeset(&self, changeset: CellDataChangeset<C>, cell_rev: Option<CellRevision>) -> FlowyResult<String>;
}
/// The changeset will be deserialized into specific data base on the FieldType.
/// For example, it's String on FieldType::RichText, and SelectOptionChangeset on FieldType::SingleSelect
/// changeset: It will be deserialized into specific data base on the FieldType.
/// For example,
/// FieldType::RichText => String
/// FieldType::SingleSelect => SelectOptionChangeset
///
/// cell_rev: It will be None if the cell does not contain any data.
pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
changeset: C,
cell_rev: Option<CellRevision>,
@ -114,14 +118,16 @@ pub fn try_decode_cell_data(
}
}
/// If the cell data is not String type, it should impl this trait.
/// Deserialize the String into cell specific data type.
pub trait FromCellString {
fn from_cell_str(s: &str) -> FlowyResult<Self>
where
Self: Sized;
}
/// CellData is a helper struct. String will be parser into Option<T> only if the T impl the FromCellString trait.
pub struct CellData<T>(pub Option<T>);
impl<T> CellData<T> {
pub fn try_into_inner(self) -> FlowyResult<T> {
match self.0 {
@ -158,7 +164,8 @@ impl std::convert::From<CellData<String>> for String {
}
}
// CellChangeset
/// If the changeset applying to the cell is not String type, it should impl this trait.
/// Deserialize the string into cell specific changeset.
pub trait FromCellChangeset {
fn from_changeset(changeset: String) -> FlowyResult<Self>
where

View File

@ -8,8 +8,7 @@ use crate::services::field::{default_type_option_builder_from_type, type_option_
use crate::services::filter::{GridFilterChangeset, GridFilterService};
use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::row::{
make_grid_blocks, make_row_from_row_rev, make_row_rev_from_context, make_rows_from_row_revs,
CreateRowRevisionBuilder, CreateRowRevisionPayload, GridBlockSnapshot,
make_grid_blocks, make_row_from_row_rev, make_rows_from_row_revs, GridBlockSnapshot, RowRevisionBuilder,
};
use crate::services::setting::make_grid_setting;
use bytes::Bytes;
@ -274,8 +273,7 @@ impl GridRevisionEditor {
let block_id = self.block_id().await?;
// insert empty row below the row whose id is upper_row_id
let row_rev_ctx = CreateRowRevisionBuilder::new(&field_revs).build();
let row_rev = make_row_rev_from_context(&block_id, row_rev_ctx);
let row_rev = RowRevisionBuilder::new(&field_revs).build(&block_id);
let row_order = RowInfo::from(&row_rev);
// insert the row
@ -287,12 +285,11 @@ impl GridRevisionEditor {
Ok(row_order)
}
pub async fn insert_rows(&self, contexts: Vec<CreateRowRevisionPayload>) -> FlowyResult<Vec<RowInfo>> {
pub async fn insert_rows(&self, row_revs: Vec<RowRevision>) -> FlowyResult<Vec<RowInfo>> {
let block_id = self.block_id().await?;
let mut rows_by_block_id: HashMap<String, Vec<RowRevision>> = HashMap::new();
let mut row_orders = vec![];
for ctx in contexts {
let row_rev = make_row_rev_from_context(&block_id, ctx);
for row_rev in row_revs {
row_orders.push(RowInfo::from(&row_rev));
rows_by_block_id
.entry(block_id.clone())
@ -307,10 +304,7 @@ impl GridRevisionEditor {
}
pub async fn update_row(&self, changeset: RowMetaChangeset) -> FlowyResult<()> {
let field_revs = self.get_field_revs(None).await?;
self.block_manager
.update_row(changeset, |row_rev| make_row_from_row_rev(&field_revs, row_rev))
.await
self.block_manager.update_row(changeset, make_row_from_row_rev).await
}
pub async fn get_rows(&self, block_id: &str) -> FlowyResult<RepeatedRow> {
@ -322,26 +316,20 @@ impl GridRevisionEditor {
debug_assert_eq!(grid_block_snapshot.len(), 1);
if grid_block_snapshot.len() == 1 {
let snapshot = grid_block_snapshot.pop().unwrap();
let field_revs = self.get_field_revs(None).await?;
let rows = make_rows_from_row_revs(&field_revs, &snapshot.row_revs);
let rows = make_rows_from_row_revs(&snapshot.row_revs);
Ok(rows.into())
} else {
Ok(vec![].into())
}
}
pub async fn get_row(&self, row_id: &str) -> FlowyResult<Option<Row>> {
pub async fn get_row_rev(&self, row_id: &str) -> FlowyResult<Option<Arc<RowRevision>>> {
match self.block_manager.get_row_rev(row_id).await? {
None => Ok(None),
Some(row_rev) => {
let field_revs = self.get_field_revs(None).await?;
let row_revs = vec![row_rev];
let mut rows = make_rows_from_row_revs(&field_revs, &row_revs);
debug_assert!(rows.len() == 1);
Ok(rows.pop())
}
Some(row_rev) => Ok(Some(row_rev)),
}
}
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
let _ = self.block_manager.delete_row(row_id).await?;
Ok(())
@ -360,6 +348,10 @@ impl GridRevisionEditor {
Some(Cell::new(&params.field_id, data))
}
pub async fn get_cell_display(&self, _params: &CellIdentifier) -> Option<String> {
todo!()
}
pub async fn get_cell_rev(&self, row_id: &str, field_id: &str) -> FlowyResult<Option<CellRevision>> {
let row_rev = self.block_manager.get_row_rev(row_id).await?;
match row_rev {
@ -395,7 +387,6 @@ impl GridRevisionEditor {
let cell_rev = self.get_cell_rev(&row_id, &field_id).await?;
// Update the changeset.data property with the return value.
content = Some(apply_cell_data_changeset(content.unwrap(), cell_rev, field_rev)?);
let field_revs = self.get_field_revs(None).await?;
let cell_changeset = CellChangeset {
grid_id,
row_id,
@ -404,7 +395,7 @@ impl GridRevisionEditor {
};
let _ = self
.block_manager
.update_cell(cell_changeset, |row_rev| make_row_from_row_rev(&field_revs, row_rev))
.update_cell(cell_changeset, make_row_from_row_rev)
.await?;
Ok(())
}

View File

@ -6,12 +6,12 @@ use indexmap::IndexMap;
use std::collections::HashMap;
use std::sync::Arc;
pub struct CreateRowRevisionBuilder<'a> {
pub struct RowRevisionBuilder<'a> {
field_rev_map: HashMap<&'a String, &'a Arc<FieldRevision>>,
payload: CreateRowRevisionPayload,
}
impl<'a> CreateRowRevisionBuilder<'a> {
impl<'a> RowRevisionBuilder<'a> {
pub fn new(fields: &'a [Arc<FieldRevision>]) -> Self {
let field_rev_map = fields
.iter()
@ -28,10 +28,10 @@ impl<'a> CreateRowRevisionBuilder<'a> {
Self { field_rev_map, payload }
}
pub fn add_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> {
pub fn insert_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> {
match self.field_rev_map.get(&field_id.to_owned()) {
None => {
let msg = format!("Invalid field_id: {}", field_id);
let msg = format!("Can't find the field with id: {}", field_id);
Err(FlowyError::internal().context(msg))
}
Some(field_rev) => {
@ -43,7 +43,7 @@ impl<'a> CreateRowRevisionBuilder<'a> {
}
}
pub fn add_select_option_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> {
pub fn insert_select_option_cell(&mut self, field_id: &str, data: String) -> FlowyResult<()> {
match self.field_rev_map.get(&field_id.to_owned()) {
None => {
let msg = format!("Invalid field_id: {}", field_id);
@ -71,18 +71,14 @@ impl<'a> CreateRowRevisionBuilder<'a> {
self
}
pub fn build(self) -> CreateRowRevisionPayload {
self.payload
}
}
pub fn make_row_rev_from_context(block_id: &str, payload: CreateRowRevisionPayload) -> RowRevision {
RowRevision {
id: payload.row_id,
block_id: block_id.to_owned(),
cells: payload.cell_by_field_id,
height: payload.height,
visibility: payload.visibility,
pub fn build(self, block_id: &str) -> RowRevision {
RowRevision {
id: self.payload.row_id,
block_id: block_id.to_owned(),
cells: self.payload.cell_by_field_id,
height: self.payload.height,
visibility: self.payload.visibility,
}
}
}

View File

@ -1,6 +1,6 @@
use crate::entities::{GridBlock, RepeatedGridBlock, Row, RowInfo};
use flowy_error::FlowyResult;
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
use flowy_grid_data_model::revision::RowRevision;
use std::collections::HashMap;
use std::sync::Arc;
@ -39,28 +39,14 @@ pub(crate) fn make_row_orders_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Ve
row_revs.iter().map(RowInfo::from).collect::<Vec<_>>()
}
pub(crate) fn make_row_from_row_rev(fields: &[Arc<FieldRevision>], row_rev: Arc<RowRevision>) -> Option<Row> {
make_rows_from_row_revs(fields, &[row_rev]).pop()
pub(crate) fn make_row_from_row_rev(row_rev: Arc<RowRevision>) -> Option<Row> {
make_rows_from_row_revs(&[row_rev]).pop()
}
pub(crate) fn make_rows_from_row_revs(_fields: &[Arc<FieldRevision>], row_revs: &[Arc<RowRevision>]) -> Vec<Row> {
// let field_rev_map = fields
// .iter()
// .map(|field_rev| (&field_rev.id, field_rev))
// .collect::<HashMap<&String, &FieldRevision>>();
let make_row = |row_rev: &Arc<RowRevision>| {
// let cell_by_field_id = row_rev
// .cells
// .clone()
// .into_iter()
// .flat_map(|(field_id, cell_rev)| make_cell_by_field_id(&field_rev_map, field_id, cell_rev))
// .collect::<HashMap<String, Cell>>();
Row {
id: row_rev.id.clone(),
height: row_rev.height,
}
pub(crate) fn make_rows_from_row_revs(row_revs: &[Arc<RowRevision>]) -> Vec<Row> {
let make_row = |row_rev: &Arc<RowRevision>| Row {
id: row_rev.id.clone(),
height: row_rev.height,
};
row_revs.iter().map(make_row).collect::<Vec<_>>()

View File

@ -4,29 +4,29 @@ use flowy_grid_data_model::revision::BuildGridContext;
use flowy_sync::client_grid::GridBuilder;
pub fn make_default_grid() -> BuildGridContext {
let mut grid_builder = GridBuilder::new();
// text
let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
.name("Name")
.visibility(true)
.primary(true)
.build();
grid_builder.add_field(text_field);
// single select
let single_select = SingleSelectTypeOptionBuilder::default();
let single_select_field = FieldBuilder::new(single_select).name("Type").visibility(true).build();
grid_builder.add_field(single_select_field);
// checkbox
let checkbox_field = FieldBuilder::from_field_type(&FieldType::Checkbox)
.name("Done")
.visibility(true)
.build();
grid_builder.add_field(checkbox_field);
GridBuilder::default()
.add_field(text_field)
.add_field(single_select_field)
.add_field(checkbox_field)
.add_empty_row()
.add_empty_row()
.add_empty_row()
.build()
grid_builder.add_empty_row();
grid_builder.add_empty_row();
grid_builder.add_empty_row();
grid_builder.build()
}

View File

@ -1,13 +1,5 @@
use crate::grid::block_test::script::GridRowTest;
use crate::grid::block_test::script::RowScript::*;
use crate::grid::block_test::util::GridRowTestBuilder;
use chrono::NaiveDateTime;
use flowy_grid::entities::FieldType;
use flowy_grid::services::cell::decode_any_cell_data;
use flowy_grid::services::field::select_option::SELECTION_IDS_SEPARATOR;
use flowy_grid::services::field::{DateCellData, MultiSelectTypeOption, SingleSelectTypeOption};
use crate::grid::field_test::util::make_date_cell_string;
use flowy_grid_data_model::revision::RowMetaChangeset;
#[tokio::test]
@ -18,7 +10,7 @@ async fn grid_create_row_count_test() {
CreateEmptyRow,
CreateEmptyRow,
CreateRow {
payload: GridRowTestBuilder::new(&test).build(),
row_rev: test.row_builder().build(),
},
AssertRowCount(6),
];
@ -28,15 +20,15 @@ async fn grid_create_row_count_test() {
#[tokio::test]
async fn grid_update_row() {
let mut test = GridRowTest::new().await;
let payload = GridRowTestBuilder::new(&test).build();
let row_rev = test.row_builder().build();
let changeset = RowMetaChangeset {
row_id: payload.row_id.clone(),
row_id: row_rev.id.clone(),
height: None,
visibility: None,
cell_by_field_id: Default::default(),
};
let scripts = vec![AssertRowCount(3), CreateRow { payload }, UpdateRow { changeset }];
let scripts = vec![AssertRowCount(3), CreateRow { row_rev }, UpdateRow { changeset }];
test.run_scripts(scripts).await;
let expected_row = test.last_row().unwrap();
@ -47,13 +39,13 @@ async fn grid_update_row() {
#[tokio::test]
async fn grid_delete_row() {
let mut test = GridRowTest::new().await;
let payload1 = GridRowTestBuilder::new(&test).build();
let payload2 = GridRowTestBuilder::new(&test).build();
let row_ids = vec![payload1.row_id.clone(), payload2.row_id.clone()];
let row_1 = test.row_builder().build();
let row_2 = test.row_builder().build();
let row_ids = vec![row_1.id.clone(), row_2.id.clone()];
let scripts = vec![
AssertRowCount(3),
CreateRow { payload: payload1 },
CreateRow { payload: payload2 },
CreateRow { row_rev: row_1 },
CreateRow { row_rev: row_2 },
AssertBlockCount(1),
AssertBlock {
block_index: 0,
@ -73,78 +65,17 @@ async fn grid_delete_row() {
#[tokio::test]
async fn grid_row_add_cells_test() {
let mut test = GridRowTest::new().await;
let mut builder = test.builder();
for field in test.field_revs() {
let field_type: FieldType = field.field_type_rev.into();
match field_type {
FieldType::RichText => {
builder.add_cell(&field.id, "hello world".to_owned()).unwrap();
}
FieldType::Number => {
builder.add_cell(&field.id, "18,443".to_owned()).unwrap();
}
FieldType::DateTime => {
builder
.add_cell(&field.id, make_date_cell_string("1647251762"))
.unwrap();
}
FieldType::SingleSelect => {
let type_option = SingleSelectTypeOption::from(field);
let option = type_option.options.first().unwrap();
builder.add_select_option_cell(&field.id, option.id.clone()).unwrap();
}
FieldType::MultiSelect => {
let type_option = MultiSelectTypeOption::from(field);
let ops_ids = type_option
.options
.iter()
.map(|option| option.id.clone())
.collect::<Vec<_>>()
.join(SELECTION_IDS_SEPARATOR);
builder.add_select_option_cell(&field.id, ops_ids).unwrap();
}
FieldType::Checkbox => {
builder.add_cell(&field.id, "false".to_string()).unwrap();
}
FieldType::URL => {
builder.add_cell(&field.id, "1".to_string()).unwrap();
}
}
}
let context = builder.build();
let scripts = vec![CreateRow { payload: context }];
test.run_scripts(scripts).await;
}
let mut builder = test.row_builder();
#[tokio::test]
async fn grid_row_add_date_cell_test() {
let mut test = GridRowTest::new().await;
let mut builder = test.builder();
let mut date_field = None;
let timestamp = 1647390674;
for field in test.field_revs() {
let field_type: FieldType = field.field_type_rev.into();
if field_type == FieldType::DateTime {
date_field = Some(field.clone());
NaiveDateTime::from_timestamp(123, 0);
// The data should not be empty
assert!(builder.add_cell(&field.id, "".to_string()).is_err());
assert!(builder.add_cell(&field.id, make_date_cell_string("123")).is_ok());
assert!(builder
.add_cell(&field.id, make_date_cell_string(&timestamp.to_string()))
.is_ok());
}
}
let context = builder.build();
let date_field = date_field.unwrap();
let cell_rev = context.cell_by_field_id.get(&date_field.id).unwrap();
assert_eq!(
decode_any_cell_data(cell_rev, &date_field)
.parse::<DateCellData>()
.unwrap()
.date,
"2022/03/16",
);
let scripts = vec![CreateRow { payload: context }];
builder.insert_text_cell("hello world");
builder.insert_number_cell("18,443");
builder.insert_date_cell("1647251762");
builder.insert_single_select_cell(|options| options.first().unwrap());
builder.insert_multi_select_cell(|options| options);
builder.insert_checkbox_cell("false");
builder.insert_url_cell("1");
let row_rev = builder.build();
let scripts = vec![CreateRow { row_rev }];
test.run_scripts(scripts).await;
}

View File

@ -1,15 +1,16 @@
use crate::grid::block_test::util::GridRowTestBuilder;
use crate::grid::grid_editor::GridEditorTest;
use flowy_grid::entities::RowInfo;
use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload};
use flowy_grid::entities::{CellIdentifier, RowInfo};
use flowy_grid_data_model::revision::{
FieldRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
};
use std::sync::Arc;
pub enum RowScript {
CreateEmptyRow,
CreateRow {
payload: CreateRowRevisionPayload,
row_rev: RowRevision,
},
UpdateRow {
changeset: RowMetaChangeset,
@ -20,6 +21,11 @@ pub enum RowScript {
DeleteRows {
row_ids: Vec<String>,
},
AssertCell {
row_id: String,
field_id: String,
expected_display: Option<String>,
},
AssertRowCount(usize),
CreateBlock {
block: GridBlockMetaRevision,
@ -49,10 +55,6 @@ impl GridRowTest {
Self { inner: editor_test }
}
pub fn field_revs(&self) -> &Vec<Arc<FieldRevision>> {
&self.field_revs
}
pub fn last_row(&self) -> Option<RowRevision> {
self.row_revs.last().map(|a| a.clone().as_ref().clone())
}
@ -63,8 +65,8 @@ impl GridRowTest {
}
}
pub fn builder(&self) -> CreateRowRevisionBuilder {
CreateRowRevisionBuilder::new(&self.field_revs)
pub fn row_builder(&self) -> GridRowTestBuilder {
GridRowTestBuilder::new(self.block_id(), &self.field_revs)
}
pub async fn run_script(&mut self, script: RowScript) {
@ -76,8 +78,8 @@ impl GridRowTest {
self.row_revs = self.get_row_revs().await;
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
}
RowScript::CreateRow { payload: context } => {
let row_orders = self.editor.insert_rows(vec![context]).await.unwrap();
RowScript::CreateRow { row_rev } => {
let row_orders = self.editor.insert_rows(vec![row_rev]).await.unwrap();
for row_order in row_orders {
self.row_order_by_row_id
.insert(row_order.row_id().to_owned(), row_order);
@ -96,6 +98,24 @@ impl GridRowTest {
self.row_revs = self.get_row_revs().await;
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
}
RowScript::AssertCell {
row_id,
field_id,
expected_display,
} => {
let id = CellIdentifier {
grid_id: self.grid_id.clone(),
field_id,
row_id,
};
let display = self.editor.get_cell_display(&id).await;
match expected_display {
None => {}
Some(expected_display) => {
assert_eq!(display.unwrap(), expected_display);
}
}
}
RowScript::AssertRow { expected_row } => {
let row = &*self
.row_revs

View File

@ -1,66 +1,97 @@
use crate::grid::block_test::script::GridRowTest;
use flowy_grid::entities::FieldType;
use flowy_grid::services::field::DateCellChangeset;
use flowy_grid::services::row::{CreateRowRevisionBuilder, CreateRowRevisionPayload};
use flowy_grid_data_model::revision::FieldRevision;
use flowy_grid::services::field::select_option::{SelectOption, SELECTION_IDS_SEPARATOR};
use flowy_grid::services::field::{DateCellChangeset, MultiSelectTypeOption, SingleSelectTypeOption};
use flowy_grid::services::row::RowRevisionBuilder;
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
use std::sync::Arc;
use strum::EnumCount;
pub struct GridRowTestBuilder<'a> {
test: &'a GridRowTest,
inner_builder: CreateRowRevisionBuilder<'a>,
block_id: String,
field_revs: &'a [Arc<FieldRevision>],
inner_builder: RowRevisionBuilder<'a>,
}
impl<'a> GridRowTestBuilder<'a> {
pub fn new(test: &'a GridRowTest) -> Self {
assert_eq!(test.field_revs().len(), FieldType::COUNT);
let inner_builder = CreateRowRevisionBuilder::new(test.field_revs());
Self { test, inner_builder }
}
#[allow(dead_code)]
pub fn update_text_cell(mut self, data: String) -> Self {
let text_field = self.field_rev_with_type(&FieldType::DateTime);
self.inner_builder.add_cell(&text_field.id, data).unwrap();
self
pub fn new(block_id: &str, field_revs: &'a [Arc<FieldRevision>]) -> Self {
assert_eq!(field_revs.len(), FieldType::COUNT);
let inner_builder = RowRevisionBuilder::new(field_revs);
Self {
block_id: block_id.to_owned(),
field_revs,
inner_builder,
}
}
#[allow(dead_code)]
pub fn update_number_cell(mut self, data: String) -> Self {
let number_field = self.field_rev_with_type(&FieldType::DateTime);
self.inner_builder.add_cell(&number_field.id, data).unwrap();
self
pub fn insert_text_cell(&mut self, data: &str) {
let text_field = self.field_rev_with_type(&FieldType::RichText);
self.inner_builder
.insert_cell(&text_field.id, data.to_string())
.unwrap();
}
#[allow(dead_code)]
pub fn update_date_cell(mut self, value: i64) -> Self {
pub fn insert_number_cell(&mut self, data: &str) {
let number_field = self.field_rev_with_type(&FieldType::Number);
self.inner_builder
.insert_cell(&number_field.id, data.to_string())
.unwrap();
}
pub fn insert_date_cell(&mut self, data: &str) {
let value = serde_json::to_string(&DateCellChangeset {
date: Some(value.to_string()),
date: Some(data.to_string()),
time: None,
})
.unwrap();
let date_field = self.field_rev_with_type(&FieldType::DateTime);
self.inner_builder.add_cell(&date_field.id, value).unwrap();
self
self.inner_builder.insert_cell(&date_field.id, value).unwrap();
}
#[allow(dead_code)]
pub fn update_checkbox_cell(mut self, data: bool) -> Self {
pub fn insert_checkbox_cell(&mut self, data: &str) {
let number_field = self.field_rev_with_type(&FieldType::Checkbox);
self.inner_builder.add_cell(&number_field.id, data.to_string()).unwrap();
self
self.inner_builder
.insert_cell(&number_field.id, data.to_string())
.unwrap();
}
#[allow(dead_code)]
pub fn update_url_cell(mut self, data: String) -> Self {
let number_field = self.field_rev_with_type(&FieldType::Checkbox);
self.inner_builder.add_cell(&number_field.id, data).unwrap();
self
pub fn insert_url_cell(&mut self, data: &str) {
let number_field = self.field_rev_with_type(&FieldType::URL);
self.inner_builder
.insert_cell(&number_field.id, data.to_string())
.unwrap();
}
pub fn insert_single_select_cell<F>(&mut self, f: F)
where
F: Fn(&Vec<SelectOption>) -> &SelectOption,
{
let single_select_field = self.field_rev_with_type(&FieldType::SingleSelect);
let type_option = SingleSelectTypeOption::from(&single_select_field);
let option = f(&type_option.options);
self.inner_builder
.insert_select_option_cell(&single_select_field.id, option.id.clone())
.unwrap();
}
pub fn insert_multi_select_cell<F>(&mut self, f: F)
where
F: Fn(&Vec<SelectOption>) -> &Vec<SelectOption>,
{
let multi_select_field = self.field_rev_with_type(&FieldType::MultiSelect);
let type_option = MultiSelectTypeOption::from(&multi_select_field);
let options = f(&type_option.options);
let ops_ids = options
.iter()
.map(|option| option.id.clone())
.collect::<Vec<_>>()
.join(SELECTION_IDS_SEPARATOR);
self.inner_builder
.insert_select_option_cell(&multi_select_field.id, ops_ids)
.unwrap();
}
pub fn field_rev_with_type(&self, field_type: &FieldType) -> FieldRevision {
self.test
.field_revs()
self.field_revs
.iter()
.find(|field_rev| {
let t_field_type: FieldType = field_rev.field_type_rev.into();
@ -71,7 +102,7 @@ impl<'a> GridRowTestBuilder<'a> {
.clone()
}
pub fn build(self) -> CreateRowRevisionPayload {
self.inner_builder.build()
pub fn build(self) -> RowRevision {
self.inner_builder.build(&self.block_id)
}
}

View File

@ -6,7 +6,7 @@ use flowy_grid::entities::*;
use flowy_grid::services::field::select_option::SelectOption;
use flowy_grid::services::field::*;
use flowy_grid::services::grid_editor::{GridPadBuilder, GridRevisionEditor};
use flowy_grid::services::row::CreateRowRevisionPayload;
use flowy_grid::services::row::{CreateRowRevisionPayload, RowRevisionBuilder};
use flowy_grid::services::setting::GridSettingChangesetBuilder;
use flowy_grid_data_model::revision::*;
use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;
@ -20,6 +20,7 @@ use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use strum::EnumCount;
use strum::IntoEnumIterator;
use tokio::time::sleep;
pub struct GridEditorTest {
@ -37,14 +38,13 @@ impl GridEditorTest {
pub async fn new() -> Self {
let sdk = FlowySDKTest::default();
let _ = sdk.init_user().await;
let build_context = make_all_field_test_grid();
let build_context = make_test_grid();
let view_data: Bytes = build_context.into();
let test = ViewTest::new_grid_view(&sdk, view_data.to_vec()).await;
let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
let field_revs = editor.get_field_revs(None).await.unwrap();
let block_meta_revs = editor.get_block_meta_revs().await.unwrap();
let row_revs = editor.grid_block_snapshots(None).await.unwrap().pop().unwrap().row_revs;
assert_eq!(row_revs.len(), 3);
assert_eq!(block_meta_revs.len(), 1);
// It seems like you should add the field in the make_test_grid() function.
@ -90,75 +90,98 @@ impl GridEditorTest {
.pop()
.unwrap()
}
pub fn block_id(&self) -> &str {
&self.block_meta_revs.last().unwrap().block_id
}
}
fn make_all_field_test_grid() -> BuildGridContext {
let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
.name("Name")
.visibility(true)
.build();
// This grid is assumed to contain all the Fields.
fn make_test_grid() -> BuildGridContext {
let mut grid_builder = GridBuilder::new();
// Iterate through the FieldType to create the corresponding Field.
for field_type in FieldType::iter() {
let field_type: FieldType = field_type;
// Single Select
let single_select = SingleSelectTypeOptionBuilder::default()
.option(SelectOption::new("Live"))
.option(SelectOption::new("Completed"))
.option(SelectOption::new("Planned"))
.option(SelectOption::new("Paused"));
let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
// The
match field_type {
FieldType::RichText => {
let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
.name("Name")
.visibility(true)
.build();
grid_builder.add_field(text_field);
}
FieldType::Number => {
// Number
let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD);
let number_field = FieldBuilder::new(number).name("Price").visibility(true).build();
grid_builder.add_field(number_field);
}
FieldType::DateTime => {
// Date
let date = DateTypeOptionBuilder::default()
.date_format(DateFormat::US)
.time_format(TimeFormat::TwentyFourHour);
let date_field = FieldBuilder::new(date).name("Time").visibility(true).build();
grid_builder.add_field(date_field);
}
FieldType::SingleSelect => {
// Single Select
let single_select = SingleSelectTypeOptionBuilder::default()
.option(SelectOption::new("Live"))
.option(SelectOption::new("Completed"))
.option(SelectOption::new("Planned"))
.option(SelectOption::new("Paused"));
let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
grid_builder.add_field(single_select_field);
}
FieldType::MultiSelect => {
// MultiSelect
let multi_select = MultiSelectTypeOptionBuilder::default()
.option(SelectOption::new("Google"))
.option(SelectOption::new("Facebook"))
.option(SelectOption::new("Twitter"));
let multi_select_field = FieldBuilder::new(multi_select)
.name("Platform")
.visibility(true)
.build();
grid_builder.add_field(multi_select_field);
}
FieldType::Checkbox => {
// Checkbox
let checkbox = CheckboxTypeOptionBuilder::default();
let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build();
grid_builder.add_field(checkbox_field);
}
FieldType::URL => {
// URL
let url = URLTypeOptionBuilder::default();
let url_field = FieldBuilder::new(url).name("link").visibility(true).build();
grid_builder.add_field(url_field);
}
}
}
// MultiSelect
let multi_select = MultiSelectTypeOptionBuilder::default()
.option(SelectOption::new("Google"))
.option(SelectOption::new("Facebook"))
.option(SelectOption::new("Twitter"));
let multi_select_field = FieldBuilder::new(multi_select)
.name("Platform")
.visibility(true)
.build();
// Number
let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD);
let number_field = FieldBuilder::new(number).name("Price").visibility(true).build();
// Date
let date = DateTypeOptionBuilder::default()
.date_format(DateFormat::US)
.time_format(TimeFormat::TwentyFourHour);
let date_field = FieldBuilder::new(date).name("Time").visibility(true).build();
// Checkbox
let checkbox = CheckboxTypeOptionBuilder::default();
let checkbox_field = FieldBuilder::new(checkbox).name("is done").visibility(true).build();
// URL
let url = URLTypeOptionBuilder::default();
let url_field = FieldBuilder::new(url).name("link").visibility(true).build();
// for i in 0..3 {
// for field_type in FieldType::iter() {
// let field_type: FieldType = field_type;
// match field_type {
// FieldType::RichText => {}
// FieldType::Number => {}
// FieldType::DateTime => {}
// FieldType::SingleSelect => {}
// FieldType::MultiSelect => {}
// FieldType::Checkbox => {}
// FieldType::URL => {}
// }
// }
// }
GridBuilder::default()
.add_field(text_field)
.add_field(single_select_field)
.add_field(multi_select_field)
.add_field(number_field)
.add_field(date_field)
.add_field(checkbox_field)
.add_field(url_field)
.add_empty_row()
.add_empty_row()
.add_empty_row()
.build()
// We have many assumptions base on the number of the rows, so do not change the number of the loop.
for _i in 0..10 {
for field_type in FieldType::iter() {
let field_type: FieldType = field_type;
// let mut row_builder = RowRevisionBuilder::new()
match field_type {
FieldType::RichText => {}
FieldType::Number => {}
FieldType::DateTime => {}
FieldType::SingleSelect => {}
FieldType::MultiSelect => {}
FieldType::Checkbox => {}
FieldType::URL => {}
}
}
}
// assert_eq!(row_revs.len(), 10);
// .add_empty_row()
// .add_empty_row()
// .add_empty_row()
grid_builder.build()
}

View File

@ -26,18 +26,23 @@ impl std::default::Default for GridBuilder {
}
impl GridBuilder {
pub fn add_field(mut self, field: FieldRevision) -> Self {
pub fn new() -> Self {
Self::default()
}
pub fn add_field(&mut self, field: FieldRevision) {
self.build_context.field_revs.push(field);
self
}
pub fn add_empty_row(mut self) -> Self {
let row = RowRevision::new(&self.build_context.blocks.first().unwrap().block_id);
pub fn add_row(&mut self, row_rev: RowRevision) {
let block_meta_rev = self.build_context.blocks.first_mut().unwrap();
let block_rev = self.build_context.blocks_meta_data.first_mut().unwrap();
block_rev.rows.push(Arc::new(row));
block_rev.rows.push(Arc::new(row_rev));
block_meta_rev.row_count += 1;
self
}
pub fn add_empty_row(&mut self) {
let row = RowRevision::new(&self.build_context.blocks.first().unwrap().block_id);
self.add_row(row);
}
pub fn build(self) -> BuildGridContext {