chore: add filter feature flag & enable filter tests

This commit is contained in:
appflowy 2022-07-09 23:28:15 +08:00
parent ec1113b134
commit 24f2bf398e
14 changed files with 195 additions and 34 deletions

View File

@ -50,6 +50,7 @@ lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file
[features]
default = []
default = ["filter"]
dart = ["lib-infra/dart"]
filter = []
flowy_unit_test = ["flowy-revision/flowy_unit_test"]

View File

@ -1,5 +1,7 @@
use crate::entities::FieldType;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::ErrorCode;
use flowy_grid_data_model::parser::NotEmptyStr;
use flowy_grid_data_model::revision::GridFilterRevision;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
@ -17,6 +19,53 @@ pub struct GridDateFilter {
pub end: Option<i64>,
}
#[derive(ProtoBuf, Default, Clone, Debug)]
pub struct CreateGridDateFilterPayload {
#[pb(index = 1)]
pub field_id: String,
#[pb(index = 2)]
pub field_type: FieldType,
#[pb(index = 3)]
pub condition: DateFilterCondition,
#[pb(index = 4, one_of)]
pub start: Option<i64>,
#[pb(index = 5, one_of)]
pub end: Option<i64>,
}
pub struct CreateGridDateFilterParams {
pub field_id: String,
pub field_type: FieldType,
pub condition: DateFilterCondition,
pub start: Option<i64>,
pub end: Option<i64>,
}
impl TryInto<CreateGridDateFilterParams> for CreateGridDateFilterPayload {
type Error = ErrorCode;
fn try_into(self) -> Result<CreateGridDateFilterParams, Self::Error> {
let field_id = NotEmptyStr::parse(self.field_id)
.map_err(|_| ErrorCode::FieldIdIsEmpty)?
.0;
Ok(CreateGridDateFilterParams {
field_id,
condition: self.condition,
start: self.start,
field_type: self.field_type,
end: self.end,
})
}
}
#[derive(Serialize, Deserialize, Default)]
struct DateRange {
start: Option<i64>,

View File

@ -8,10 +8,12 @@ use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
type RowId = String;
#[derive(Default)]
pub(crate) struct FilterResultCache {
// key: row id
inner: DashMap<String, FilterResult>,
inner: DashMap<RowId, FilterResult>,
}
impl FilterResultCache {
@ -67,7 +69,7 @@ pub(crate) struct FilterCache {
impl FilterCache {
pub(crate) async fn from_grid_pad(grid_pad: &Arc<RwLock<GridRevisionPad>>) -> Arc<Self> {
let this = Arc::new(Self::default());
let _ = reload_filter_cache(this.clone(), None, grid_pad).await;
let _ = refresh_filter_cache(this.clone(), None, grid_pad).await;
this
}
@ -98,7 +100,8 @@ impl FilterCache {
}
}
pub(crate) async fn reload_filter_cache(
/// Refresh the filter according to the field id.
pub(crate) async fn refresh_filter_cache(
cache: Arc<FilterCache>,
field_ids: Option<Vec<String>>,
grid_pad: &Arc<RwLock<GridRevisionPad>>,

View File

@ -7,7 +7,7 @@ use crate::services::field::{
SingleSelectTypeOption, URLTypeOption,
};
use crate::services::filter::filter_cache::{
reload_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache,
refresh_filter_cache, FilterCache, FilterId, FilterResult, FilterResultCache,
};
use crate::services::grid_editor_task::GridServiceTaskScheduler;
use crate::services::row::GridBlockSnapshot;
@ -62,6 +62,7 @@ impl GridFilterService {
let mut changesets = vec![];
for (index, block) in task_context.blocks.into_iter().enumerate() {
// The row_ids contains the row that its visibility was changed.
let row_ids = block
.row_revs
.par_iter()
@ -74,6 +75,8 @@ impl GridFilterService {
let mut visible_rows = vec![];
let mut hide_rows = vec![];
// Query the filter result from the cache
for row_id in row_ids {
if self
.filter_result_cache
@ -93,8 +96,11 @@ impl GridFilterService {
visible_rows,
..Default::default()
};
// Save the changeset for each block
changesets.push(changeset);
}
self.notify(changesets).await;
Ok(())
}
@ -106,7 +112,7 @@ impl GridFilterService {
if let Some(filter_id) = &changeset.insert_filter {
let field_ids = Some(vec![filter_id.field_id.clone()]);
reload_filter_cache(self.filter_cache.clone(), field_ids, &self.grid_pad).await;
refresh_filter_cache(self.filter_cache.clone(), field_ids, &self.grid_pad).await;
}
if let Some(filter_id) = &changeset.delete_filter {

View File

@ -0,0 +1,2 @@
mod script;
mod test;

View File

@ -0,0 +1,91 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(clippy::all)]
#![allow(dead_code)]
#![allow(unused_imports)]
use flowy_grid::entities::{CreateGridFilterPayload, GridLayoutType, GridSetting};
use flowy_grid::services::setting::GridSettingChangesetBuilder;
use flowy_grid_data_model::revision::FieldTypeRevision;
use flowy_sync::entities::grid::{CreateGridFilterParams, DeleteFilterParams, GridSettingChangesetParams};
use crate::grid::script::GridEditorTest;
pub enum FilterScript {
#[allow(dead_code)]
UpdateGridSetting {
params: GridSettingChangesetParams,
},
InsertGridTableFilter {
payload: CreateGridFilterPayload,
},
AssertTableFilterCount {
count: i32,
},
DeleteGridTableFilter {
filter_id: String,
field_type_rev: FieldTypeRevision,
},
#[allow(dead_code)]
AssertGridSetting {
expected_setting: GridSetting,
},
}
pub struct GridFilterTest {
pub editor_test: GridEditorTest,
}
impl GridFilterTest {
pub async fn new() -> Self {
let editor_test = GridEditorTest::new().await;
Self {
editor_test
}
}
pub async fn run_scripts(&mut self, scripts: Vec<FilterScript>) {
for script in scripts {
self.run_script(script).await;
}
}
pub async fn run_script(&mut self, script: FilterScript) {
match script {
FilterScript::UpdateGridSetting { params } => {
let _ = self.editor.update_grid_setting(params).await.unwrap();
}
FilterScript::InsertGridTableFilter { payload } => {
let params: CreateGridFilterParams = payload.try_into().unwrap();
let layout_type = GridLayoutType::Table;
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
.insert_filter(params)
.build();
let _ = self.editor.update_grid_setting(params).await.unwrap();
}
FilterScript::AssertTableFilterCount { count } => {
let layout_type = GridLayoutType::Table;
let filters = self.editor.get_grid_filter(&layout_type).await.unwrap();
assert_eq!(count as usize, filters.len());
}
FilterScript::DeleteGridTableFilter { filter_id ,field_type_rev} => {
let layout_type = GridLayoutType::Table;
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
.delete_filter(DeleteFilterParams { filter_id, field_type_rev })
.build();
let _ = self.editor.update_grid_setting(params).await.unwrap();
}
FilterScript::AssertGridSetting { expected_setting } => {
let setting = self.editor.get_grid_setting().await.unwrap();
assert_eq!(expected_setting, setting);
}
}
}
}
impl std::ops::Deref for GridFilterTest {
type Target = GridEditorTest;
fn deref(&self) -> &Self::Target {
&self.editor_test
}
}

View File

@ -1,41 +1,41 @@
use crate::grid::script::EditorScript::*;
use crate::grid::script::*;
use flowy_grid::entities::CreateGridFilterPayload;
use crate::grid::filter_test::script::FilterScript::*;
use crate::grid::filter_test::script::*;
use flowy_grid::entities::{CreateGridFilterPayload, TextFilterCondition};
use flowy_grid_data_model::revision::FieldRevision;
#[tokio::test]
async fn grid_filter_create_test() {
let test = GridEditorTest::new().await;
let mut test = GridFilterTest::new().await;
let field_rev = test.text_field();
let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
GridEditorTest::new().await.run_scripts(scripts).await;
test.run_scripts(scripts).await;
}
#[tokio::test]
#[should_panic]
async fn grid_filter_invalid_condition_panic_test() {
let test = GridEditorTest::new().await;
let field_rev = test.text_field();
let mut test = GridFilterTest::new().await;
// 100 is not a valid condition, so this test should be panic.
let payload = CreateGridFilterPayload::new(field_rev, 100, Some("abc".to_owned()));
let payload = create_filter(&test, "abc");
let scripts = vec![InsertGridTableFilter { payload }];
GridEditorTest::new().await.run_scripts(scripts).await;
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn grid_filter_delete_test() {
let mut test = GridEditorTest::new().await;
let field_rev = test.text_field().clone();
let payload = CreateGridFilterPayload::new(&field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
let mut test = GridFilterTest::new().await;
let payload = create_filter(&test, "abc");
let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
test.run_scripts(scripts).await;
let filter = test.grid_filters().await.pop().unwrap();
test.run_scripts(vec![
DeleteGridTableFilter {
filter_id: filter.id,
field_type: field_rev.field_type.clone(),
field_type_rev: field_rev.field_type_rev.clone(),
},
AssertTableFilterCount { count: 0 },
])
@ -44,3 +44,8 @@ async fn grid_filter_delete_test() {
#[tokio::test]
async fn grid_filter_get_rows_test() {}
fn create_filter(grid_filter_test: &GridFilterTest, s: &str) -> CreateGridFilterPayload {
let field_rev = grid_filter_test.text_field();
CreateGridFilterPayload::new(&field_rev, TextFilterCondition::TextIsEmpty, Some(s.to_owned()))
}

View File

@ -2,7 +2,7 @@ mod block_test;
mod cell_test;
mod field_test;
mod field_util;
// mod filter_test;
mod filter_test;
mod row_test;
mod row_util;
mod script;

View File

@ -82,7 +82,7 @@ pub enum EditorScript {
},
DeleteGridTableFilter {
filter_id: String,
field_type: FieldType,
field_type_rev: FieldTypeRevision,
},
#[allow(dead_code)]
AssertGridSetting {
@ -271,10 +271,10 @@ impl GridEditorTest {
let filters = self.editor.get_grid_filter(&layout_type).await.unwrap();
assert_eq!(count as usize, filters.len());
}
EditorScript::DeleteGridTableFilter { filter_id ,field_type} => {
EditorScript::DeleteGridTableFilter { filter_id ,field_type_rev} => {
let layout_type = GridLayoutType::Table;
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
.delete_filter(DeleteFilterParams { filter_id, field_type_rev: field_type.into() })
.delete_filter(DeleteFilterParams { filter_id, field_type_rev })
.build();
let _ = self.editor.update_grid_setting(params).await.unwrap();
}

View File

@ -5,7 +5,7 @@ pub enum PayloadError {}
// TODO: support stream data
#[derive(Clone)]
#[cfg_attr(feature = "user_serde", derive(serde::Serialize))]
#[cfg_attr(feature = "use_serde", derive(serde::Serialize))]
pub enum Payload {
None,
Bytes(Bytes),

View File

@ -9,7 +9,7 @@ use derivative::*;
use std::{convert::TryFrom, fmt, fmt::Formatter};
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "user_serde", derive(serde::Serialize))]
#[cfg_attr(feature = "use_serde", derive(serde::Serialize))]
pub enum StatusCode {
Ok = 0,
Err = 1,
@ -18,7 +18,7 @@ pub enum StatusCode {
// serde user guide: https://serde.rs/field-attrs.html
#[derive(Debug, Clone, Derivative)]
#[cfg_attr(feature = "user_serde", derive(serde::Serialize))]
#[cfg_attr(feature = "use_serde", derive(serde::Serialize))]
pub struct EventResponse {
#[derivative(Debug = "ignore")]
pub payload: Payload,

View File

@ -31,6 +31,11 @@ pub struct GridRevision {
pub fields: Vec<Arc<FieldRevision>>,
pub blocks: Vec<Arc<GridBlockMetaRevision>>,
#[cfg(feature = "filter")]
#[serde(default)]
pub setting: GridSettingRevision,
#[cfg(not(feature = "filter"))]
#[serde(default, skip)]
pub setting: GridSettingRevision,
}

View File

@ -56,12 +56,12 @@ impl GridSettingRevision {
&self,
layout: &GridLayoutRevision,
field_id: &str,
field_type: &FieldTypeRevision,
field_type_rev: &FieldTypeRevision,
) -> Option<Vec<Arc<GridFilterRevision>>> {
self.filters
.get(layout)
.and_then(|filter_rev_map_by_field_id| filter_rev_map_by_field_id.get(field_id))
.and_then(|filter_rev_map| filter_rev_map.get(field_type))
.and_then(|filter_rev_map| filter_rev_map.get(field_type_rev))
.cloned()
}

View File

@ -17,8 +17,8 @@ pub type GridRevisionDelta = PlainTextDelta;
pub type GridRevisionDeltaBuilder = PlainTextDeltaBuilder;
pub struct GridRevisionPad {
pub(crate) grid_rev: Arc<GridRevision>,
pub(crate) delta: GridRevisionDelta,
grid_rev: Arc<GridRevision>,
delta: GridRevisionDelta,
}
pub trait JsonDeserializer {
@ -358,10 +358,9 @@ impl GridRevisionPad {
if is_contain {
// Only return the filters for the current fields' type.
if let Some(mut t_filter_revs) =
self.grid_rev
.setting
.get_filters(layout_ty, &field_rev.id, &field_rev.field_type_rev)
let field_id = &field_rev.id;
let field_type_rev = &field_rev.field_type_rev;
if let Some(mut t_filter_revs) = self.grid_rev.setting.get_filters(layout_ty, field_id, &field_type_rev)
{
filter_revs.append(&mut t_filter_revs);
}