Skip to content

Commit

Permalink
Merge pull request #784 from AppFlowy-IO/publish-outline
Browse files Browse the repository at this point in the history
feat: published view outline endpoint
  • Loading branch information
khorshuheng authored Sep 3, 2024
2 parents 8015e34 + a7a1a2f commit 6c0c99a
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 16 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.env
.dockerignore
spec.yaml
**/target
target/
deploy/
tests/
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions libs/app-error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ pub enum AppError {

#[error("{0}")]
InvalidContentType(String),

#[error("{0}")]
InvalidPublishedOutline(String),
}

impl AppError {
Expand Down Expand Up @@ -200,6 +203,7 @@ impl AppError {
AppError::AIServiceUnavailable(_) => ErrorCode::AIServiceUnavailable,
AppError::StringLengthLimitReached(_) => ErrorCode::StringLengthLimitReached,
AppError::InvalidContentType(_) => ErrorCode::InvalidContentType,
AppError::InvalidPublishedOutline(_) => ErrorCode::InvalidPublishedOutline,
}
}
}
Expand Down Expand Up @@ -318,6 +322,7 @@ pub enum ErrorCode {
InvalidContentType = 1036,
SingleUploadLimitExceeded = 1037,
AppleRevokeTokenError = 1038,
InvalidPublishedOutline = 1039,
}

impl ErrorCode {
Expand Down
36 changes: 36 additions & 0 deletions libs/database/src/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,39 @@ pub async fn select_published_collab_info<'a, E: Executor<'a, Database = Postgre

Ok(res)
}

pub async fn select_workspace_id_for_publish_namespace<'a, E: Executor<'a, Database = Postgres>>(
executor: E,
publish_namespace: &str,
) -> Result<Uuid, AppError> {
let res = sqlx::query!(
r#"
SELECT workspace_id
FROM af_workspace
WHERE publish_namespace = $1
"#,
publish_namespace,
)
.fetch_one(executor)
.await?;

Ok(res.workspace_id)
}

pub async fn select_published_view_ids_for_workspace<'a, E: Executor<'a, Database = Postgres>>(
executor: E,
workspace_id: Uuid,
) -> Result<Vec<Uuid>, AppError> {
let res = sqlx::query_scalar!(
r#"
SELECT view_id
FROM af_published_collab
WHERE workspace_id = $1
"#,
workspace_id,
)
.fetch_all(executor)
.await?;

Ok(res)
}
17 changes: 17 additions & 0 deletions libs/shared-entity/src/dto/workspace_dto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ pub enum ViewLayout {
Chat = 4,
}

impl Default for ViewLayout {
fn default() -> Self {
Self::Document
}
}

#[derive(Default, Debug, Deserialize, Serialize)]
pub struct QueryWorkspaceParam {
pub include_member_count: Option<bool>,
Expand All @@ -173,3 +179,14 @@ pub struct QueryWorkspaceParam {
pub struct QueryWorkspaceFolder {
pub depth: Option<u32>,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct PublishedView {
pub view_id: String,
pub name: String,
pub icon: Option<ViewIcon>,
pub layout: ViewLayout,
/// contains fields like `is_space`, and font information
pub extra: Option<serde_json::Value>,
pub children: Vec<PublishedView>,
}
17 changes: 17 additions & 0 deletions src/api/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ pub fn workspace_scope() -> Scope {
web::resource("/{workspace_id}/folder")
.route(web::get().to(get_workspace_folder_handler))
)
.service(
web::resource("/published-outline/{publish_namespace}")
.route(web::get().to(get_workspace_publish_outline_handler))
)
.service(
web::resource("/{workspace_id}/collab/{object_id}/member/list")
.route(web::get().to(get_collab_member_list_handler)),
Expand Down Expand Up @@ -1394,6 +1398,19 @@ async fn get_workspace_folder_handler(
Ok(Json(AppResponse::Ok().with_data(folder_view)))
}

async fn get_workspace_publish_outline_handler(
publish_namespace: web::Path<String>,
state: Data<AppState>,
) -> Result<Json<AppResponse<PublishedView>>> {
let published_view = biz::collab::ops::get_published_view(
state.collab_access_control_storage.clone(),
publish_namespace.into_inner(),
&state.pg_pool,
)
.await?;
Ok(Json(AppResponse::Ok().with_data(published_view)))
}

#[inline]
async fn parser_realtime_msg(
payload: Bytes,
Expand Down
22 changes: 17 additions & 5 deletions src/biz/collab/folder_view.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashSet;

use collab_folder::Folder;
use shared_entity::dto::workspace_dto::FolderView;
use collab_folder::{Folder, ViewLayout as CollabFolderViewLayout};
use shared_entity::dto::workspace_dto::{FolderView, ViewLayout};
use uuid::Uuid;

pub fn collab_folder_to_folder_view(folder: &Folder, depth: u32) -> FolderView {
Expand Down Expand Up @@ -126,7 +126,7 @@ impl<'a> From<FolderViewIntermediate<'a>> for FolderView {
}
}

fn view_is_space(view: &collab_folder::View) -> bool {
pub fn view_is_space(view: &collab_folder::View) -> bool {
let extra = match view.extra.as_ref() {
Some(extra) => extra,
None => return false,
Expand All @@ -144,14 +144,16 @@ fn view_is_space(view: &collab_folder::View) -> bool {
}
}

fn to_dto_view_icon(icon: collab_folder::ViewIcon) -> shared_entity::dto::workspace_dto::ViewIcon {
pub fn to_dto_view_icon(
icon: collab_folder::ViewIcon,
) -> shared_entity::dto::workspace_dto::ViewIcon {
shared_entity::dto::workspace_dto::ViewIcon {
ty: to_dto_view_icon_type(icon.ty),
value: icon.value,
}
}

fn to_dto_view_icon_type(
pub fn to_dto_view_icon_type(
icon: collab_folder::IconType,
) -> shared_entity::dto::workspace_dto::IconType {
match icon {
Expand All @@ -160,3 +162,13 @@ fn to_dto_view_icon_type(
collab_folder::IconType::Icon => shared_entity::dto::workspace_dto::IconType::Icon,
}
}

pub fn to_view_layout(collab_folder_view_layout: &CollabFolderViewLayout) -> ViewLayout {
match collab_folder_view_layout {
CollabFolderViewLayout::Document => ViewLayout::Document,
CollabFolderViewLayout::Grid => ViewLayout::Grid,
CollabFolderViewLayout::Board => ViewLayout::Board,
CollabFolderViewLayout::Calendar => ViewLayout::Calendar,
CollabFolderViewLayout::Chat => ViewLayout::Chat,
}
}
1 change: 1 addition & 0 deletions src/biz/collab/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod access_control;
pub mod folder_view;
pub mod ops;
pub mod publish_outline;
51 changes: 42 additions & 9 deletions src/biz/collab/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ use collab_entity::CollabType;
use collab_entity::EncodedCollab;
use collab_folder::{CollabOrigin, Folder};
use database::collab::{CollabStorage, GetCollabOrigin};
use database_entity::dto::QueryCollab;
use database_entity::dto::QueryCollabParams;
use database::publish::select_published_view_ids_for_workspace;
use database::publish::select_workspace_id_for_publish_namespace;
use database_entity::dto::{QueryCollab, QueryCollabParams};
use sqlx::PgPool;
use std::ops::DerefMut;

use anyhow::Context;
use shared_entity::dto::workspace_dto::FolderView;
use shared_entity::dto::workspace_dto::{FolderView, PublishedView};
use sqlx::types::Uuid;
use std::collections::HashSet;

use tracing::{event, trace};
use validator::Validate;

Expand All @@ -24,6 +27,7 @@ use database_entity::dto::{
};

use super::folder_view::collab_folder_to_folder_view;
use super::publish_outline::collab_folder_to_published_outline;

/// Create a new collab member
/// If the collab member already exists, return [AppError::RecordAlreadyExists]
Expand Down Expand Up @@ -165,26 +169,33 @@ pub async fn get_user_workspace_structure(
depth, depth_limit
)));
}
let folder = get_latest_collab_folder(collab_storage, &uid, &workspace_id).await?;
let folder =
get_latest_collab_folder(collab_storage, GetCollabOrigin::User { uid }, &workspace_id).await?;
let folder_view: FolderView = collab_folder_to_folder_view(&folder, depth);
Ok(folder_view)
}

pub async fn get_latest_collab_folder(
collab_storage: Arc<CollabAccessControlStorage>,
uid: &i64,
collab_origin: GetCollabOrigin,
workspace_id: &str,
) -> Result<Folder, AppError> {
let folder_uid = if let GetCollabOrigin::User { uid } = collab_origin {
uid
} else {
// Dummy uid to open the collab folder if the request does not originate from user
0
};
let encoded_collab = get_latest_collab_encoded(
collab_storage,
uid,
collab_origin,
workspace_id,
workspace_id,
CollabType::Folder,
)
.await?;
let folder = Folder::from_collab_doc_state(
uid,
folder_uid,
CollabOrigin::Server,
encoded_collab.into(),
workspace_id,
Expand All @@ -196,14 +207,14 @@ pub async fn get_latest_collab_folder(

pub async fn get_latest_collab_encoded(
collab_storage: Arc<CollabAccessControlStorage>,
uid: &i64,
collab_origin: GetCollabOrigin,
workspace_id: &str,
oid: &str,
collab_type: CollabType,
) -> Result<EncodedCollab, AppError> {
collab_storage
.get_encode_collab(
GetCollabOrigin::User { uid: *uid },
collab_origin,
QueryCollabParams {
workspace_id: workspace_id.to_string(),
inner: QueryCollab {
Expand All @@ -215,3 +226,25 @@ pub async fn get_latest_collab_encoded(
)
.await
}

pub async fn get_published_view(
collab_storage: Arc<CollabAccessControlStorage>,
publish_namespace: String,
pg_pool: &PgPool,
) -> Result<PublishedView, AppError> {
let workspace_id = select_workspace_id_for_publish_namespace(pg_pool, &publish_namespace).await?;
let folder = get_latest_collab_folder(
collab_storage,
GetCollabOrigin::Server,
&workspace_id.to_string(),
)
.await?;
let publish_view_ids = select_published_view_ids_for_workspace(pg_pool, workspace_id).await?;
let publish_view_ids: HashSet<String> = publish_view_ids
.into_iter()
.map(|id| id.to_string())
.collect();
let published_view: PublishedView =
collab_folder_to_published_outline(&folder, &publish_view_ids)?;
Ok(published_view)
}
Loading

0 comments on commit 6c0c99a

Please sign in to comment.