diff --git a/libs/client-api/src/http_collab.rs b/libs/client-api/src/http_collab.rs index d02b9c54..d1841167 100644 --- a/libs/client-api/src/http_collab.rs +++ b/libs/client-api/src/http_collab.rs @@ -4,7 +4,7 @@ use app_error::AppError; use bytes::Bytes; use chrono::{DateTime, Utc}; use client_api_entity::workspace_dto::{ - AFDatabase, AFDatabaseRow, AFDatabaseRowDetail, DatabaseRowUpdatedItem, + AFDatabase, AFDatabaseField, AFDatabaseRow, AFDatabaseRowDetail, DatabaseRowUpdatedItem, ListDatabaseRowDetailParam, ListDatabaseRowUpdatedParam, }; use client_api_entity::{ @@ -192,6 +192,24 @@ impl Client { AppResponse::from_response(resp).await?.into_data() } + pub async fn get_database_fields( + &self, + workspace_id: &str, + database_id: &str, + ) -> Result, AppResponseError> { + let url = format!( + "{}/api/workspace/{}/database/{}/fields", + self.base_url, workspace_id, database_id + ); + let resp = self + .http_client_with_auth(Method::GET, &url) + .await? + .send() + .await?; + log_request_id(&resp); + AppResponse::from_response(resp).await?.into_data() + } + pub async fn list_database_row_ids_updated( &self, workspace_id: &str, diff --git a/libs/shared-entity/src/dto/workspace_dto.rs b/libs/shared-entity/src/dto/workspace_dto.rs index fb071bee..c0fc989b 100644 --- a/libs/shared-entity/src/dto/workspace_dto.rs +++ b/libs/shared-entity/src/dto/workspace_dto.rs @@ -344,12 +344,6 @@ pub struct AFDatabase { pub views: Vec, } -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct AFDatabaseField { - pub name: String, - pub field_type: String, -} - #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct AFDatabaseRow { pub id: String, @@ -360,3 +354,11 @@ pub struct AFDatabaseRowDetail { pub id: String, pub cells: HashMap>, } + +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct AFDatabaseField { + pub id: String, + pub name: String, + pub field_type: String, + pub is_primary: bool, +} diff --git a/src/api/workspace.rs b/src/api/workspace.rs index 03229f5c..4484c166 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -261,6 +261,10 @@ pub fn workspace_scope() -> Scope { web::resource("/{workspace_id}/database/{database_id}/row") .route(web::get().to(list_database_row_id_handler)), ) + .service( + web::resource("/{workspace_id}/database/{database_id}/fields") + .route(web::get().to(get_database_fields_handler)), + ) .service( web::resource("/{workspace_id}/database/{database_id}/row/updated") .route(web::get().to(list_database_row_id_updated_handler)), @@ -1906,6 +1910,28 @@ async fn list_database_row_id_handler( Ok(Json(AppResponse::Ok().with_data(db_rows))) } +async fn get_database_fields_handler( + user_uuid: UserUuid, + path_param: web::Path<(String, String)>, + state: Data, +) -> Result>>> { + let (workspace_id, db_id) = path_param.into_inner(); + let uid = state.user_cache.get_user_uid(&user_uuid).await?; + state + .workspace_access_control + .enforce_action(&uid, &workspace_id, Action::Read) + .await?; + + let db_fields = biz::collab::ops::get_database_fields( + &state.collab_access_control_storage, + &workspace_id, + &db_id, + ) + .await?; + + Ok(Json(AppResponse::Ok().with_data(db_fields))) +} + async fn list_database_row_id_updated_handler( user_uuid: UserUuid, path_param: web::Path<(String, String)>, diff --git a/src/biz/collab/ops.rs b/src/biz/collab/ops.rs index c9140706..0f5512cf 100644 --- a/src/biz/collab/ops.rs +++ b/src/biz/collab/ops.rs @@ -25,6 +25,7 @@ use database::publish::select_workspace_id_for_publish_namespace; use database_entity::dto::QueryCollabResult; use database_entity::dto::{QueryCollab, QueryCollabParams}; use shared_entity::dto::workspace_dto::AFDatabase; +use shared_entity::dto::workspace_dto::AFDatabaseField; use shared_entity::dto::workspace_dto::AFDatabaseRow; use shared_entity::dto::workspace_dto::AFDatabaseRowDetail; use shared_entity::dto::workspace_dto::DatabaseRowUpdatedItem; @@ -444,26 +445,8 @@ pub async fn list_database_row_ids( workspace_uuid_str: &str, database_uuid_str: &str, ) -> Result, AppError> { - let db_collab = get_latest_collab( - collab_storage, - GetCollabOrigin::Server, - workspace_uuid_str, - database_uuid_str, - CollabType::Database, - ) - .await?; - let db_body = DatabaseBody::from_collab( - &db_collab, - Arc::new(NoPersistenceDatabaseCollabService), - None, - ) - .ok_or_else(|| { - AppError::Internal(anyhow::anyhow!( - "Failed to create database body from collab, db_collab_id: {}", - database_uuid_str, - )) - })?; - + let (db_collab, db_body) = + get_database_body(collab_storage, workspace_uuid_str, database_uuid_str).await?; // get any view_id let txn = db_collab.transact(); let iid = db_body.get_inline_view_id(&txn); @@ -483,6 +466,27 @@ pub async fn list_database_row_ids( Ok(db_rows) } +pub async fn get_database_fields( + collab_storage: &CollabAccessControlStorage, + workspace_uuid_str: &str, + database_uuid_str: &str, +) -> Result, AppError> { + let (db_collab, db_body) = + get_database_body(collab_storage, workspace_uuid_str, database_uuid_str).await?; + + let all_fields = db_body.fields.get_all_fields(&db_collab.transact()); + let mut acc = Vec::with_capacity(all_fields.len()); + for field in all_fields { + acc.push(AFDatabaseField { + id: field.id, + name: field.name, + field_type: format!("{:?}", FieldType::from(field.field_type)), + is_primary: field.is_primary, + }); + } + Ok(acc) +} + pub async fn list_database_row_ids_updated( collab_storage: &CollabAccessControlStorage, pg_pool: &PgPool, @@ -736,3 +740,30 @@ fn add_to_selection_from_type_options( } }; } + +async fn get_database_body( + collab_storage: &CollabAccessControlStorage, + workspace_uuid_str: &str, + database_uuid_str: &str, +) -> Result<(Collab, DatabaseBody), AppError> { + let db_collab = get_latest_collab( + collab_storage, + GetCollabOrigin::Server, + workspace_uuid_str, + database_uuid_str, + CollabType::Database, + ) + .await?; + let db_body = DatabaseBody::from_collab( + &db_collab, + Arc::new(NoPersistenceDatabaseCollabService), + None, + ) + .ok_or_else(|| { + AppError::Internal(anyhow::anyhow!( + "Failed to create database body from collab, db_collab_id: {}", + database_uuid_str, + )) + })?; + Ok((db_collab, db_body)) +} diff --git a/tests/workspace/workspace_crud.rs b/tests/workspace/workspace_crud.rs index 4f23afa0..91dbe50d 100644 --- a/tests/workspace/workspace_crud.rs +++ b/tests/workspace/workspace_crud.rs @@ -1,6 +1,7 @@ use client_api_test::generate_unique_registered_user_client; use collab_entity::CollabType; use database_entity::dto::QueryCollabParams; +use shared_entity::dto::workspace_dto::AFDatabaseField; use shared_entity::dto::workspace_dto::CreateWorkspaceParam; use shared_entity::dto::workspace_dto::PatchWorkspaceParam; @@ -24,6 +25,48 @@ async fn workspace_list_database() { .unwrap(); assert_eq!(db_row_ids.len(), 5, "{:?}", db_row_ids); } + { + let mut db_fields = c + .get_database_fields(&workspace_id, &todos_db.id) + .await + .unwrap(); + db_fields.sort(); + + let expected = vec![ + AFDatabaseField { + id: "3AE6iK".to_string(), + name: "Last modified".to_string(), + field_type: "LastEditedTime".to_string(), + is_primary: false, + }, + AFDatabaseField { + id: "KinVda".to_string(), + name: "Tasks".to_string(), + field_type: "Checklist".to_string(), + is_primary: false, + }, + AFDatabaseField { + id: "SqwRg1".to_string(), + name: "Status".to_string(), + field_type: "SingleSelect".to_string(), + is_primary: false, + }, + AFDatabaseField { + id: "phVRgL".to_string(), + name: "Description".to_string(), + field_type: "RichText".to_string(), + is_primary: true, + }, + AFDatabaseField { + id: "wdX8DG".to_string(), + name: "Multiselect".to_string(), + field_type: "MultiSelect".to_string(), + is_primary: false, + }, + ]; + + assert_eq!(db_fields, expected, "{:#?}", db_fields); + } { let db_row_ids = c .list_database_row_ids_updated(&workspace_id, &todos_db.id, None) @@ -31,7 +74,6 @@ async fn workspace_list_database() { .unwrap(); assert_eq!(db_row_ids.len(), 5, "{:?}", db_row_ids); } - { let db_row_ids = c .list_database_row_ids(&workspace_id, &todos_db.id)