Explorar o código

chore: add filter feature flag & enable filter tests

appflowy %!s(int64=2) %!d(string=hai) anos
pai
achega
24f2bf398e

+ 2 - 1
frontend/rust-lib/flowy-grid/Cargo.toml

@@ -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"]

+ 49 - 0
frontend/rust-lib/flowy-grid/src/entities/filter_entities/date_filter.rs

@@ -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>,

+ 6 - 3
frontend/rust-lib/flowy-grid/src/services/filter/filter_cache.rs

@@ -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>>,

+ 8 - 2
frontend/rust-lib/flowy-grid/src/services/filter/filter_service.rs

@@ -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 {

+ 2 - 0
frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs

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

+ 91 - 0
frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs

@@ -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
+    }
+}

+ 18 - 13
frontend/rust-lib/flowy-grid/tests/grid/filter_test.rs → frontend/rust-lib/flowy-grid/tests/grid/filter_test/test.rs

@@ -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()))
+}

+ 1 - 1
frontend/rust-lib/flowy-grid/tests/grid/mod.rs

@@ -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;

+ 3 - 3
frontend/rust-lib/flowy-grid/tests/grid/script.rs

@@ -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();
             }

+ 1 - 1
frontend/rust-lib/lib-dispatch/src/request/payload.rs

@@ -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),

+ 2 - 2
frontend/rust-lib/lib-dispatch/src/response/response.rs

@@ -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,

+ 5 - 0
shared-lib/flowy-grid-data-model/src/revision/grid_rev.rs

@@ -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,
 }

+ 2 - 2
shared-lib/flowy-grid-data-model/src/revision/grid_setting_rev.rs

@@ -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()
     }
 

+ 5 - 6
shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs

@@ -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);
                 }