From 787e6beb0d880f6e1723abee2889b0d1c34d2c00 Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Tue, 12 Nov 2024 23:48:41 +0800 Subject: [PATCH] feat: add name filtering --- libs/client-api/src/http_collab.rs | 4 +- libs/shared-entity/src/dto/workspace_dto.rs | 13 +++- src/api/workspace.rs | 3 + src/biz/collab/ops.rs | 50 +++++++------ tests/workspace/workspace_crud.rs | 80 ++++++++++++++------- 5 files changed, 102 insertions(+), 48 deletions(-) diff --git a/libs/client-api/src/http_collab.rs b/libs/client-api/src/http_collab.rs index bafa0540..cf4a240e 100644 --- a/libs/client-api/src/http_collab.rs +++ b/libs/client-api/src/http_collab.rs @@ -1,7 +1,7 @@ use crate::http::log_request_id; use crate::{blocking_brotli_compress, Client}; use app_error::AppError; -use client_api_entity::workspace_dto::AFDatabase; +use client_api_entity::workspace_dto::{AFDatabase, ListDatabaseParam}; use client_api_entity::{ BatchQueryCollabParams, BatchQueryCollabResult, CreateCollabParams, DeleteCollabParams, QueryCollab, UpdateCollabWebParams, @@ -145,11 +145,13 @@ impl Client { pub async fn list_databases( &self, workspace_id: &str, + name_filter: Option, ) -> Result, AppResponseError> { let url = format!("{}/api/workspace/{}/database", self.base_url, workspace_id); let resp = self .http_client_with_auth(Method::GET, &url) .await? + .query(&ListDatabaseParam { name_filter }) .send() .await?; log_request_id(&resp); diff --git a/libs/shared-entity/src/dto/workspace_dto.rs b/libs/shared-entity/src/dto/workspace_dto.rs index ec6b4ea2..4741a1ef 100644 --- a/libs/shared-entity/src/dto/workspace_dto.rs +++ b/libs/shared-entity/src/dto/workspace_dto.rs @@ -263,6 +263,11 @@ pub struct QueryWorkspaceParam { pub include_member_count: Option, } +#[derive(Default, Debug, Deserialize, Serialize)] +pub struct ListDatabaseParam { + pub name_filter: Option, // logic: if database name contains +} + #[derive(Default, Debug, Deserialize, Serialize)] pub struct QueryWorkspaceFolder { pub depth: Option, @@ -284,7 +289,7 @@ pub struct PublishedView { #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct AFDatabase { pub id: String, - pub name: String, + pub names: Vec, pub fields: Vec, } @@ -293,3 +298,9 @@ pub struct AFDatabaseField { pub name: String, pub field_type: String, } + +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct AFDatabaseMeta { + pub name: String, + pub icon: String, +} diff --git a/src/api/workspace.rs b/src/api/workspace.rs index 39114eed..58f4837b 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -1801,7 +1801,9 @@ async fn list_database_handler( user_uuid: UserUuid, workspace_id: web::Path, state: Data, + query: web::Query, ) -> Result>>> { + let name_filter = query.into_inner().name_filter; let uid = state.user_cache.get_user_uid(&user_uuid).await?; let workspace_id = workspace_id.into_inner(); let dbs = biz::collab::ops::list_database( @@ -1809,6 +1811,7 @@ async fn list_database_handler( &state.collab_access_control_storage, uid, workspace_id, + name_filter, ) .await?; Ok(Json(AppResponse::Ok().with_data(dbs))) diff --git a/src/biz/collab/ops.rs b/src/biz/collab/ops.rs index 02e1707e..adc929bd 100644 --- a/src/biz/collab/ops.rs +++ b/src/biz/collab/ops.rs @@ -363,6 +363,7 @@ pub async fn list_database( collab_storage: &CollabAccessControlStorage, uid: i64, workspace_uuid_str: String, + name_filter: Option, ) -> Result, AppError> { let workspace_uuid: Uuid = workspace_uuid_str.as_str().parse()?; let ws_db_oid = select_workspace_database_oid(pg_pool, &workspace_uuid).await?; @@ -416,28 +417,35 @@ pub async fn list_database( Arc::new(NoPersistenceDatabaseCollabService), None, ) { - Some(db_body) => match db_body.metas.get_inline_view_id(&txn) { - Some(iid) => match db_body.views.get_view(&txn, &iid) { - Some(iview) => { - let name = iview.name; + Some(db_body) => { + let db_views = db_body.views.get_all_views_meta(&txn); + let names = db_views + .iter() + .map(|v| v.name.clone()) + .filter(|name| !name.is_empty()) + .collect::>(); - let db_fields = db_body.fields.get_all_fields(&txn); - let mut af_fields: Vec = Vec::with_capacity(db_fields.len()); - for db_field in db_fields { - af_fields.push(AFDatabaseField { - name: db_field.name, - field_type: format!("{:?}", FieldType::from(db_field.field_type)), - }); - } - af_databases.push(AFDatabase { - id: db_body.get_database_id(&txn), - name, - fields: af_fields, - }); - }, - None => tracing::warn!("Failed to get inline view: {}", iid), - }, - None => tracing::error!("Failed to get inline view id for database: {}", oid), + // if there exists a name filter, + // there must be at least one view name that contains the filter + if let Some(name_filter) = &name_filter { + if !names.iter().any(|name| name.contains(name_filter)) { + continue; + } + } + + let db_fields = db_body.fields.get_all_fields(&txn); + let mut af_fields: Vec = Vec::with_capacity(db_fields.len()); + for db_field in db_fields { + af_fields.push(AFDatabaseField { + name: db_field.name, + field_type: format!("{:?}", FieldType::from(db_field.field_type)), + }); + } + af_databases.push(AFDatabase { + id: db_body.get_database_id(&txn), + names, + fields: af_fields, + }); }, None => tracing::error!("Failed to create db_body from db_collab, oid: {}", oid), }, diff --git a/tests/workspace/workspace_crud.rs b/tests/workspace/workspace_crud.rs index 76e01385..6e1835dd 100644 --- a/tests/workspace/workspace_crud.rs +++ b/tests/workspace/workspace_crud.rs @@ -8,33 +8,63 @@ use shared_entity::dto::workspace_dto::PatchWorkspaceParam; #[tokio::test] async fn workspace_list_database() { let (c, _user) = generate_unique_registered_user_client().await; - let workspace_id = c.get_workspaces().await.unwrap()[0].workspace_id; - let dbs = c.list_databases(&workspace_id.to_string()).await.unwrap(); - assert_eq!(dbs.len(), 1); + let workspace_id = c.get_workspaces().await.unwrap()[0] + .workspace_id + .to_string(); - let db = &dbs[0]; + { + let dbs = c.list_databases(&workspace_id, None).await.unwrap(); + assert_eq!(dbs.len(), 1); - assert_eq!(db.name, ""); - assert!(db.fields.contains(&AFDatabaseField { - name: "Last modified".to_string(), - field_type: "LastEditedTime".to_string(), - })); - assert!(db.fields.contains(&AFDatabaseField { - name: "Multiselect".to_string(), - field_type: "MultiSelect".to_string(), - })); - assert!(db.fields.contains(&AFDatabaseField { - name: "Tasks".to_string(), - field_type: "Checklist".to_string(), - })); - assert!(db.fields.contains(&AFDatabaseField { - name: "Status".to_string(), - field_type: "SingleSelect".to_string(), - })); - assert!(db.fields.contains(&AFDatabaseField { - name: "Description".to_string(), - field_type: "RichText".to_string(), - })); + let db = &dbs[0]; + + assert_eq!(db.names.len(), 2); + assert!(db.names.contains(&String::from("Untitled"))); + assert!(db.names.contains(&String::from("Grid"))); + + assert!(db.fields.contains(&AFDatabaseField { + name: "Last modified".to_string(), + field_type: "LastEditedTime".to_string(), + })); + assert!(db.fields.contains(&AFDatabaseField { + name: "Multiselect".to_string(), + field_type: "MultiSelect".to_string(), + })); + assert!(db.fields.contains(&AFDatabaseField { + name: "Tasks".to_string(), + field_type: "Checklist".to_string(), + })); + assert!(db.fields.contains(&AFDatabaseField { + name: "Status".to_string(), + field_type: "SingleSelect".to_string(), + })); + assert!(db.fields.contains(&AFDatabaseField { + name: "Description".to_string(), + field_type: "RichText".to_string(), + })); + } + + { + let dbs = c + .list_databases(&workspace_id, Some(String::from("nomatch"))) + .await + .unwrap(); + assert_eq!(dbs.len(), 0); + } + { + let dbs = c + .list_databases(&workspace_id, Some(String::from("ntitle"))) + .await + .unwrap(); + assert_eq!(dbs.len(), 1); + } + { + let dbs = c + .list_databases(&workspace_id, Some(String::from("rid"))) + .await + .unwrap(); + assert_eq!(dbs.len(), 1); + } } #[tokio::test]