From 23e1da2910dadb94f44f2fb834e657e656ffd428 Mon Sep 17 00:00:00 2001 From: khorshuheng Date: Wed, 23 Oct 2024 13:56:35 +0800 Subject: [PATCH] feat: add publish info to list templates --- ...c0d867526bca0afee9aa4af294fbbebf3db0a.json | 46 ++++++++++++++++ libs/database-entity/src/dto.rs | 55 +++++++++++++++---- libs/database/src/publish.rs | 26 +++++++++ src/api/template.rs | 2 +- src/biz/template/ops.rs | 38 ++++++++++--- tests/workspace/template.rs | 9 +++ 6 files changed, 155 insertions(+), 21 deletions(-) create mode 100644 .sqlx/query-e2ab2c5d0328f2a0900dfbd9080c0d867526bca0afee9aa4af294fbbebf3db0a.json diff --git a/.sqlx/query-e2ab2c5d0328f2a0900dfbd9080c0d867526bca0afee9aa4af294fbbebf3db0a.json b/.sqlx/query-e2ab2c5d0328f2a0900dfbd9080c0d867526bca0afee9aa4af294fbbebf3db0a.json new file mode 100644 index 000000000..b447b43be --- /dev/null +++ b/.sqlx/query-e2ab2c5d0328f2a0900dfbd9080c0d867526bca0afee9aa4af294fbbebf3db0a.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n aw.publish_namespace AS namespace,\n apc.publish_name,\n apc.view_id,\n au.email AS publisher_email,\n apc.created_at AS publish_timestamp\n FROM af_published_collab apc\n JOIN af_user au ON apc.published_by = au.uid\n JOIN af_workspace aw ON apc.workspace_id = aw.workspace_id\n WHERE apc.view_id = ANY($1);\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "namespace", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "publish_name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "view_id", + "type_info": "Uuid" + }, + { + "ordinal": 3, + "name": "publisher_email", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "publish_timestamp", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "UuidArray" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "e2ab2c5d0328f2a0900dfbd9080c0d867526bca0afee9aa4af294fbbebf3db0a" +} diff --git a/libs/database-entity/src/dto.rs b/libs/database-entity/src/dto.rs index e72ee1c83..00a5a67d0 100644 --- a/libs/database-entity/src/dto.rs +++ b/libs/database-entity/src/dto.rs @@ -1344,22 +1344,22 @@ pub struct TemplateWithPublishInfo { pub publish_info: PublishInfo, } -impl TemplateWithPublishInfo { - pub fn from_template_and_publish_info(template: &Template, publish_info: &PublishInfo) -> Self { +impl From<(Template, PublishInfo)> for TemplateWithPublishInfo { + fn from((template, publish_info): (Template, PublishInfo)) -> Self { Self { view_id: template.view_id, created_at: template.created_at, last_updated_at: template.last_updated_at, - name: template.name.clone(), - description: template.description.clone(), - about: template.about.clone(), - view_url: template.view_url.clone(), - categories: template.categories.clone(), - creator: template.creator.clone(), + name: template.name, + description: template.description, + about: template.about, + view_url: template.view_url, + categories: template.categories, + creator: template.creator, is_new_template: template.is_new_template, is_featured: template.is_featured, - related_templates: template.related_templates.clone(), - publish_info: publish_info.clone(), + related_templates: template.related_templates, + publish_info, } } } @@ -1378,9 +1378,42 @@ pub struct TemplateMinimal { pub is_featured: bool, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct TemplateMinimalWithPublishInfo { + pub view_id: Uuid, + pub created_at: DateTime, + pub last_updated_at: DateTime, + pub name: String, + pub description: String, + pub view_url: String, + pub creator: TemplateCreatorMinimal, + pub categories: Vec, + pub is_new_template: bool, + pub is_featured: bool, + pub publish_info: PublishInfo, +} + +impl From<(TemplateMinimal, PublishInfo)> for TemplateMinimalWithPublishInfo { + fn from((template, publish_info): (TemplateMinimal, PublishInfo)) -> Self { + Self { + view_id: template.view_id, + created_at: template.created_at, + last_updated_at: template.last_updated_at, + name: template.name, + description: template.description, + view_url: template.view_url, + creator: template.creator, + categories: template.categories, + is_new_template: template.is_new_template, + is_featured: template.is_featured, + publish_info, + } + } +} + #[derive(Serialize, Deserialize, Debug)] pub struct Templates { - pub templates: Vec, + pub templates: Vec, } #[derive(Serialize, Deserialize, Debug)] diff --git a/libs/database/src/publish.rs b/libs/database/src/publish.rs index bf4fafb27..ffd770232 100644 --- a/libs/database/src/publish.rs +++ b/libs/database/src/publish.rs @@ -357,6 +357,32 @@ pub async fn select_default_published_view_id<'a, E: Executor<'a, Database = Pos Ok(res) } +pub async fn select_published_collab_info_for_view_ids<'a, E: Executor<'a, Database = Postgres>>( + executor: E, + view_ids: &[Uuid], +) -> Result, AppError> { + let res = sqlx::query_as!( + PublishInfo, + r#" + SELECT + aw.publish_namespace AS namespace, + apc.publish_name, + apc.view_id, + au.email AS publisher_email, + apc.created_at AS publish_timestamp + FROM af_published_collab apc + JOIN af_user au ON apc.published_by = au.uid + JOIN af_workspace aw ON apc.workspace_id = aw.workspace_id + WHERE apc.view_id = ANY($1); + "#, + view_ids, + ) + .fetch_all(executor) + .await?; + + Ok(res) +} + pub async fn select_published_collab_info<'a, E: Executor<'a, Database = Postgres>>( executor: E, view_id: &Uuid, diff --git a/src/api/template.rs b/src/api/template.rs index 1b79796c7..3eb4ff49a 100644 --- a/src/api/template.rs +++ b/src/api/template.rs @@ -219,7 +219,7 @@ async fn list_templates_handler( state: Data, ) -> Result> { let data = data.into_inner(); - let template_summary_list = get_templates( + let template_summary_list = get_templates_with_publish_info( &state.pg_pool, data.category_id, data.is_featured, diff --git a/src/biz/template/ops.rs b/src/biz/template/ops.rs index c91b9ea38..4d01ae48d 100644 --- a/src/biz/template/ops.rs +++ b/src/biz/template/ops.rs @@ -1,4 +1,4 @@ -use std::{ops::DerefMut, path::Path}; +use std::{collections::HashMap, ops::DerefMut, path::Path}; use actix_multipart::form::bytes::Bytes as MPBytes; use anyhow::Context; @@ -6,12 +6,12 @@ use app_error::ErrorCode; use aws_sdk_s3::primitives::ByteStream; use database::{ file::{s3_client_impl::AwsS3BucketClientImpl, BucketClient, ResponseBlob}, - publish::select_published_collab_info, + publish::{select_published_collab_info, select_published_collab_info_for_view_ids}, template::*, }; use database_entity::dto::{ - AccountLink, Template, TemplateCategory, TemplateCategoryType, TemplateCreator, TemplateHomePage, - TemplateMinimal, TemplateWithPublishInfo, + AccountLink, PublishInfo, Template, TemplateCategory, TemplateCategoryType, TemplateCreator, + TemplateHomePage, TemplateMinimalWithPublishInfo, TemplateWithPublishInfo, }; use shared_entity::response::AppResponseError; use sqlx::PgPool; @@ -229,13 +229,13 @@ pub async fn update_template( Ok(updated_template) } -pub async fn get_templates( +pub async fn get_templates_with_publish_info( pg_pool: &PgPool, category_id: Option, is_featured: Option, is_new_template: Option, name_contains: Option<&str>, -) -> Result, AppResponseError> { +) -> Result, AppResponseError> { let templates = select_templates( pg_pool, category_id, @@ -245,7 +245,28 @@ pub async fn get_templates( None, ) .await?; - Ok(templates) + let view_ids = templates.iter().map(|t| t.view_id).collect::>(); + let publish_info_for_views = + select_published_collab_info_for_view_ids(pg_pool, &view_ids).await?; + let mut publish_info_map = publish_info_for_views + .into_iter() + .map(|info| (info.view_id, info)) + .collect::>(); + let templates_with_publish_info: Vec = templates + .into_iter() + .filter_map(|template| { + publish_info_map + .remove(&template.view_id) + .map(|pub_info| TemplateMinimalWithPublishInfo::from((template, pub_info))) + }) + .collect(); + if templates_with_publish_info.len() != view_ids.len() { + return Err(AppResponseError::new( + ErrorCode::Internal, + "one or more templates does not have a publish info", + )); + } + Ok(templates_with_publish_info) } pub async fn get_template_with_publish_info( @@ -254,8 +275,7 @@ pub async fn get_template_with_publish_info( ) -> Result { let template = select_template_view_by_id(pg_pool, view_id).await?; let pub_info = select_published_collab_info(pg_pool, &view_id).await?; - let template_with_pub_info = - TemplateWithPublishInfo::from_template_and_publish_info(&template, &pub_info); + let template_with_pub_info = TemplateWithPublishInfo::from((template, pub_info)); Ok(template_with_pub_info) } diff --git a/tests/workspace/template.rs b/tests/workspace/template.rs index 49e1178df..59e9403d7 100644 --- a/tests/workspace/template.rs +++ b/tests/workspace/template.rs @@ -361,6 +361,15 @@ async fn test_template_crud() { assert_eq!(templates.len(), 2); assert!(view_ids.contains(&published_view_ids[2])); assert!(view_ids.contains(&published_view_ids[3])); + assert_eq!( + templates[0] + .publish_info + .namespace + .as_ref() + .unwrap() + .to_string(), + published_view_namespace.clone() + ); let featured_templates = guest_client .get_templates(None, Some(true), None, Some(template_name_prefix.clone()))