mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(themes): Add ability to update email config for themes (#8033)
This commit is contained in:
@ -29,7 +29,6 @@ pub struct UploadFileAssetData {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct UploadFileRequest {
|
||||
pub lineage: ThemeLineage,
|
||||
pub asset_name: String,
|
||||
pub asset_data: Secret<Vec<u8>>,
|
||||
}
|
||||
@ -44,9 +43,8 @@ pub struct CreateThemeRequest {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct UpdateThemeRequest {
|
||||
pub lineage: ThemeLineage,
|
||||
pub theme_data: ThemeData,
|
||||
// TODO: Add support to update email config
|
||||
pub theme_data: Option<ThemeData>,
|
||||
pub email_config: Option<EmailThemeConfig>,
|
||||
}
|
||||
|
||||
// All the below structs are for the theme.json file,
|
||||
|
||||
@ -18,7 +18,7 @@ use crate::{
|
||||
db_metrics::{track_database_call, DatabaseOperation},
|
||||
},
|
||||
schema::themes::dsl,
|
||||
user::theme::{Theme, ThemeNew},
|
||||
user::theme::{Theme, ThemeNew, ThemeUpdate, ThemeUpdateInternal},
|
||||
PgPooledConn, StorageResult,
|
||||
};
|
||||
|
||||
@ -131,6 +131,23 @@ impl Theme {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_by_theme_id(
|
||||
conn: &PgPooledConn,
|
||||
theme_id: String,
|
||||
update: ThemeUpdate,
|
||||
) -> StorageResult<Self> {
|
||||
let update_internal: ThemeUpdateInternal = update.into();
|
||||
|
||||
let predicate = dsl::theme_id.eq(theme_id);
|
||||
generics::generic_update_with_unique_predicate_get_result::<
|
||||
<Self as HasTable>::Table,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(conn, predicate, update_internal)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_by_theme_id_and_lineage(
|
||||
conn: &PgPooledConn,
|
||||
theme_id: String,
|
||||
|
||||
@ -3,7 +3,8 @@ use common_utils::{
|
||||
date_time, id_type,
|
||||
types::user::{EmailThemeConfig, ThemeLineage},
|
||||
};
|
||||
use diesel::{Identifiable, Insertable, Queryable, Selectable};
|
||||
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
|
||||
use router_derive::DebugAsDisplay;
|
||||
use time::PrimitiveDateTime;
|
||||
|
||||
use crate::schema::themes;
|
||||
@ -27,7 +28,7 @@ pub struct Theme {
|
||||
pub email_entity_logo_url: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
|
||||
#[derive(Clone, Debug, Insertable, DebugAsDisplay)]
|
||||
#[diesel(table_name = themes)]
|
||||
pub struct ThemeNew {
|
||||
pub theme_id: String,
|
||||
@ -85,3 +86,32 @@ impl Theme {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, AsChangeset, DebugAsDisplay)]
|
||||
#[diesel(table_name = themes)]
|
||||
pub struct ThemeUpdateInternal {
|
||||
pub email_primary_color: Option<String>,
|
||||
pub email_foreground_color: Option<String>,
|
||||
pub email_background_color: Option<String>,
|
||||
pub email_entity_name: Option<String>,
|
||||
pub email_entity_logo_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ThemeUpdate {
|
||||
EmailConfig { email_config: EmailThemeConfig },
|
||||
}
|
||||
|
||||
impl From<ThemeUpdate> for ThemeUpdateInternal {
|
||||
fn from(value: ThemeUpdate) -> Self {
|
||||
match value {
|
||||
ThemeUpdate::EmailConfig { email_config } => Self {
|
||||
email_primary_color: Some(email_config.primary_color),
|
||||
email_foreground_color: Some(email_config.foreground_color),
|
||||
email_background_color: Some(email_config.background_color),
|
||||
email_entity_name: Some(email_config.entity_name),
|
||||
email_entity_logo_url: Some(email_config.entity_logo_url),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ use common_utils::{
|
||||
ext_traits::{ByteSliceExt, Encode},
|
||||
types::user::ThemeLineage,
|
||||
};
|
||||
use diesel_models::user::theme::ThemeNew;
|
||||
use diesel_models::user::theme::{ThemeNew, ThemeUpdate};
|
||||
use error_stack::ResultExt;
|
||||
use hyperswitch_domain_models::api::ApplicationResponse;
|
||||
use masking::ExposeInterface;
|
||||
@ -91,17 +91,13 @@ pub async fn upload_file_to_theme_storage(
|
||||
) -> UserResponse<()> {
|
||||
let db_theme = state
|
||||
.store
|
||||
.find_theme_by_lineage(request.lineage)
|
||||
.find_theme_by_theme_id(theme_id)
|
||||
.await
|
||||
.to_not_found_response(UserErrors::ThemeNotFound)?;
|
||||
|
||||
if theme_id != db_theme.theme_id {
|
||||
return Err(UserErrors::ThemeNotFound.into());
|
||||
}
|
||||
|
||||
theme_utils::upload_file_to_theme_bucket(
|
||||
&state,
|
||||
&theme_utils::get_specific_file_key(&theme_id, &request.asset_name),
|
||||
&theme_utils::get_specific_file_key(&db_theme.theme_id, &request.asset_name),
|
||||
request.asset_data.expose(),
|
||||
)
|
||||
.await?;
|
||||
@ -175,26 +171,34 @@ pub async fn update_theme(
|
||||
theme_id: String,
|
||||
request: theme_api::UpdateThemeRequest,
|
||||
) -> UserResponse<theme_api::GetThemeResponse> {
|
||||
let db_theme = state
|
||||
.store
|
||||
.find_theme_by_lineage(request.lineage)
|
||||
.await
|
||||
.to_not_found_response(UserErrors::ThemeNotFound)?;
|
||||
let db_theme = match request.email_config {
|
||||
Some(email_config) => {
|
||||
let theme_update = ThemeUpdate::EmailConfig { email_config };
|
||||
state
|
||||
.store
|
||||
.update_theme_by_theme_id(theme_id.clone(), theme_update)
|
||||
.await
|
||||
.to_not_found_response(UserErrors::ThemeNotFound)?
|
||||
}
|
||||
None => state
|
||||
.store
|
||||
.find_theme_by_theme_id(theme_id)
|
||||
.await
|
||||
.to_not_found_response(UserErrors::ThemeNotFound)?,
|
||||
};
|
||||
|
||||
if theme_id != db_theme.theme_id {
|
||||
return Err(UserErrors::ThemeNotFound.into());
|
||||
if let Some(theme_data) = request.theme_data {
|
||||
theme_utils::upload_file_to_theme_bucket(
|
||||
&state,
|
||||
&theme_utils::get_theme_file_key(&db_theme.theme_id),
|
||||
theme_data
|
||||
.encode_to_vec()
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("Failed to parse ThemeData")?,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
theme_utils::upload_file_to_theme_bucket(
|
||||
&state,
|
||||
&theme_utils::get_theme_file_key(&db_theme.theme_id),
|
||||
request
|
||||
.theme_data
|
||||
.encode_to_vec()
|
||||
.change_context(UserErrors::InternalServerError)?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let file = theme_utils::retrieve_file_from_theme_bucket(
|
||||
&state,
|
||||
&theme_utils::get_theme_file_key(&db_theme.theme_id),
|
||||
|
||||
@ -4137,6 +4137,16 @@ impl ThemeInterface for KafkaStore {
|
||||
self.diesel_store.find_theme_by_lineage(lineage).await
|
||||
}
|
||||
|
||||
async fn update_theme_by_theme_id(
|
||||
&self,
|
||||
theme_id: String,
|
||||
theme_update: diesel_models::user::theme::ThemeUpdate,
|
||||
) -> CustomResult<diesel_models::user::theme::Theme, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.update_theme_by_theme_id(theme_id, theme_update)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delete_theme_by_lineage_and_theme_id(
|
||||
&self,
|
||||
theme_id: String,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use common_utils::types::user::ThemeLineage;
|
||||
use diesel_models::user::theme as storage;
|
||||
use diesel_models::user::theme::{self as storage, ThemeUpdate};
|
||||
use error_stack::report;
|
||||
|
||||
use super::MockDb;
|
||||
@ -31,6 +31,12 @@ pub trait ThemeInterface {
|
||||
lineage: ThemeLineage,
|
||||
) -> CustomResult<storage::Theme, errors::StorageError>;
|
||||
|
||||
async fn update_theme_by_theme_id(
|
||||
&self,
|
||||
theme_id: String,
|
||||
theme_update: ThemeUpdate,
|
||||
) -> CustomResult<storage::Theme, errors::StorageError>;
|
||||
|
||||
async fn delete_theme_by_lineage_and_theme_id(
|
||||
&self,
|
||||
theme_id: String,
|
||||
@ -81,6 +87,17 @@ impl ThemeInterface for Store {
|
||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||
}
|
||||
|
||||
async fn update_theme_by_theme_id(
|
||||
&self,
|
||||
theme_id: String,
|
||||
theme_update: ThemeUpdate,
|
||||
) -> CustomResult<storage::Theme, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::Theme::update_by_theme_id(&conn, theme_id, theme_update)
|
||||
.await
|
||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||
}
|
||||
|
||||
async fn delete_theme_by_lineage_and_theme_id(
|
||||
&self,
|
||||
theme_id: String,
|
||||
@ -256,6 +273,35 @@ impl ThemeInterface for MockDb {
|
||||
)
|
||||
}
|
||||
|
||||
async fn update_theme_by_theme_id(
|
||||
&self,
|
||||
theme_id: String,
|
||||
theme_update: ThemeUpdate,
|
||||
) -> CustomResult<storage::Theme, errors::StorageError> {
|
||||
let mut themes = self.themes.lock().await;
|
||||
themes
|
||||
.iter_mut()
|
||||
.find(|theme| theme.theme_id == theme_id)
|
||||
.map(|theme| {
|
||||
match theme_update {
|
||||
ThemeUpdate::EmailConfig { email_config } => {
|
||||
theme.email_primary_color = email_config.primary_color;
|
||||
theme.email_foreground_color = email_config.foreground_color;
|
||||
theme.email_background_color = email_config.background_color;
|
||||
theme.email_entity_name = email_config.entity_name;
|
||||
theme.email_entity_logo_url = email_config.entity_logo_url;
|
||||
}
|
||||
}
|
||||
theme.clone()
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
report!(errors::StorageError::ValueNotFound(format!(
|
||||
"Theme with id {} not found",
|
||||
theme_id,
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
async fn delete_theme_by_lineage_and_theme_id(
|
||||
&self,
|
||||
theme_id: String,
|
||||
|
||||
@ -56,12 +56,10 @@ pub async fn upload_file_to_theme_storage(
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
MultipartForm(payload): MultipartForm<theme_api::UploadFileAssetData>,
|
||||
query: web::Query<ThemeLineage>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::UploadFileToThemeStorage;
|
||||
let theme_id = path.into_inner();
|
||||
let payload = theme_api::UploadFileRequest {
|
||||
lineage: query.into_inner(),
|
||||
asset_name: payload.asset_name.into_inner(),
|
||||
asset_data: Secret::new(payload.asset_data.data.to_vec()),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user