diff --git a/.sqlx/query-0eae8461a1caa6a609bfc4f329a4768ca0372a7b8cac54d83e3277ea0ad5ed9d.json b/.sqlx/query-0eae8461a1caa6a609bfc4f329a4768ca0372a7b8cac54d83e3277ea0ad5ed9d.json new file mode 100644 index 00000000..0231b748 --- /dev/null +++ b/.sqlx/query-0eae8461a1caa6a609bfc4f329a4768ca0372a7b8cac54d83e3277ea0ad5ed9d.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM public.af_workspace where workspace_id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "0eae8461a1caa6a609bfc4f329a4768ca0372a7b8cac54d83e3277ea0ad5ed9d" +} diff --git a/.sqlx/query-c08e9c86c0877ed54499e47dbf83ffe12c16dfedea5b826e2becb0d65d6c61b1.json b/.sqlx/query-c08e9c86c0877ed54499e47dbf83ffe12c16dfedea5b826e2becb0d65d6c61b1.json new file mode 100644 index 00000000..056cec7a --- /dev/null +++ b/.sqlx/query-c08e9c86c0877ed54499e47dbf83ffe12c16dfedea5b826e2becb0d65d6c61b1.json @@ -0,0 +1,58 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO public.af_workspace (owner_uid)\n SELECT uid FROM public.af_user WHERE uuid = $1\n RETURNING *;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "database_storage_id", + "type_info": "Uuid" + }, + { + "ordinal": 2, + "name": "owner_uid", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "workspace_type", + "type_info": "Int4" + }, + { + "ordinal": 5, + "name": "deleted_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 6, + "name": "workspace_name", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + false, + false, + true, + false, + true, + true + ] + }, + "hash": "c08e9c86c0877ed54499e47dbf83ffe12c16dfedea5b826e2becb0d65d6c61b1" +} diff --git a/libs/client-api/src/http.rs b/libs/client-api/src/http.rs index 1aac9112..ad361c61 100644 --- a/libs/client-api/src/http.rs +++ b/libs/client-api/src/http.rs @@ -489,9 +489,23 @@ impl Client { .into_data() } + #[instrument(level = "debug", skip_all, err)] + pub async fn add_workspace(&self) -> Result { + let url = format!("{}/api/workspace", self.base_url); + let resp = self + .http_client_with_auth(Method::POST, &url) + .await? + .send() + .await?; + log_request_id(&resp); + AppResponse::::from_response(resp) + .await? + .into_data() + } + #[instrument(level = "debug", skip_all, err)] pub async fn get_workspaces(&self) -> Result { - let url = format!("{}/api/workspace/list", self.base_url); + let url = format!("{}/api/workspace", self.base_url); let resp = self .http_client_with_auth(Method::GET, &url) .await? diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index c2a6cfd3..1b2e3be6 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -12,6 +12,41 @@ use crate::pg_row::{AFPermissionRow, AFUserProfileRow, AFWorkspaceMemberRow, AFW use crate::user::select_uid_from_email; use app_error::AppError; +#[inline] +pub async fn delete_from_workspace(pg_pool: &PgPool, workspace_id: &Uuid) -> Result<(), AppError> { + let pg_row = sqlx::query!( + r#" + DELETE FROM public.af_workspace where workspace_id = $1 + "#, + workspace_id + ) + .execute(pg_pool) + .await?; + + assert!(pg_row.rows_affected() == 1); + Ok(()) +} + +#[inline] +pub async fn insert_user_workspace( + pg_pool: &PgPool, + user_uuid: &Uuid, +) -> Result { + let workspace = sqlx::query_as!( + AFWorkspaceRow, + r#" + INSERT INTO public.af_workspace (owner_uid) + SELECT uid FROM public.af_user WHERE uuid = $1 + RETURNING *; + "#, + user_uuid + ) + .fetch_one(pg_pool) + .await?; + + Ok(workspace) +} + /// Checks whether a user, identified by a UUID, is an 'Owner' of a workspace, identified by its /// workspace_id. #[inline] diff --git a/src/api/workspace.rs b/src/api/workspace.rs index 3ae3d275..525391b1 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -39,17 +39,28 @@ pub const COLLAB_OBJECT_ID_PATH: &str = "object_id"; pub fn workspace_scope() -> Scope { web::scope("/api/workspace") - .service(web::resource("list").route(web::get().to(list_handler))) - .service(web::resource("{workspace_id}/open").route(web::put().to(open_workspace_handler))) + + // deprecated, use `/` instead + .service(web::resource("/list").route(web::get().to(list_workspace_handler))) + + .service(web::resource("/") + .route(web::get().to(list_workspace_handler)) + .route(web::post().to(add_workpace_handler)) + ) + .service(web::resource("/{workspace_id}") + .route(web::delete().to(delete_workspace_handler)) + ) + + .service(web::resource("/{workspace_id}/open").route(web::put().to(open_workspace_handler))) .service( - web::resource("{workspace_id}/member") + web::resource("/{workspace_id}/member") .route(web::get().to(get_workspace_members_handler)) .route(web::post().to(add_workspace_members_handler)) .route(web::put().to(update_workspace_member_handler)) .route(web::delete().to(remove_workspace_member_handler)), ) .service( - web::resource("{workspace_id}/collab/{object_id}") + web::resource("/{workspace_id}/collab/{object_id}") .app_data( PayloadConfig::new(5 * 1024 * 1024), // 5 MB ) @@ -59,37 +70,37 @@ pub fn workspace_scope() -> Scope { .route(web::delete().to(delete_collab_handler)), ) .service( - web::resource("{workspace_id}/batch/collab") + web::resource("/{workspace_id}/batch/collab") .route(web::post().to(create_collab_list_handler)), ) // will be deprecated .service( - web::resource("{workspace_id}/collabs") + web::resource("/{workspace_id}/collabs") .app_data(PayloadConfig::new(10 * 1024 * 1024)) .route(web::post().to(batch_create_collab_handler)), ) .service( - web::resource("{workspace_id}/{object_id}/snapshot") + web::resource("/{workspace_id}/{object_id}/snapshot") .route(web::get().to(get_collab_snapshot_handler)) .route(web::post().to(create_collab_snapshot_handler)), ) .service( - web::resource("{workspace_id}/{object_id}/snapshot/list") + web::resource("/{workspace_id}/{object_id}/snapshot/list") .route(web::get().to(get_all_collab_snapshot_list_handler)), ) .service( - web::resource("{workspace_id}/collab/{object_id}/member") + web::resource("/{workspace_id}/collab/{object_id}/member") .route(web::post().to(add_collab_member_handler)) .route(web::get().to(get_collab_member_handler)) .route(web::put().to(update_collab_member_handler)) .route(web::delete().to(remove_collab_member_handler)), ) .service( - web::resource("{workspace_id}/collab/{object_id}/member/list") + web::resource("/{workspace_id}/collab/{object_id}/member/list") .route(web::get().to(get_collab_member_list_handler)), ) .service( - web::resource("{workspace_id}/collab_list").route(web::get().to(batch_get_collab_handler)), + web::resource("/{workspace_id}/collab_list").route(web::get().to(batch_get_collab_handler)), ) } @@ -103,8 +114,28 @@ pub fn collab_scope() -> Scope { ) } +// Adds a workspace for user, if success, return the workspace id #[instrument(skip_all, err)] -async fn list_handler( +async fn add_workpace_handler( + uuid: UserUuid, + state: Data, +) -> Result>> { + let new_workspace = workspace::ops::add_workspace_for_user(&state.pg_pool, &uuid).await?; + Ok(AppResponse::Ok().with_data(new_workspace).into()) +} + +async fn delete_workspace_handler( + _user_id: UserUuid, + workspace_id: web::Path, + state: Data, +) -> Result>> { + // TODO: add permission for workspace deletion + workspace::ops::delete_workspace_for_user(&state.pg_pool, &workspace_id).await?; + Ok(AppResponse::Ok().into()) +} + +#[instrument(skip_all, err)] +async fn list_workspace_handler( uuid: UserUuid, state: Data, ) -> Result> { diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index 9500165a..1be3e802 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -4,9 +4,9 @@ use database::collab::upsert_collab_member_with_txn; use database::pg_row::{AFWorkspaceMemberRow, AFWorkspaceRow}; use database::user::select_uid_from_email; use database::workspace::{ - delete_workspace_members, insert_workspace_member_with_txn, select_all_user_workspaces, - select_workspace, select_workspace_member_list, update_updated_at_of_workspace, - upsert_workspace_member, + delete_from_workspace, delete_workspace_members, insert_user_workspace, + insert_workspace_member_with_txn, select_all_user_workspaces, select_workspace, + select_workspace_member_list, update_updated_at_of_workspace, upsert_workspace_member, }; use database_entity::dto::{AFAccessLevel, AFRole, AFWorkspace}; use shared_entity::dto::workspace_dto::{CreateWorkspaceMember, WorkspaceMemberChangeset}; @@ -17,6 +17,23 @@ use std::ops::DerefMut; use tracing::instrument; use uuid::Uuid; +pub async fn delete_workspace_for_user( + pg_pool: &PgPool, + workspace_id: &Uuid, +) -> Result<(), AppResponseError> { + delete_from_workspace(pg_pool, workspace_id).await?; + Ok(()) +} + +pub async fn add_workspace_for_user( + pg_pool: &PgPool, + user_uuid: &Uuid, +) -> Result { + let new_workspace_row = insert_user_workspace(pg_pool, user_uuid).await?; + let new_workspace = AFWorkspace::try_from(new_workspace_row)?; + Ok(new_workspace) +} + pub async fn get_all_user_workspaces( pg_pool: &PgPool, user_uuid: &Uuid, diff --git a/tests/workspace/mod.rs b/tests/workspace/mod.rs index dd28e554..51bd3fc3 100644 --- a/tests/workspace/mod.rs +++ b/tests/workspace/mod.rs @@ -1,3 +1,4 @@ mod blob; mod member_crud; mod template_test; +mod workspace_crud; diff --git a/tests/workspace/workspace_crud.rs b/tests/workspace/workspace_crud.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/workspace/workspace_crud.rs @@ -0,0 +1 @@ +