feat: add name filtering

This commit is contained in:
Zack Fu Zi Xiang 2024-11-12 23:48:41 +08:00
parent 288fd59d3b
commit 787e6beb0d
No known key found for this signature in database
5 changed files with 102 additions and 48 deletions

View File

@ -1,7 +1,7 @@
use crate::http::log_request_id; use crate::http::log_request_id;
use crate::{blocking_brotli_compress, Client}; use crate::{blocking_brotli_compress, Client};
use app_error::AppError; use app_error::AppError;
use client_api_entity::workspace_dto::AFDatabase; use client_api_entity::workspace_dto::{AFDatabase, ListDatabaseParam};
use client_api_entity::{ use client_api_entity::{
BatchQueryCollabParams, BatchQueryCollabResult, CreateCollabParams, DeleteCollabParams, BatchQueryCollabParams, BatchQueryCollabResult, CreateCollabParams, DeleteCollabParams,
QueryCollab, UpdateCollabWebParams, QueryCollab, UpdateCollabWebParams,
@ -145,11 +145,13 @@ impl Client {
pub async fn list_databases( pub async fn list_databases(
&self, &self,
workspace_id: &str, workspace_id: &str,
name_filter: Option<String>,
) -> Result<Vec<AFDatabase>, AppResponseError> { ) -> Result<Vec<AFDatabase>, AppResponseError> {
let url = format!("{}/api/workspace/{}/database", self.base_url, workspace_id); let url = format!("{}/api/workspace/{}/database", self.base_url, workspace_id);
let resp = self let resp = self
.http_client_with_auth(Method::GET, &url) .http_client_with_auth(Method::GET, &url)
.await? .await?
.query(&ListDatabaseParam { name_filter })
.send() .send()
.await?; .await?;
log_request_id(&resp); log_request_id(&resp);

View File

@ -263,6 +263,11 @@ pub struct QueryWorkspaceParam {
pub include_member_count: Option<bool>, pub include_member_count: Option<bool>,
} }
#[derive(Default, Debug, Deserialize, Serialize)]
pub struct ListDatabaseParam {
pub name_filter: Option<String>, // logic: if database name contains
}
#[derive(Default, Debug, Deserialize, Serialize)] #[derive(Default, Debug, Deserialize, Serialize)]
pub struct QueryWorkspaceFolder { pub struct QueryWorkspaceFolder {
pub depth: Option<u32>, pub depth: Option<u32>,
@ -284,7 +289,7 @@ pub struct PublishedView {
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct AFDatabase { pub struct AFDatabase {
pub id: String, pub id: String,
pub name: String, pub names: Vec<String>,
pub fields: Vec<AFDatabaseField>, pub fields: Vec<AFDatabaseField>,
} }
@ -293,3 +298,9 @@ pub struct AFDatabaseField {
pub name: String, pub name: String,
pub field_type: String, pub field_type: String,
} }
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct AFDatabaseMeta {
pub name: String,
pub icon: String,
}

View File

@ -1801,7 +1801,9 @@ async fn list_database_handler(
user_uuid: UserUuid, user_uuid: UserUuid,
workspace_id: web::Path<String>, workspace_id: web::Path<String>,
state: Data<AppState>, state: Data<AppState>,
query: web::Query<ListDatabaseParam>,
) -> Result<Json<AppResponse<Vec<AFDatabase>>>> { ) -> Result<Json<AppResponse<Vec<AFDatabase>>>> {
let name_filter = query.into_inner().name_filter;
let uid = state.user_cache.get_user_uid(&user_uuid).await?; let uid = state.user_cache.get_user_uid(&user_uuid).await?;
let workspace_id = workspace_id.into_inner(); let workspace_id = workspace_id.into_inner();
let dbs = biz::collab::ops::list_database( let dbs = biz::collab::ops::list_database(
@ -1809,6 +1811,7 @@ async fn list_database_handler(
&state.collab_access_control_storage, &state.collab_access_control_storage,
uid, uid,
workspace_id, workspace_id,
name_filter,
) )
.await?; .await?;
Ok(Json(AppResponse::Ok().with_data(dbs))) Ok(Json(AppResponse::Ok().with_data(dbs)))

View File

@ -363,6 +363,7 @@ pub async fn list_database(
collab_storage: &CollabAccessControlStorage, collab_storage: &CollabAccessControlStorage,
uid: i64, uid: i64,
workspace_uuid_str: String, workspace_uuid_str: String,
name_filter: Option<String>,
) -> Result<Vec<AFDatabase>, AppError> { ) -> Result<Vec<AFDatabase>, AppError> {
let workspace_uuid: Uuid = workspace_uuid_str.as_str().parse()?; let workspace_uuid: Uuid = workspace_uuid_str.as_str().parse()?;
let ws_db_oid = select_workspace_database_oid(pg_pool, &workspace_uuid).await?; let ws_db_oid = select_workspace_database_oid(pg_pool, &workspace_uuid).await?;
@ -416,10 +417,21 @@ pub async fn list_database(
Arc::new(NoPersistenceDatabaseCollabService), Arc::new(NoPersistenceDatabaseCollabService),
None, None,
) { ) {
Some(db_body) => match db_body.metas.get_inline_view_id(&txn) { Some(db_body) => {
Some(iid) => match db_body.views.get_view(&txn, &iid) { let db_views = db_body.views.get_all_views_meta(&txn);
Some(iview) => { let names = db_views
let name = iview.name; .iter()
.map(|v| v.name.clone())
.filter(|name| !name.is_empty())
.collect::<Vec<String>>();
// 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 db_fields = db_body.fields.get_all_fields(&txn);
let mut af_fields: Vec<AFDatabaseField> = Vec::with_capacity(db_fields.len()); let mut af_fields: Vec<AFDatabaseField> = Vec::with_capacity(db_fields.len());
@ -431,14 +443,10 @@ pub async fn list_database(
} }
af_databases.push(AFDatabase { af_databases.push(AFDatabase {
id: db_body.get_database_id(&txn), id: db_body.get_database_id(&txn),
name, names,
fields: af_fields, fields: af_fields,
}); });
}, },
None => tracing::warn!("Failed to get inline view: {}", iid),
},
None => tracing::error!("Failed to get inline view id for database: {}", oid),
},
None => tracing::error!("Failed to create db_body from db_collab, oid: {}", oid), None => tracing::error!("Failed to create db_body from db_collab, oid: {}", oid),
}, },
Err(err) => tracing::error!("Failed to create db_collab: {:?}", err), Err(err) => tracing::error!("Failed to create db_collab: {:?}", err),

View File

@ -8,13 +8,20 @@ use shared_entity::dto::workspace_dto::PatchWorkspaceParam;
#[tokio::test] #[tokio::test]
async fn workspace_list_database() { async fn workspace_list_database() {
let (c, _user) = generate_unique_registered_user_client().await; let (c, _user) = generate_unique_registered_user_client().await;
let workspace_id = c.get_workspaces().await.unwrap()[0].workspace_id; let workspace_id = c.get_workspaces().await.unwrap()[0]
let dbs = c.list_databases(&workspace_id.to_string()).await.unwrap(); .workspace_id
.to_string();
{
let dbs = c.list_databases(&workspace_id, None).await.unwrap();
assert_eq!(dbs.len(), 1); assert_eq!(dbs.len(), 1);
let db = &dbs[0]; let db = &dbs[0];
assert_eq!(db.name, ""); 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 { assert!(db.fields.contains(&AFDatabaseField {
name: "Last modified".to_string(), name: "Last modified".to_string(),
field_type: "LastEditedTime".to_string(), field_type: "LastEditedTime".to_string(),
@ -37,6 +44,29 @@ async fn workspace_list_database() {
})); }));
} }
{
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] #[tokio::test]
async fn add_and_delete_workspace_for_user() { async fn add_and_delete_workspace_for_user() {
let (c, _user) = generate_unique_registered_user_client().await; let (c, _user) = generate_unique_registered_user_client().await;