feat(users): refactor ProdIntent to support product-type context and merchant-scope (#7638)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Sandeep Kumar
2025-04-10 15:14:07 +05:30
committed by GitHub
parent 203ae3e97e
commit bbd2102274
10 changed files with 51 additions and 19 deletions

View File

@ -1,4 +1,4 @@
use common_enums::CountryAlpha2; use common_enums::{CountryAlpha2, MerchantProductType};
use common_utils::{id_type, pii}; use common_utils::{id_type, pii};
use masking::Secret; use masking::Secret;
use strum::EnumString; use strum::EnumString;
@ -103,6 +103,8 @@ pub struct ProdIntent {
pub poc_contact: Option<String>, pub poc_contact: Option<String>,
pub comments: Option<String>, pub comments: Option<String>,
pub is_completed: bool, pub is_completed: bool,
#[serde(default)]
pub product_type: MerchantProductType,
} }
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]

View File

@ -1,6 +1,7 @@
use serde; use serde;
use utoipa::ToSchema; use utoipa::ToSchema;
#[derive( #[derive(
Copy,
Default, Default,
Clone, Clone,
Debug, Debug,

View File

@ -2974,7 +2974,7 @@ pub async fn list_merchants_for_user_in_org(
.map(|merchant_account| user_api::UserMerchantAccountResponse { .map(|merchant_account| user_api::UserMerchantAccountResponse {
merchant_name: merchant_account.merchant_name.clone(), merchant_name: merchant_account.merchant_name.clone(),
merchant_id: merchant_account.get_id().to_owned(), merchant_id: merchant_account.get_id().to_owned(),
product_type: merchant_account.product_type.clone(), product_type: merchant_account.product_type,
version: merchant_account.version, version: merchant_account.version,
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),

View File

@ -38,7 +38,6 @@ pub async fn set_metadata(
Ok(ApplicationResponse::StatusOk) Ok(ApplicationResponse::StatusOk)
} }
#[cfg(feature = "v1")]
pub async fn get_multiple_metadata( pub async fn get_multiple_metadata(
state: SessionState, state: SessionState,
user: UserFromToken, user: UserFromToken,
@ -462,7 +461,7 @@ async fn insert_metadata(
pii::Email::from_str(inner_poc_email) pii::Email::from_str(inner_poc_email)
.change_context(UserErrors::EmailParsingError)?; .change_context(UserErrors::EmailParsingError)?;
} }
let mut metadata = utils::insert_user_scoped_metadata_to_db( let mut metadata = utils::insert_merchant_scoped_metadata_to_db(
state, state,
user.user_id.clone(), user.user_id.clone(),
user.merchant_id.clone(), user.merchant_id.clone(),
@ -473,7 +472,7 @@ async fn insert_metadata(
.await; .await;
if utils::is_update_required(&metadata) { if utils::is_update_required(&metadata) {
metadata = utils::update_user_scoped_metadata( metadata = utils::update_merchant_scoped_metadata(
state, state,
user.user_id.clone(), user.user_id.clone(),
user.merchant_id.clone(), user.merchant_id.clone(),
@ -500,7 +499,6 @@ async fn insert_metadata(
EntityType::Merchant, EntityType::Merchant,
) )
.await?; .await?;
let email_contents = email_types::BizEmailProd::new( let email_contents = email_types::BizEmailProd::new(
state, state,
data, data,
@ -662,7 +660,6 @@ async fn fetch_metadata(
Ok(dashboard_metadata) Ok(dashboard_metadata)
} }
#[cfg(feature = "v1")]
pub async fn backfill_metadata( pub async fn backfill_metadata(
state: &SessionState, state: &SessionState,
user: &UserFromToken, user: &UserFromToken,
@ -705,6 +702,11 @@ pub async fn backfill_metadata(
return Ok(None); return Ok(None);
}; };
#[cfg(feature = "v1")]
let processor_name = mca.connector_name.clone();
#[cfg(feature = "v2")]
let processor_name = mca.connector_name.to_string().clone();
Some( Some(
insert_metadata( insert_metadata(
state, state,
@ -712,13 +714,14 @@ pub async fn backfill_metadata(
DBEnum::StripeConnected, DBEnum::StripeConnected,
types::MetaData::StripeConnected(api::ProcessorConnected { types::MetaData::StripeConnected(api::ProcessorConnected {
processor_id: mca.get_id(), processor_id: mca.get_id(),
processor_name: mca.connector_name, processor_name,
}), }),
) )
.await, .await,
) )
.transpose() .transpose()
} }
DBEnum::PaypalConnected => { DBEnum::PaypalConnected => {
let mca = if let Some(paypal_connected) = get_merchant_connector_account_by_name( let mca = if let Some(paypal_connected) = get_merchant_connector_account_by_name(
state, state,
@ -745,6 +748,11 @@ pub async fn backfill_metadata(
return Ok(None); return Ok(None);
}; };
#[cfg(feature = "v1")]
let processor_name = mca.connector_name.clone();
#[cfg(feature = "v2")]
let processor_name = mca.connector_name.to_string().clone();
Some( Some(
insert_metadata( insert_metadata(
state, state,
@ -752,7 +760,7 @@ pub async fn backfill_metadata(
DBEnum::PaypalConnected, DBEnum::PaypalConnected,
types::MetaData::PaypalConnected(api::ProcessorConnected { types::MetaData::PaypalConnected(api::ProcessorConnected {
processor_id: mca.get_id(), processor_id: mca.get_id(),
processor_name: mca.connector_name, processor_name,
}), }),
) )
.await, .await,

View File

@ -2136,6 +2136,12 @@ impl User {
), ),
); );
route = route.service(
web::resource("/data")
.route(web::get().to(user::get_multiple_dashboard_metadata))
.route(web::post().to(user::set_dashboard_metadata)),
);
route route
} }
} }

View File

@ -197,7 +197,6 @@ pub async fn set_dashboard_metadata(
.await .await
} }
#[cfg(feature = "v1")]
pub async fn get_multiple_dashboard_metadata( pub async fn get_multiple_dashboard_metadata(
state: web::Data<AppState>, state: web::Data<AppState>,
req: HttpRequest, req: HttpRequest,

View File

@ -104,6 +104,10 @@
<strong>Business Website:</strong> <strong>Business Website:</strong>
{business_website} {business_website}
</li> </li>
<li>
<strong>Product Type:</strong>
{product_type}
</li>
</ol> </ol>
</td> </td>
</tr> </tr>

View File

@ -1,5 +1,5 @@
use api_models::user::dashboard_metadata::ProdIntent; use api_models::user::dashboard_metadata::ProdIntent;
use common_enums::EntityType; use common_enums::{EntityType, MerchantProductType};
use common_utils::{errors::CustomResult, pii, types::theme::EmailThemeConfig}; use common_utils::{errors::CustomResult, pii, types::theme::EmailThemeConfig};
use error_stack::ResultExt; use error_stack::ResultExt;
use external_services::email::{EmailContents, EmailData, EmailError}; use external_services::email::{EmailContents, EmailData, EmailError};
@ -64,6 +64,7 @@ pub enum EmailBody {
legal_business_name: String, legal_business_name: String,
business_location: String, business_location: String,
business_website: String, business_website: String,
product_type: MerchantProductType,
}, },
ReconActivation { ReconActivation {
user_name: String, user_name: String,
@ -199,6 +200,7 @@ pub mod html {
legal_business_name, legal_business_name,
business_location, business_location,
business_website, business_website,
product_type,
} => { } => {
format!( format!(
include_str!("assets/bizemailprod.html"), include_str!("assets/bizemailprod.html"),
@ -207,6 +209,7 @@ pub mod html {
business_location = business_location, business_location = business_location,
business_website = business_website, business_website = business_website,
username = user_name, username = user_name,
product_type = product_type
) )
} }
EmailBody::ProFeatureRequest { EmailBody::ProFeatureRequest {
@ -558,6 +561,7 @@ pub struct BizEmailProd {
pub settings: std::sync::Arc<configs::Settings>, pub settings: std::sync::Arc<configs::Settings>,
pub theme_id: Option<String>, pub theme_id: Option<String>,
pub theme_config: EmailThemeConfig, pub theme_config: EmailThemeConfig,
pub product_type: MerchantProductType,
} }
impl BizEmailProd { impl BizEmailProd {
@ -582,6 +586,7 @@ impl BizEmailProd {
business_website: data.business_website.unwrap_or_default(), business_website: data.business_website.unwrap_or_default(),
theme_id, theme_id,
theme_config, theme_config,
product_type: data.product_type,
}) })
} }
} }
@ -595,6 +600,7 @@ impl EmailData for BizEmailProd {
legal_business_name: self.legal_business_name.clone(), legal_business_name: self.legal_business_name.clone(),
business_location: self.business_location.clone(), business_location: self.business_location.clone(),
business_website: self.business_website.clone(), business_website: self.business_website.clone(),
product_type: self.product_type,
}); });
Ok(EmailContents { Ok(EmailContents {

View File

@ -417,7 +417,7 @@ impl NewUserMerchant {
} }
pub fn get_product_type(&self) -> Option<common_enums::MerchantProductType> { pub fn get_product_type(&self) -> Option<common_enums::MerchantProductType> {
self.product_type.clone() self.product_type
} }
pub async fn check_if_already_exists_in_db(&self, state: SessionState) -> UserResult<()> { pub async fn check_if_already_exists_in_db(&self, state: SessionState) -> UserResult<()> {
@ -703,11 +703,18 @@ impl TryFrom<UserMerchantCreateRequestWithToken> for NewUserMerchant {
} else { } else {
id_type::MerchantId::new_from_unix_timestamp() id_type::MerchantId::new_from_unix_timestamp()
}; };
let (user_from_storage, user_merchant_create, user_from_token) = value;
Ok(Self { Ok(Self {
merchant_id, merchant_id,
company_name: Some(UserCompanyName::new(value.1.company_name.clone())?), company_name: Some(UserCompanyName::new(
product_type: value.1.product_type.clone(), user_merchant_create.company_name.clone(),
new_organization: NewUserOrganization::from(value), )?),
product_type: user_merchant_create.product_type,
new_organization: NewUserOrganization::from((
user_from_storage,
user_merchant_create,
user_from_token,
)),
}) })
} }
} }

View File

@ -218,10 +218,9 @@ pub fn separate_metadata_type_based_on_scope(
| DBEnum::SetupWoocomWebhook | DBEnum::SetupWoocomWebhook
| DBEnum::OnboardingSurvey | DBEnum::OnboardingSurvey
| DBEnum::IsMultipleConfiguration | DBEnum::IsMultipleConfiguration
| DBEnum::ReconStatus => merchant_scoped.push(key), | DBEnum::ReconStatus
DBEnum::Feedback | DBEnum::ProdIntent | DBEnum::IsChangePasswordRequired => { | DBEnum::ProdIntent => merchant_scoped.push(key),
user_scoped.push(key) DBEnum::Feedback | DBEnum::IsChangePasswordRequired => user_scoped.push(key),
}
} }
} }
(merchant_scoped, user_scoped) (merchant_scoped, user_scoped)