From a18ebc4db37aeef8f9f1d5332caacfa28694a1e7 Mon Sep 17 00:00:00 2001 From: kasisnu <5044120+kasisnu@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:26:57 +0530 Subject: [PATCH] get endpoints for zs datasets & voices --- .../voice_designer/datasets/get_dataset.rs | 15 +- .../voice_designer/voices/get_voice.rs | 21 ++- .../voice_datasets/get_dataset.rs | 140 ++++++++++++++++++ .../voice_designer/voice_datasets/mod.rs | 1 + .../voice_designer/voices/get_voice.rs | 139 +++++++++++++++++ .../endpoints/voice_designer/voices/mod.rs | 1 + .../service/web/storyteller_web/src/routes.rs | 4 + 7 files changed, 315 insertions(+), 6 deletions(-) create mode 100644 crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voice_datasets/get_dataset.rs create mode 100644 crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voices/get_voice.rs diff --git a/crates/lib/mysql_queries/src/queries/voice_designer/datasets/get_dataset.rs b/crates/lib/mysql_queries/src/queries/voice_designer/datasets/get_dataset.rs index f5f00cf65f..810fbe537c 100644 --- a/crates/lib/mysql_queries/src/queries/voice_designer/datasets/get_dataset.rs +++ b/crates/lib/mysql_queries/src/queries/voice_designer/datasets/get_dataset.rs @@ -1,4 +1,5 @@ use anyhow::anyhow; +use chrono::{DateTime, Utc}; use log::error; use sqlx::{MySql, MySqlPool}; use sqlx::pool::PoolConnection; @@ -15,6 +16,8 @@ pub struct ZsDataset { pub ietf_primary_language_subtag: String, pub maybe_creator_user_token: Option, pub creator_set_visibility: Visibility, + pub created_at: DateTime, + pub updated_at: DateTime, } pub async fn get_dataset_by_token( @@ -72,6 +75,8 @@ pub async fn get_dataset_by_token_with_connection( ietf_primary_language_subtag: record.ietf_primary_language_subtag, maybe_creator_user_token: record.maybe_creator_user_token, creator_set_visibility: record.creator_set_visibility, + created_at: record.created_at, + updated_at: record.updated_at, })) } @@ -88,7 +93,9 @@ async fn select_include_deleted( zd.ietf_language_tag, zd.ietf_primary_language_subtag, zd.maybe_creator_user_token, - zd.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility` + zd.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`, + zd.created_at, + zd.updated_at FROM zs_voice_datasets as zd WHERE zd.token = ? @@ -111,7 +118,9 @@ async fn select_without_deleted( zd.ietf_language_tag, zd.ietf_primary_language_subtag, zd.maybe_creator_user_token, - zd.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility` + zd.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`, + zd.created_at, + zd.updated_at FROM zs_voice_datasets as zd WHERE zd.token = ? @@ -130,4 +139,6 @@ pub struct RawDataset { ietf_primary_language_subtag: String, maybe_creator_user_token: Option, creator_set_visibility: Visibility, + created_at: DateTime, + updated_at: DateTime, } \ No newline at end of file diff --git a/crates/lib/mysql_queries/src/queries/voice_designer/voices/get_voice.rs b/crates/lib/mysql_queries/src/queries/voice_designer/voices/get_voice.rs index aec5047379..d37f9dd9ac 100644 --- a/crates/lib/mysql_queries/src/queries/voice_designer/voices/get_voice.rs +++ b/crates/lib/mysql_queries/src/queries/voice_designer/voices/get_voice.rs @@ -1,4 +1,5 @@ use anyhow::anyhow; +use chrono::{DateTime, Utc}; use log::error; use sqlx::{MySql, MySqlPool}; use sqlx::pool::PoolConnection; @@ -14,7 +15,10 @@ pub struct ZsVoice { pub ietf_language_tag: String, pub ietf_primary_language_subtag: String, pub bucket_hash: String, - pub maybe_creator_user_token: Option + pub maybe_creator_user_token: Option, + pub creator_set_visibility: Visibility, + pub created_at: DateTime, + pub updated_at: DateTime, } pub async fn get_voice_by_token( @@ -71,7 +75,10 @@ pub async fn get_voice_by_token_with_connection( ietf_language_tag: record.ietf_language_tag, ietf_primary_language_subtag: record.ietf_primary_language_subtag, bucket_hash: record.bucket_hash, - maybe_creator_user_token: record.maybe_creator_user_token + maybe_creator_user_token: record.maybe_creator_user_token, + creator_set_visibility: record.creator_set_visibility, + created_at: record.created_at, + updated_at: record.updated_at, })) } @@ -89,7 +96,9 @@ async fn select_include_deleted( zv.ietf_primary_language_subtag, zv.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`, zv.bucket_hash, - zv.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility` + zv.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`, + zv.created_at, + zv.updated_at FROM zs_voices as zv WHERE zv.token = ? @@ -113,7 +122,9 @@ async fn select_without_deleted( zv.ietf_primary_language_subtag, zv.maybe_creator_user_token as `maybe_creator_user_token: tokens::tokens::users::UserToken`, zv.bucket_hash, - zv.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility` + zv.creator_set_visibility as `creator_set_visibility: enums::common::visibility::Visibility`, + zv.created_at, + zv.updated_at FROM zs_voices as zv WHERE zv.token = ? @@ -133,4 +144,6 @@ pub struct RawVoice { maybe_creator_user_token: Option, bucket_hash: String, creator_set_visibility: Visibility, + created_at: DateTime, + updated_at: DateTime, } diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voice_datasets/get_dataset.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voice_datasets/get_dataset.rs new file mode 100644 index 0000000000..ab33526cfb --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voice_datasets/get_dataset.rs @@ -0,0 +1,140 @@ +use std::fmt; +use std::sync::Arc; + +use actix_web::{HttpRequest, HttpResponse, web}; +use actix_web::error::ResponseError; +use actix_web::http::StatusCode; +use actix_web::web::Path; +use chrono::{DateTime, Utc}; +use log::warn; + +use enums::common::visibility::Visibility; +use mysql_queries::queries::voice_designer::datasets::get_dataset::get_dataset_by_token; +use mysql_queries::queries::voice_designer::datasets::list_datasets_by_username::list_datasets_by_username; +use tokens::tokens::users::UserToken; +use tokens::tokens::zs_voice_datasets::ZsVoiceDatasetToken; + +use crate::http_server::common_responses::user_details_lite::UserDetailsLight; +use crate::server_state::ServerState; + +#[derive(Serialize, Clone)] +pub struct GetDatasetResponse { + dataset_token: ZsVoiceDatasetToken, + title: String, + + ietf_language_tag: String, + ietf_primary_language_subtag: String, + + creator: UserDetailsLight, + + creator_set_visibility: Visibility, + + created_at: DateTime, + updated_at: DateTime, +} + +#[derive(Deserialize)] +pub struct GetDatasetPathInfo { + dataset_token: String, +} + +#[derive(Debug)] +pub enum GetDatasetError { + NotAuthorized, + NotFound, + ServerError, +} + +impl fmt::Display for GetDatasetError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl ResponseError for GetDatasetError { + fn status_code(&self) -> StatusCode { + match *self { + GetDatasetError::NotAuthorized => StatusCode::UNAUTHORIZED, + GetDatasetError::ServerError => StatusCode::INTERNAL_SERVER_ERROR, + GetDatasetError::NotFound => StatusCode::NOT_FOUND, + } + } +} + +pub async fn get_dataset_handler( + http_request: HttpRequest, + path: Path, + server_state: web::Data> +) -> Result { + + let maybe_user_session = server_state + .session_checker + .maybe_get_user_session(&http_request, &server_state.mysql_pool) + .await + .map_err(|e| { + warn!("Session checker error: {:?}", e); + GetDatasetError::ServerError + })?; + + let user_session = match maybe_user_session { + Some(session) => session, + None => { + warn!("not logged in"); + return Err(GetDatasetError::NotAuthorized); + } + }; + + let dataset_token = path.dataset_token.clone(); + let creator_user_token = user_session.user_token.clone(); + let is_mod = user_session.can_ban_users; + + let dataset_lookup_result = get_dataset_by_token( + &ZsVoiceDatasetToken::new(dataset_token.clone()), + is_mod, + &server_state.mysql_pool, + ).await; + + let dataset = match dataset_lookup_result { + Ok(Some(dataset)) => dataset, + Ok(None) => { + warn!("Dataset not found: {:?}", dataset_token); + return Err(GetDatasetError::NotFound); + }, + Err(err) => { + warn!("Error looking up dataset: {:?}", err); + return Err(GetDatasetError::ServerError); + } + }; + + let is_creator = dataset.maybe_creator_user_token.as_deref() + .map(|creator_user_token| creator_user_token == &user_session.user_token) + .unwrap_or(false); + + if !is_creator && !is_mod { + warn!("user is not allowed to view this dataset: {}", user_session.user_token); + return Err(GetDatasetError::NotAuthorized); + } + + let response = GetDatasetResponse{ + dataset_token: dataset.token, + title: dataset.title, + ietf_language_tag: dataset.ietf_language_tag, + ietf_primary_language_subtag: dataset.ietf_primary_language_subtag, + creator: UserDetailsLight::from_db_fields( + &UserToken::new_from_str(user_session.user_token.as_ref()), + user_session.username.as_ref(), + user_session.display_name.as_ref(), + user_session.email_gravatar_hash.as_ref(), + ), + creator_set_visibility: dataset.creator_set_visibility, + created_at: dataset.created_at, + updated_at: dataset.updated_at, + }; + + let body = serde_json::to_string(&response) + .map_err(|e| GetDatasetError::ServerError)?; + + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voice_datasets/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voice_datasets/mod.rs index 2e05d36601..5819c46c84 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voice_datasets/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voice_datasets/mod.rs @@ -3,3 +3,4 @@ pub mod delete_dataset; pub mod list_datasets_by_session; pub mod list_datasets_by_user; pub mod update_dataset; +pub mod get_dataset; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voices/get_voice.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voices/get_voice.rs new file mode 100644 index 0000000000..bbf9ddbcb7 --- /dev/null +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voices/get_voice.rs @@ -0,0 +1,139 @@ +use std::fmt; +use std::sync::Arc; + +use actix_web::{HttpRequest, HttpResponse, web}; +use actix_web::error::ResponseError; +use actix_web::http::StatusCode; +use actix_web::web::Path; +use chrono::{DateTime, Utc}; +use log::warn; + +use enums::common::visibility::Visibility; +use mysql_queries::queries::voice_designer::voices::get_voice::get_voice_by_token; +use tokens::tokens::users::UserToken; +use tokens::tokens::zs_voices::ZsVoiceToken; + +use crate::http_server::common_responses::user_details_lite::UserDetailsLight; +use crate::server_state::ServerState; + +#[derive(Serialize, Clone)] +pub struct GetVoiceResponse { + voice_token: ZsVoiceToken, + title: String, + + ietf_language_tag: String, + ietf_primary_language_subtag: String, + + creator: UserDetailsLight, + + creator_set_visibility: Visibility, + + created_at: DateTime, + updated_at: DateTime, +} + +#[derive(Deserialize)] +pub struct GetVoicePathInfo { + voice_token: String, +} + +#[derive(Debug)] +pub enum GetVoiceError { + NotAuthorized, + NotFound, + ServerError, +} + +impl fmt::Display for GetVoiceError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl ResponseError for GetVoiceError { + fn status_code(&self) -> StatusCode { + match *self { + GetVoiceError::NotAuthorized => StatusCode::UNAUTHORIZED, + GetVoiceError::ServerError => StatusCode::INTERNAL_SERVER_ERROR, + GetVoiceError::NotFound => StatusCode::NOT_FOUND, + } + } +} + +pub async fn get_voice_handler( + http_request: HttpRequest, + path: Path, + server_state: web::Data> +) -> Result { + + let maybe_user_session = server_state + .session_checker + .maybe_get_user_session(&http_request, &server_state.mysql_pool) + .await + .map_err(|e| { + warn!("Session checker error: {:?}", e); + GetVoiceError::ServerError + })?; + + let user_session = match maybe_user_session { + Some(session) => session, + None => { + warn!("not logged in"); + return Err(GetVoiceError::NotAuthorized); + } + }; + + let voice_token = path.voice_token.clone(); + let creator_user_token = user_session.user_token.clone(); + let is_mod = user_session.can_ban_users; + + let voice_lookup_result = get_voice_by_token( + &ZsVoiceToken::new(voice_token.clone()), + is_mod, + &server_state.mysql_pool, + ).await; + + let voice = match voice_lookup_result { + Ok(Some(voice)) => voice, + Ok(None) => { + warn!("Voice not found: {:?}", voice_token); + return Err(GetVoiceError::NotFound); + }, + Err(err) => { + warn!("Error looking up voice: {:?}", err); + return Err(GetVoiceError::ServerError); + } + }; + + let is_creator = voice.maybe_creator_user_token + .map(|creator_user_token| creator_user_token.as_str() == &user_session.user_token) + .unwrap_or(false); + + if !is_creator && !is_mod { + warn!("user is not allowed to view this voice: {}", user_session.user_token); + return Err(GetVoiceError::NotAuthorized); + } + + let response = GetVoiceResponse{ + voice_token: voice.token, + title: voice.title, + ietf_language_tag: voice.ietf_language_tag, + ietf_primary_language_subtag: voice.ietf_primary_language_subtag, + creator: UserDetailsLight::from_db_fields( + &UserToken::new_from_str(user_session.user_token.as_ref()), + user_session.username.as_ref(), + user_session.display_name.as_ref(), + user_session.email_gravatar_hash.as_ref(), + ), + creator_set_visibility: voice.creator_set_visibility, + created_at: voice.created_at, + updated_at: voice.updated_at, + }; + + let body = serde_json::to_string(&response) + .map_err(|e| GetVoiceError::ServerError)?; + + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voices/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voices/mod.rs index c36939b796..b9376c7e34 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voices/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/voice_designer/voices/mod.rs @@ -5,3 +5,4 @@ pub mod list_voices_by_session; pub mod list_voices_by_user; pub mod search_voices; pub mod update_voice; +pub mod get_voice; diff --git a/crates/service/web/storyteller_web/src/routes.rs b/crates/service/web/storyteller_web/src/routes.rs index 9080bac572..6602eaa552 100644 --- a/crates/service/web/storyteller_web/src/routes.rs +++ b/crates/service/web/storyteller_web/src/routes.rs @@ -116,11 +116,13 @@ use crate::http_server::endpoints::voice_designer::voice_dataset_samples::list_s use crate::http_server::endpoints::voice_designer::voice_dataset_samples::upload_sample::upload_sample_handler; use crate::http_server::endpoints::voice_designer::voice_datasets::create_dataset::create_dataset_handler; use crate::http_server::endpoints::voice_designer::voice_datasets::delete_dataset::delete_dataset_handler; +use crate::http_server::endpoints::voice_designer::voice_datasets::get_dataset::get_dataset_handler; use crate::http_server::endpoints::voice_designer::voice_datasets::list_datasets_by_session::list_datasets_by_session_handler; use crate::http_server::endpoints::voice_designer::voice_datasets::list_datasets_by_user::list_datasets_by_user_handler; use crate::http_server::endpoints::voice_designer::voice_datasets::update_dataset::update_dataset_handler; use crate::http_server::endpoints::voice_designer::voices::create_voice::create_voice_handler; use crate::http_server::endpoints::voice_designer::voices::delete_voice::delete_voice_handler; +use crate::http_server::endpoints::voice_designer::voices::get_voice::get_voice_handler; use crate::http_server::endpoints::voice_designer::voices::list_available_voices::list_available_voices_handler; use crate::http_server::endpoints::voice_designer::voices::list_voices_by_session::list_voices_by_session_handler; use crate::http_server::endpoints::voice_designer::voices::list_voices_by_user::list_voices_by_user_handler; @@ -1172,6 +1174,7 @@ fn add_voice_designer_routes (app:App)-> App .route(web::post().to(update_dataset_handler)) .route(web::head().to(|| HttpResponse::Ok())) ) + .route("/{dataset_token}", web::get().to(get_dataset_handler)) .route("/{dataset_token}/delete", web::delete().to(delete_dataset_handler)) .route("/user/{username}/list", web::get().to(list_datasets_by_user_handler)) .route("/session/list", web::get().to(list_datasets_by_session_handler)) @@ -1181,6 +1184,7 @@ fn add_voice_designer_routes (app:App)-> App .route("/list", web::get().to(list_available_voices_handler)) .route("/search", web::post().to(search_voices)) .route("/create", web::post().to(create_voice_handler)) + .route("/{voice_token}", web::get().to(get_voice_handler)) .route("/{voice_token}/update", web::post().to(update_voice_handler)) .route("/{voice_token}/delete", web::delete().to(delete_voice_handler)) .route("/user/{username}/list", web::get().to(list_voices_by_user_handler))