use app_error::AppError; use database_entity::dto::{ PatchPublishedCollab, PublishCollabItem, PublishCollabKey, PublishInfo, }; use sqlx::{Executor, PgPool, Postgres}; use uuid::Uuid; pub async fn select_user_is_collab_publisher_for_all_views( pg_pool: &PgPool, user_uuid: &Uuid, workspace_uuid: &Uuid, view_ids: &[Uuid], ) -> Result { let count = sqlx::query_scalar!( r#" SELECT COUNT(*) FROM af_published_collab WHERE workspace_id = $1 AND view_id = ANY($2) AND published_by = (SELECT uid FROM af_user WHERE uuid = $3) "#, workspace_uuid, view_ids, user_uuid, ) .fetch_one(pg_pool) .await?; match count { Some(c) => Ok(c == view_ids.len() as i64), None => Ok(false), } } #[inline] pub async fn select_workspace_publish_namespace_exists<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, namespace: &str, ) -> Result { let res = sqlx::query_scalar!( r#" SELECT EXISTS( SELECT 1 FROM af_workspace WHERE workspace_id = $1 AND publish_namespace = $2 ) "#, workspace_id, namespace, ) .fetch_one(executor) .await?; Ok(res.unwrap_or(false)) } #[inline] pub async fn update_workspace_publish_namespace<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, new_namespace: &str, ) -> Result<(), AppError> { let res = sqlx::query!( r#" UPDATE af_workspace SET publish_namespace = $1 WHERE workspace_id = $2 "#, new_namespace, workspace_id, ) .execute(executor) .await?; if res.rows_affected() != 1 { tracing::error!( "Failed to update workspace publish namespace, workspace_id: {}, new_namespace: {}, rows_affected: {}", workspace_id, new_namespace, res.rows_affected() ); } Ok(()) } #[inline] pub async fn update_workspace_default_publish_view<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, new_view_id: &Uuid, ) -> Result<(), AppError> { let res = sqlx::query!( r#" UPDATE af_workspace SET default_published_view_id = $1 WHERE workspace_id = $2 "#, new_view_id, workspace_id, ) .execute(executor) .await?; if res.rows_affected() != 1 { tracing::error!( "Failed to update workspace default publish view, workspace_id: {}, new_view_id: {}, rows_affected: {}", workspace_id, new_view_id, res.rows_affected() ); } Ok(()) } #[inline] pub async fn update_workspace_default_publish_view_set_null< 'a, E: Executor<'a, Database = Postgres>, >( executor: E, workspace_id: &Uuid, ) -> Result<(), AppError> { let res = sqlx::query!( r#" UPDATE af_workspace SET default_published_view_id = NULL WHERE workspace_id = $1 "#, workspace_id, ) .execute(executor) .await?; if res.rows_affected() != 1 { tracing::error!( "Failed to unset workspace default publish view, workspace_id: {}, rows_affected: {}", workspace_id, res.rows_affected() ); } Ok(()) } #[inline] pub async fn select_workspace_publish_namespace<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, ) -> Result { let res = sqlx::query_scalar!( r#" SELECT publish_namespace FROM af_workspace WHERE workspace_id = $1 "#, workspace_id, ) .fetch_one(executor) .await?; Ok(res) } #[inline] pub async fn insert_or_replace_publish_collabs<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, publisher_uuid: &Uuid, publish_items: Vec>>, ) -> Result<(), AppError> { let item_count = publish_items.len(); let mut view_ids: Vec = Vec::with_capacity(item_count); let mut publish_names: Vec = Vec::with_capacity(item_count); let mut metadatas: Vec = Vec::with_capacity(item_count); let mut blobs: Vec> = Vec::with_capacity(item_count); publish_items.into_iter().for_each(|item| { view_ids.push(item.meta.view_id); publish_names.push(item.meta.publish_name); metadatas.push(item.meta.metadata); blobs.push(item.data); }); let res = sqlx::query!( r#" INSERT INTO af_published_collab (workspace_id, view_id, publish_name, published_by, metadata, blob) SELECT * FROM UNNEST( (SELECT array_agg((SELECT $1::uuid)) FROM generate_series(1, $7))::uuid[], $2::uuid[], $3::text[], (SELECT array_agg((SELECT uid FROM af_user WHERE uuid = $4)) FROM generate_series(1, $7))::bigint[], $5::jsonb[], $6::bytea[] ) ON CONFLICT (workspace_id, view_id) DO UPDATE SET metadata = EXCLUDED.metadata, blob = EXCLUDED.blob, published_by = EXCLUDED.published_by, publish_name = EXCLUDED.publish_name "#, workspace_id, &view_ids, &publish_names, publisher_uuid, &metadatas, &blobs, item_count as i32, ) .execute(executor) .await?; if res.rows_affected() != item_count as u64 { tracing::warn!( "Failed to insert or replace publish collab meta batch, workspace_id: {}, publisher_uuid: {}, rows_affected: {}, item_count: {}", workspace_id, publisher_uuid, res.rows_affected(), item_count ); } Ok(()) } #[inline] pub async fn select_publish_collab_meta<'a, E: Executor<'a, Database = Postgres>>( executor: E, publish_namespace: &str, publish_name: &str, ) -> Result { let res = sqlx::query!( r#" SELECT metadata FROM af_published_collab WHERE workspace_id = (SELECT workspace_id FROM af_workspace WHERE publish_namespace = $1) AND publish_name = $2 "#, publish_namespace, publish_name, ) .fetch_one(executor) .await?; let metadata: serde_json::Value = res.metadata; Ok(metadata) } #[inline] pub async fn delete_published_collabs<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, view_ids: &[Uuid], ) -> Result<(), AppError> { let res = sqlx::query!( r#" DELETE FROM af_published_collab WHERE workspace_id = $1 AND view_id = ANY($2) "#, workspace_id, view_ids, ) .execute(executor) .await?; if res.rows_affected() != view_ids.len() as u64 { tracing::error!( "Failed to delete published collabs, workspace_id: {}, view_ids: {:?}, rows_affected: {}", workspace_id, view_ids, res.rows_affected() ); } Ok(()) } #[inline] pub async fn update_published_collabs( txn: &mut sqlx::Transaction<'_, Postgres>, workspace_id: &Uuid, patches: &[PatchPublishedCollab], ) -> Result<(), AppError> { for patch in patches { let new_publish_name = match &patch.publish_name { Some(new_publish_name) => new_publish_name, None => continue, }; let res = sqlx::query!( r#" UPDATE af_published_collab SET publish_name = $1 WHERE workspace_id = $2 AND view_id = $3 "#, patch.publish_name, workspace_id, patch.view_id, ) .execute(txn.as_mut()) .await?; if res.rows_affected() != 1 { tracing::error!( "Failed to update published collab publish name, workspace_id: {}, view_id: {}, new_publish_name: {}, rows_affected: {}", workspace_id, patch.view_id, new_publish_name, res.rows_affected() ); } } Ok(()) } #[inline] pub async fn select_published_metadata_for_view_id( pg_pool: &PgPool, view_id: &Uuid, ) -> Result, AppError> { let res = sqlx::query!( r#" SELECT workspace_id, metadata FROM af_published_collab WHERE view_id = $1 "#, view_id, ) .fetch_optional(pg_pool) .await?; Ok(res.map(|res| (res.workspace_id, res.metadata))) } #[inline] pub async fn select_published_data_for_view_id( pg_pool: &PgPool, view_id: &Uuid, ) -> Result)>, AppError> { let res = sqlx::query!( r#" SELECT metadata, blob FROM af_published_collab WHERE view_id = $1 "#, view_id, ) .fetch_optional(pg_pool) .await?; Ok(res.map(|res| (res.metadata, res.blob))) } #[inline] pub async fn select_published_collab_workspace_view_id<'a, E: Executor<'a, Database = Postgres>>( executor: E, publish_namespace: &str, publish_name: &str, ) -> Result { let key = sqlx::query_as!( PublishCollabKey, r#" SELECT workspace_id, view_id FROM af_published_collab WHERE workspace_id = (SELECT workspace_id FROM af_workspace WHERE publish_namespace = $1) AND publish_name = $2 "#, publish_namespace, publish_name, ) .fetch_one(executor) .await?; Ok(key) } #[inline] pub async fn select_published_collab_blob<'a, E: Executor<'a, Database = Postgres>>( executor: E, publish_namespace: &str, publish_name: &str, ) -> Result, AppError> { let res = sqlx::query_scalar!( r#" SELECT blob FROM af_published_collab WHERE workspace_id = (SELECT workspace_id FROM af_workspace WHERE publish_namespace = $1) AND publish_name = $2 "#, publish_namespace, publish_name, ) .fetch_one(executor) .await?; Ok(res) } pub async fn select_default_published_view_id_for_namespace< 'a, E: Executor<'a, Database = Postgres>, >( executor: E, namespace: &str, ) -> Result, AppError> { let res = sqlx::query_scalar!( r#" SELECT default_published_view_id FROM af_workspace WHERE publish_namespace = $1 "#, namespace, ) .fetch_one(executor) .await?; Ok(res) } pub async fn select_default_published_view_id<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &Uuid, ) -> Result, AppError> { let res = sqlx::query_scalar!( r#" SELECT default_published_view_id FROM af_workspace WHERE workspace_id = $1 "#, workspace_id, ) .fetch_one(executor) .await?; 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, ) -> Result { 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 = $1; "#, view_id, ) .fetch_one(executor) .await?; Ok(res) } pub async fn select_all_published_collab_info<'a, E: Executor<'a, Database = Postgres>>( executor: E, workspace_id: &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.workspace_id = $1; "#, workspace_id, ) .fetch_all(executor) .await?; Ok(res) } pub async fn select_workspace_id_for_publish_namespace<'a, E: Executor<'a, Database = Postgres>>( executor: E, publish_namespace: &str, ) -> Result { 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, 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) }