mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(user): add email apis and new enums for metadata (#3053)
Co-authored-by: Rachit Naithani <rachit.naithani@juspay.in> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Rachit Naithani <81706961+racnan@users.noreply.github.com>
This commit is contained in:
@ -7,7 +7,8 @@ use crate::user::{
|
||||
GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest,
|
||||
},
|
||||
AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest,
|
||||
DashboardEntryResponse, GetUsersResponse, SignUpRequest, SignUpWithMerchantIdRequest,
|
||||
DashboardEntryResponse, ForgotPasswordRequest, GetUsersResponse, InviteUserRequest,
|
||||
InviteUserResponse, ResetPasswordRequest, SignUpRequest, SignUpWithMerchantIdRequest,
|
||||
SwitchMerchantIdRequest, UserMerchantCreate,
|
||||
};
|
||||
|
||||
@ -33,7 +34,11 @@ common_utils::impl_misc_api_event_type!(
|
||||
UserMerchantCreate,
|
||||
GetUsersResponse,
|
||||
AuthorizeResponse,
|
||||
ConnectAccountRequest
|
||||
ConnectAccountRequest,
|
||||
ForgotPasswordRequest,
|
||||
ResetPasswordRequest,
|
||||
InviteUserRequest,
|
||||
InviteUserResponse
|
||||
);
|
||||
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
|
||||
@ -174,7 +174,7 @@ pub struct RefundListMetaData {
|
||||
pub currency: Vec<enums::Currency>,
|
||||
/// The list of available refund status filters
|
||||
#[schema(value_type = Vec<RefundStatus>)]
|
||||
pub status: Vec<enums::RefundStatus>,
|
||||
pub refund_status: Vec<enums::RefundStatus>,
|
||||
}
|
||||
|
||||
/// The status for refunds
|
||||
|
||||
@ -65,6 +65,29 @@ pub struct ChangePasswordRequest {
|
||||
pub old_password: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, serde::Serialize)]
|
||||
pub struct ForgotPasswordRequest {
|
||||
pub email: pii::Email,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, serde::Serialize)]
|
||||
pub struct ResetPasswordRequest {
|
||||
pub token: Secret<String>,
|
||||
pub password: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct InviteUserRequest {
|
||||
pub email: pii::Email,
|
||||
pub name: Secret<String>,
|
||||
pub role_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct InviteUserResponse {
|
||||
pub is_email_sent: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
pub struct SwitchMerchantIdRequest {
|
||||
pub merchant_id: String,
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use common_enums::CountryAlpha2;
|
||||
use common_utils::pii;
|
||||
use masking::Secret;
|
||||
use strum::EnumString;
|
||||
|
||||
@ -12,8 +14,11 @@ pub enum SetMetaDataRequest {
|
||||
ConfiguredRouting(ConfiguredRouting),
|
||||
TestPayment(TestPayment),
|
||||
IntegrationMethod(IntegrationMethod),
|
||||
ConfigurationType(ConfigurationType),
|
||||
IntegrationCompleted,
|
||||
SPRoutingConfigured(ConfiguredRouting),
|
||||
Feedback(Feedback),
|
||||
ProdIntent(ProdIntent),
|
||||
SPTestPayment,
|
||||
DownloadWoocom,
|
||||
ConfigureWoocom,
|
||||
@ -49,10 +54,38 @@ pub struct TestPayment {
|
||||
pub payment_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct IntegrationMethod {
|
||||
pub integration_type: String,
|
||||
}
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub enum ConfigurationType {
|
||||
Single,
|
||||
Multiple,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct Feedback {
|
||||
pub email: pii::Email,
|
||||
pub description: Option<String>,
|
||||
pub rating: Option<i32>,
|
||||
pub category: Option<String>,
|
||||
}
|
||||
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct ProdIntent {
|
||||
pub legal_business_name: Option<String>,
|
||||
pub business_label: Option<String>,
|
||||
pub business_location: Option<CountryAlpha2>,
|
||||
pub display_name: Option<String>,
|
||||
pub poc_email: Option<String>,
|
||||
pub business_type: Option<String>,
|
||||
pub business_identifier: Option<String>,
|
||||
pub business_website: Option<String>,
|
||||
pub poc_name: Option<String>,
|
||||
pub poc_contact: Option<String>,
|
||||
pub comments: Option<String>,
|
||||
pub is_completed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, EnumString, serde::Serialize)]
|
||||
pub enum GetMetaDataRequest {
|
||||
@ -65,10 +98,13 @@ pub enum GetMetaDataRequest {
|
||||
ConfiguredRouting,
|
||||
TestPayment,
|
||||
IntegrationMethod,
|
||||
ConfigurationType,
|
||||
IntegrationCompleted,
|
||||
StripeConnected,
|
||||
PaypalConnected,
|
||||
SPRoutingConfigured,
|
||||
Feedback,
|
||||
ProdIntent,
|
||||
SPTestPayment,
|
||||
DownloadWoocom,
|
||||
ConfigureWoocom,
|
||||
@ -98,10 +134,13 @@ pub enum GetMetaDataResponse {
|
||||
ConfiguredRouting(Option<ConfiguredRouting>),
|
||||
TestPayment(Option<TestPayment>),
|
||||
IntegrationMethod(Option<IntegrationMethod>),
|
||||
ConfigurationType(Option<ConfigurationType>),
|
||||
IntegrationCompleted(bool),
|
||||
StripeConnected(Option<ProcessorConnected>),
|
||||
PaypalConnected(Option<ProcessorConnected>),
|
||||
SPRoutingConfigured(Option<ConfiguredRouting>),
|
||||
Feedback(Option<Feedback>),
|
||||
ProdIntent(Option<ProdIntent>),
|
||||
SPTestPayment(bool),
|
||||
DownloadWoocom(bool),
|
||||
ConfigureWoocom(bool),
|
||||
|
||||
@ -452,10 +452,13 @@ pub enum DashboardMetadata {
|
||||
ConfiguredRouting,
|
||||
TestPayment,
|
||||
IntegrationMethod,
|
||||
ConfigurationType,
|
||||
IntegrationCompleted,
|
||||
StripeConnected,
|
||||
PaypalConnected,
|
||||
SpRoutingConfigured,
|
||||
Feedback,
|
||||
ProdIntent,
|
||||
SpTestPayment,
|
||||
DownloadWoocom,
|
||||
ConfigureWoocom,
|
||||
|
||||
@ -28,6 +28,12 @@ impl DashboardMetadata {
|
||||
data_key: enums::DashboardMetadata,
|
||||
dashboard_metadata_update: DashboardMetadataUpdate,
|
||||
) -> StorageResult<Self> {
|
||||
let predicate = dsl::merchant_id
|
||||
.eq(merchant_id.to_owned())
|
||||
.and(dsl::org_id.eq(org_id.to_owned()))
|
||||
.and(dsl::data_key.eq(data_key.to_owned()));
|
||||
|
||||
if let Some(uid) = user_id {
|
||||
generics::generic_update_with_unique_predicate_get_result::<
|
||||
<Self as HasTable>::Table,
|
||||
_,
|
||||
@ -35,14 +41,23 @@ impl DashboardMetadata {
|
||||
_,
|
||||
>(
|
||||
conn,
|
||||
dsl::user_id
|
||||
.eq(user_id.to_owned())
|
||||
.and(dsl::merchant_id.eq(merchant_id.to_owned()))
|
||||
.and(dsl::org_id.eq(org_id.to_owned()))
|
||||
.and(dsl::data_key.eq(data_key.to_owned())),
|
||||
predicate.and(dsl::user_id.eq(uid)),
|
||||
DashboardMetadataUpdateInternal::from(dashboard_metadata_update),
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
generics::generic_update_with_unique_predicate_get_result::<
|
||||
<Self as HasTable>::Table,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
conn,
|
||||
predicate.and(dsl::user_id.is_null()),
|
||||
DashboardMetadataUpdateInternal::from(dashboard_metadata_update),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn find_user_scoped_dashboard_metadata(
|
||||
|
||||
@ -12,8 +12,12 @@ pub enum UserErrors {
|
||||
InternalServerError,
|
||||
#[error("InvalidCredentials")]
|
||||
InvalidCredentials,
|
||||
#[error("UserNotFound")]
|
||||
UserNotFound,
|
||||
#[error("UserExists")]
|
||||
UserExists,
|
||||
#[error("LinkInvalid")]
|
||||
LinkInvalid,
|
||||
#[error("InvalidOldPassword")]
|
||||
InvalidOldPassword,
|
||||
#[error("EmailParsingError")]
|
||||
@ -60,12 +64,21 @@ impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorRespon
|
||||
"Incorrect email or password",
|
||||
None,
|
||||
)),
|
||||
Self::UserNotFound => AER::Unauthorized(ApiError::new(
|
||||
sub_code,
|
||||
2,
|
||||
"Email doesn’t exist. Register",
|
||||
None,
|
||||
)),
|
||||
Self::UserExists => AER::BadRequest(ApiError::new(
|
||||
sub_code,
|
||||
3,
|
||||
"An account already exists with this email",
|
||||
None,
|
||||
)),
|
||||
Self::LinkInvalid => {
|
||||
AER::Unauthorized(ApiError::new(sub_code, 4, "Invalid or expired link", None))
|
||||
}
|
||||
Self::InvalidOldPassword => AER::BadRequest(ApiError::new(
|
||||
sub_code,
|
||||
6,
|
||||
|
||||
@ -11,7 +11,7 @@ use router_env::logger;
|
||||
|
||||
use super::errors::{UserErrors, UserResponse};
|
||||
#[cfg(feature = "email")]
|
||||
use crate::services::email::types as email_types;
|
||||
use crate::services::email::{types as email_types, types::EmailToken};
|
||||
use crate::{
|
||||
consts,
|
||||
db::user::UserInterface,
|
||||
@ -235,8 +235,7 @@ pub async fn change_password(
|
||||
user.compare_password(request.old_password)
|
||||
.change_context(UserErrors::InvalidOldPassword)?;
|
||||
|
||||
let new_password_hash =
|
||||
crate::utils::user::password::generate_password_hash(request.new_password)?;
|
||||
let new_password_hash = utils::user::password::generate_password_hash(request.new_password)?;
|
||||
|
||||
let _ = UserInterface::update_user_by_user_id(
|
||||
&*state.store,
|
||||
@ -253,6 +252,182 @@ pub async fn change_password(
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn forgot_password(
|
||||
state: AppState,
|
||||
request: user_api::ForgotPasswordRequest,
|
||||
) -> UserResponse<()> {
|
||||
let user_email = domain::UserEmail::from_pii_email(request.email)?;
|
||||
|
||||
let user_from_db = state
|
||||
.store
|
||||
.find_user_by_email(user_email.get_secret().expose().as_str())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
if e.current_context().is_db_not_found() {
|
||||
e.change_context(UserErrors::UserNotFound)
|
||||
} else {
|
||||
e.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
})
|
||||
.map(domain::UserFromStorage::from)?;
|
||||
|
||||
let email_contents = email_types::ResetPassword {
|
||||
recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?,
|
||||
settings: state.conf.clone(),
|
||||
user_name: domain::UserName::new(user_from_db.get_name())?,
|
||||
subject: "Get back to Hyperswitch - Reset Your Password Now",
|
||||
};
|
||||
|
||||
state
|
||||
.email_client
|
||||
.compose_and_send_email(
|
||||
Box::new(email_contents),
|
||||
state.conf.proxy.https_url.as_ref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| e.change_context(UserErrors::InternalServerError))?;
|
||||
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn reset_password(
|
||||
state: AppState,
|
||||
request: user_api::ResetPasswordRequest,
|
||||
) -> UserResponse<()> {
|
||||
let token = auth::decode_jwt::<EmailToken>(request.token.expose().as_str(), &state)
|
||||
.await
|
||||
.change_context(UserErrors::LinkInvalid)?;
|
||||
|
||||
let password = domain::UserPassword::new(request.password)?;
|
||||
|
||||
let hash_password = utils::user::password::generate_password_hash(password.get_secret())?;
|
||||
|
||||
//TODO: Create Update by email query
|
||||
let user_id = state
|
||||
.store
|
||||
.find_user_by_email(token.get_email())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.user_id;
|
||||
|
||||
state
|
||||
.store
|
||||
.update_user_by_user_id(
|
||||
user_id.as_str(),
|
||||
storage_user::UserUpdate::AccountUpdate {
|
||||
name: None,
|
||||
password: Some(hash_password),
|
||||
is_verified: Some(true),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
//TODO: Update User role status for invited user
|
||||
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn invite_user(
|
||||
state: AppState,
|
||||
request: user_api::InviteUserRequest,
|
||||
user_from_token: auth::UserFromToken,
|
||||
) -> UserResponse<user_api::InviteUserResponse> {
|
||||
let inviter_user = state
|
||||
.store
|
||||
.find_user_by_id(user_from_token.user_id.as_str())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
if inviter_user.email == request.email {
|
||||
return Err(UserErrors::InvalidRoleOperation.into())
|
||||
.attach_printable("User Inviting themself");
|
||||
}
|
||||
|
||||
utils::user_role::validate_role_id(request.role_id.as_str())?;
|
||||
let invitee_email = domain::UserEmail::from_pii_email(request.email.clone())?;
|
||||
|
||||
let invitee_user = state
|
||||
.store
|
||||
.find_user_by_email(invitee_email.clone().get_secret().expose().as_str())
|
||||
.await;
|
||||
|
||||
if let Ok(invitee_user) = invitee_user {
|
||||
let invitee_user_from_db = domain::UserFromStorage::from(invitee_user);
|
||||
|
||||
let now = common_utils::date_time::now();
|
||||
use diesel_models::user_role::UserRoleNew;
|
||||
state
|
||||
.store
|
||||
.insert_user_role(UserRoleNew {
|
||||
user_id: invitee_user_from_db.get_user_id().to_owned(),
|
||||
merchant_id: user_from_token.merchant_id,
|
||||
role_id: request.role_id,
|
||||
org_id: user_from_token.org_id,
|
||||
status: UserStatus::Active,
|
||||
created_by: user_from_token.user_id.clone(),
|
||||
last_modified_by: user_from_token.user_id,
|
||||
created_at: now,
|
||||
last_modified_at: now,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
if e.current_context().is_db_unique_violation() {
|
||||
e.change_context(UserErrors::UserExists)
|
||||
} else {
|
||||
e.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(ApplicationResponse::Json(user_api::InviteUserResponse {
|
||||
is_email_sent: false,
|
||||
}))
|
||||
} else if invitee_user
|
||||
.as_ref()
|
||||
.map_err(|e| e.current_context().is_db_not_found())
|
||||
.err()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let new_user = domain::NewUser::try_from((request.clone(), user_from_token))?;
|
||||
|
||||
new_user
|
||||
.insert_user_in_db(state.store.as_ref())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
new_user
|
||||
.clone()
|
||||
.insert_user_role_in_db(state.clone(), request.role_id, UserStatus::InvitationSent)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?;
|
||||
|
||||
let email_contents = email_types::InviteUser {
|
||||
recipient_email: invitee_email,
|
||||
user_name: domain::UserName::new(new_user.get_name())?,
|
||||
settings: state.conf.clone(),
|
||||
subject: "You have been invited to join Hyperswitch Community!",
|
||||
};
|
||||
|
||||
let send_email_result = state
|
||||
.email_client
|
||||
.compose_and_send_email(
|
||||
Box::new(email_contents),
|
||||
state.conf.proxy.https_url.as_ref(),
|
||||
)
|
||||
.await;
|
||||
|
||||
logger::info!(?send_email_result);
|
||||
|
||||
Ok(ApplicationResponse::Json(user_api::InviteUserResponse {
|
||||
is_email_sent: send_email_result.is_ok(),
|
||||
}))
|
||||
} else {
|
||||
Err(UserErrors::InternalServerError.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_internal_user(
|
||||
state: AppState,
|
||||
request: user_api::CreateInternalUserRequest,
|
||||
|
||||
@ -81,12 +81,17 @@ fn parse_set_request(data_enum: api::SetMetaDataRequest) -> UserResult<types::Me
|
||||
api::SetMetaDataRequest::IntegrationMethod(req) => {
|
||||
Ok(types::MetaData::IntegrationMethod(req))
|
||||
}
|
||||
api::SetMetaDataRequest::ConfigurationType(req) => {
|
||||
Ok(types::MetaData::ConfigurationType(req))
|
||||
}
|
||||
api::SetMetaDataRequest::IntegrationCompleted => {
|
||||
Ok(types::MetaData::IntegrationCompleted(true))
|
||||
}
|
||||
api::SetMetaDataRequest::SPRoutingConfigured(req) => {
|
||||
Ok(types::MetaData::SPRoutingConfigured(req))
|
||||
}
|
||||
api::SetMetaDataRequest::Feedback(req) => Ok(types::MetaData::Feedback(req)),
|
||||
api::SetMetaDataRequest::ProdIntent(req) => Ok(types::MetaData::ProdIntent(req)),
|
||||
api::SetMetaDataRequest::SPTestPayment => Ok(types::MetaData::SPTestPayment(true)),
|
||||
api::SetMetaDataRequest::DownloadWoocom => Ok(types::MetaData::DownloadWoocom(true)),
|
||||
api::SetMetaDataRequest::ConfigureWoocom => Ok(types::MetaData::ConfigureWoocom(true)),
|
||||
@ -110,10 +115,13 @@ fn parse_get_request(data_enum: api::GetMetaDataRequest) -> DBEnum {
|
||||
api::GetMetaDataRequest::ConfiguredRouting => DBEnum::ConfiguredRouting,
|
||||
api::GetMetaDataRequest::TestPayment => DBEnum::TestPayment,
|
||||
api::GetMetaDataRequest::IntegrationMethod => DBEnum::IntegrationMethod,
|
||||
api::GetMetaDataRequest::ConfigurationType => DBEnum::ConfigurationType,
|
||||
api::GetMetaDataRequest::IntegrationCompleted => DBEnum::IntegrationCompleted,
|
||||
api::GetMetaDataRequest::StripeConnected => DBEnum::StripeConnected,
|
||||
api::GetMetaDataRequest::PaypalConnected => DBEnum::PaypalConnected,
|
||||
api::GetMetaDataRequest::SPRoutingConfigured => DBEnum::SpRoutingConfigured,
|
||||
api::GetMetaDataRequest::Feedback => DBEnum::Feedback,
|
||||
api::GetMetaDataRequest::ProdIntent => DBEnum::ProdIntent,
|
||||
api::GetMetaDataRequest::SPTestPayment => DBEnum::SpTestPayment,
|
||||
api::GetMetaDataRequest::DownloadWoocom => DBEnum::DownloadWoocom,
|
||||
api::GetMetaDataRequest::ConfigureWoocom => DBEnum::ConfigureWoocom,
|
||||
@ -158,6 +166,10 @@ fn into_response(
|
||||
let resp = utils::deserialize_to_response(data)?;
|
||||
Ok(api::GetMetaDataResponse::IntegrationMethod(resp))
|
||||
}
|
||||
DBEnum::ConfigurationType => {
|
||||
let resp = utils::deserialize_to_response(data)?;
|
||||
Ok(api::GetMetaDataResponse::ConfigurationType(resp))
|
||||
}
|
||||
DBEnum::IntegrationCompleted => Ok(api::GetMetaDataResponse::IntegrationCompleted(
|
||||
data.is_some(),
|
||||
)),
|
||||
@ -173,6 +185,14 @@ fn into_response(
|
||||
let resp = utils::deserialize_to_response(data)?;
|
||||
Ok(api::GetMetaDataResponse::SPRoutingConfigured(resp))
|
||||
}
|
||||
DBEnum::Feedback => {
|
||||
let resp = utils::deserialize_to_response(data)?;
|
||||
Ok(api::GetMetaDataResponse::Feedback(resp))
|
||||
}
|
||||
DBEnum::ProdIntent => {
|
||||
let resp = utils::deserialize_to_response(data)?;
|
||||
Ok(api::GetMetaDataResponse::ProdIntent(resp))
|
||||
}
|
||||
DBEnum::SpTestPayment => Ok(api::GetMetaDataResponse::SPTestPayment(data.is_some())),
|
||||
DBEnum::DownloadWoocom => Ok(api::GetMetaDataResponse::DownloadWoocom(data.is_some())),
|
||||
DBEnum::ConfigureWoocom => Ok(api::GetMetaDataResponse::ConfigureWoocom(data.is_some())),
|
||||
@ -282,7 +302,18 @@ async fn insert_metadata(
|
||||
.await
|
||||
}
|
||||
types::MetaData::IntegrationMethod(data) => {
|
||||
utils::insert_merchant_scoped_metadata_to_db(
|
||||
let mut metadata = utils::insert_merchant_scoped_metadata_to_db(
|
||||
state,
|
||||
user.user_id.clone(),
|
||||
user.merchant_id.clone(),
|
||||
user.org_id.clone(),
|
||||
metadata_key,
|
||||
data.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
if utils::is_update_required(&metadata) {
|
||||
metadata = utils::update_merchant_scoped_metadata(
|
||||
state,
|
||||
user.user_id,
|
||||
user.merchant_id,
|
||||
@ -291,6 +322,34 @@ async fn insert_metadata(
|
||||
data,
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError);
|
||||
}
|
||||
metadata
|
||||
}
|
||||
types::MetaData::ConfigurationType(data) => {
|
||||
let mut metadata = utils::insert_merchant_scoped_metadata_to_db(
|
||||
state,
|
||||
user.user_id.clone(),
|
||||
user.merchant_id.clone(),
|
||||
user.org_id.clone(),
|
||||
metadata_key,
|
||||
data.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
if utils::is_update_required(&metadata) {
|
||||
metadata = utils::update_merchant_scoped_metadata(
|
||||
state,
|
||||
user.user_id,
|
||||
user.merchant_id,
|
||||
user.org_id,
|
||||
metadata_key,
|
||||
data,
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError);
|
||||
}
|
||||
metadata
|
||||
}
|
||||
types::MetaData::IntegrationCompleted(data) => {
|
||||
utils::insert_merchant_scoped_metadata_to_db(
|
||||
@ -336,6 +395,56 @@ async fn insert_metadata(
|
||||
)
|
||||
.await
|
||||
}
|
||||
types::MetaData::Feedback(data) => {
|
||||
let mut metadata = utils::insert_user_scoped_metadata_to_db(
|
||||
state,
|
||||
user.user_id.clone(),
|
||||
user.merchant_id.clone(),
|
||||
user.org_id.clone(),
|
||||
metadata_key,
|
||||
data.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
if utils::is_update_required(&metadata) {
|
||||
metadata = utils::update_user_scoped_metadata(
|
||||
state,
|
||||
user.user_id,
|
||||
user.merchant_id,
|
||||
user.org_id,
|
||||
metadata_key,
|
||||
data,
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError);
|
||||
}
|
||||
metadata
|
||||
}
|
||||
types::MetaData::ProdIntent(data) => {
|
||||
let mut metadata = utils::insert_user_scoped_metadata_to_db(
|
||||
state,
|
||||
user.user_id.clone(),
|
||||
user.merchant_id.clone(),
|
||||
user.org_id.clone(),
|
||||
metadata_key,
|
||||
data.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
if utils::is_update_required(&metadata) {
|
||||
metadata = utils::update_user_scoped_metadata(
|
||||
state,
|
||||
user.user_id,
|
||||
user.merchant_id,
|
||||
user.org_id,
|
||||
metadata_key,
|
||||
data,
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError);
|
||||
}
|
||||
metadata
|
||||
}
|
||||
types::MetaData::SPTestPayment(data) => {
|
||||
utils::insert_merchant_scoped_metadata_to_db(
|
||||
state,
|
||||
@ -400,7 +509,8 @@ async fn fetch_metadata(
|
||||
metadata_keys: Vec<DBEnum>,
|
||||
) -> UserResult<Vec<DashboardMetadata>> {
|
||||
let mut dashboard_metadata = Vec::with_capacity(metadata_keys.len());
|
||||
let (merchant_scoped_enums, _) = utils::separate_metadata_type_based_on_scope(metadata_keys);
|
||||
let (merchant_scoped_enums, user_scoped_enums) =
|
||||
utils::separate_metadata_type_based_on_scope(metadata_keys);
|
||||
|
||||
if !merchant_scoped_enums.is_empty() {
|
||||
let mut res = utils::get_merchant_scoped_metadata_from_db(
|
||||
@ -413,6 +523,18 @@ async fn fetch_metadata(
|
||||
dashboard_metadata.append(&mut res);
|
||||
}
|
||||
|
||||
if !user_scoped_enums.is_empty() {
|
||||
let mut res = utils::get_user_scoped_metadata_from_db(
|
||||
state,
|
||||
user.user_id.to_owned(),
|
||||
user.merchant_id.to_owned(),
|
||||
user.org_id.to_owned(),
|
||||
user_scoped_enums,
|
||||
)
|
||||
.await?;
|
||||
dashboard_metadata.append(&mut res);
|
||||
}
|
||||
|
||||
Ok(dashboard_metadata)
|
||||
}
|
||||
|
||||
|
||||
@ -997,7 +997,7 @@ impl RefundInterface for MockDb {
|
||||
let mut refund_meta_data = api_models::refunds::RefundListMetaData {
|
||||
connector: vec![],
|
||||
currency: vec![],
|
||||
status: vec![],
|
||||
refund_status: vec![],
|
||||
};
|
||||
|
||||
let mut unique_connectors = HashSet::new();
|
||||
@ -1016,7 +1016,7 @@ impl RefundInterface for MockDb {
|
||||
|
||||
refund_meta_data.connector = unique_connectors.into_iter().collect();
|
||||
refund_meta_data.currency = unique_currencies.into_iter().collect();
|
||||
refund_meta_data.status = unique_statuses.into_iter().collect();
|
||||
refund_meta_data.refund_status = unique_statuses.into_iter().collect();
|
||||
|
||||
Ok(refund_meta_data)
|
||||
}
|
||||
|
||||
@ -860,6 +860,9 @@ impl User {
|
||||
.service(
|
||||
web::resource("/connect_account").route(web::post().to(user_connect_account)),
|
||||
)
|
||||
.service(web::resource("/forgot_password").route(web::post().to(forgot_password)))
|
||||
.service(web::resource("/reset_password").route(web::post().to(reset_password)))
|
||||
.service(web::resource("user/invite").route(web::post().to(invite_user)))
|
||||
.service(
|
||||
web::resource("/signup_with_merchant_id")
|
||||
.route(web::post().to(user_signup_with_merchant_id)),
|
||||
|
||||
@ -163,6 +163,9 @@ impl From<Flow> for ApiIdentifier {
|
||||
| Flow::DeleteSampleData
|
||||
| Flow::UserMerchantAccountList
|
||||
| Flow::GetUserDetails
|
||||
| Flow::ForgotPassword
|
||||
| Flow::ResetPassword
|
||||
| Flow::InviteUser
|
||||
| Flow::UserSignUpWithMerchantId => Self::User,
|
||||
|
||||
Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => {
|
||||
|
||||
@ -294,3 +294,60 @@ pub async fn get_user_details(state: web::Data<AppState>, req: HttpRequest) -> H
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn forgot_password(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
payload: web::Json<user_api::ForgotPasswordRequest>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ForgotPassword;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state.clone(),
|
||||
&req,
|
||||
payload.into_inner(),
|
||||
|state, _, payload| user_core::forgot_password(state, payload),
|
||||
&auth::NoAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn reset_password(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
payload: web::Json<user_api::ResetPasswordRequest>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ResetPassword;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state.clone(),
|
||||
&req,
|
||||
payload.into_inner(),
|
||||
|state, _, payload| user_core::reset_password(state, payload),
|
||||
&auth::NoAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "email")]
|
||||
pub async fn invite_user(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
payload: web::Json<user_api::InviteUserRequest>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::InviteUser;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state.clone(),
|
||||
&req,
|
||||
payload.into_inner(),
|
||||
|state, user, payload| user_core::invite_user(state, payload, user),
|
||||
&auth::JWTAuth(Permission::UsersWrite),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
@ -66,6 +66,10 @@ impl EmailToken {
|
||||
};
|
||||
jwt::generate_jwt(&token_payload, settings).await
|
||||
}
|
||||
|
||||
pub fn get_email(&self) -> &str {
|
||||
self.email.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_link_with_token(
|
||||
|
||||
@ -259,6 +259,15 @@ impl From<UserMerchantCreateRequestWithToken> for NewUserOrganization {
|
||||
}
|
||||
}
|
||||
|
||||
type InviteeUserRequestWithInvitedUserToken = (user_api::InviteUserRequest, UserFromToken);
|
||||
impl From<InviteeUserRequestWithInvitedUserToken> for NewUserOrganization {
|
||||
fn from(_value: InviteeUserRequestWithInvitedUserToken) -> Self {
|
||||
let new_organization = api_org::OrganizationNew::new(None);
|
||||
let db_organization = ForeignFrom::foreign_from(new_organization);
|
||||
Self(db_organization)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MerchantId(String);
|
||||
|
||||
@ -420,6 +429,19 @@ impl TryFrom<user_api::CreateInternalUserRequest> for NewUserMerchant {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InviteeUserRequestWithInvitedUserToken> for NewUserMerchant {
|
||||
type Error = error_stack::Report<UserErrors>;
|
||||
fn try_from(value: InviteeUserRequestWithInvitedUserToken) -> UserResult<Self> {
|
||||
let merchant_id = MerchantId::new(value.clone().1.merchant_id)?;
|
||||
let new_organization = NewUserOrganization::from(value);
|
||||
Ok(Self {
|
||||
company_name: None,
|
||||
merchant_id,
|
||||
new_organization,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type UserMerchantCreateRequestWithToken =
|
||||
(UserFromStorage, user_api::UserMerchantCreate, UserFromToken);
|
||||
|
||||
@ -657,6 +679,26 @@ impl TryFrom<UserMerchantCreateRequestWithToken> for NewUser {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<InviteeUserRequestWithInvitedUserToken> for NewUser {
|
||||
type Error = error_stack::Report<UserErrors>;
|
||||
fn try_from(value: InviteeUserRequestWithInvitedUserToken) -> UserResult<Self> {
|
||||
let user_id = uuid::Uuid::new_v4().to_string();
|
||||
let email = value.0.email.clone().try_into()?;
|
||||
let name = UserName::new(value.0.name.clone())?;
|
||||
let password = password::generate_password_hash(uuid::Uuid::new_v4().to_string().into())?;
|
||||
let password = UserPassword::new(password)?;
|
||||
let new_merchant = NewUserMerchant::try_from(value)?;
|
||||
|
||||
Ok(Self {
|
||||
user_id,
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
new_merchant,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UserFromStorage(pub storage_user::User);
|
||||
|
||||
|
||||
@ -13,10 +13,13 @@ pub enum MetaData {
|
||||
ConfiguredRouting(api::ConfiguredRouting),
|
||||
TestPayment(api::TestPayment),
|
||||
IntegrationMethod(api::IntegrationMethod),
|
||||
ConfigurationType(api::ConfigurationType),
|
||||
IntegrationCompleted(bool),
|
||||
StripeConnected(api::ProcessorConnected),
|
||||
PaypalConnected(api::ProcessorConnected),
|
||||
SPRoutingConfigured(api::ConfiguredRouting),
|
||||
Feedback(api::Feedback),
|
||||
ProdIntent(api::ProdIntent),
|
||||
SPTestPayment(bool),
|
||||
DownloadWoocom(bool),
|
||||
ConfigureWoocom(bool),
|
||||
@ -36,10 +39,13 @@ impl From<&MetaData> for DBEnum {
|
||||
MetaData::ConfiguredRouting(_) => Self::ConfiguredRouting,
|
||||
MetaData::TestPayment(_) => Self::TestPayment,
|
||||
MetaData::IntegrationMethod(_) => Self::IntegrationMethod,
|
||||
MetaData::ConfigurationType(_) => Self::ConfigurationType,
|
||||
MetaData::IntegrationCompleted(_) => Self::IntegrationCompleted,
|
||||
MetaData::StripeConnected(_) => Self::StripeConnected,
|
||||
MetaData::PaypalConnected(_) => Self::PaypalConnected,
|
||||
MetaData::SPRoutingConfigured(_) => Self::SpRoutingConfigured,
|
||||
MetaData::Feedback(_) => Self::Feedback,
|
||||
MetaData::ProdIntent(_) => Self::ProdIntent,
|
||||
MetaData::SPTestPayment(_) => Self::SpTestPayment,
|
||||
MetaData::DownloadWoocom(_) => Self::DownloadWoocom,
|
||||
MetaData::ConfigureWoocom(_) => Self::ConfigureWoocom,
|
||||
|
||||
@ -50,7 +50,21 @@ impl RefundDbExt for Refund {
|
||||
.filter(dsl::merchant_id.eq(merchant_id.to_owned()))
|
||||
.order(dsl::modified_at.desc())
|
||||
.into_boxed();
|
||||
let mut search_by_pay_or_ref_id = false;
|
||||
|
||||
if let (Some(pid), Some(ref_id)) = (
|
||||
&refund_list_details.payment_id,
|
||||
&refund_list_details.refund_id,
|
||||
) {
|
||||
search_by_pay_or_ref_id = true;
|
||||
filter = filter
|
||||
.filter(dsl::payment_id.eq(pid.to_owned()))
|
||||
.or_filter(dsl::refund_id.eq(ref_id.to_owned()))
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
};
|
||||
|
||||
if !search_by_pay_or_ref_id {
|
||||
match &refund_list_details.payment_id {
|
||||
Some(pid) => {
|
||||
filter = filter.filter(dsl::payment_id.eq(pid.to_owned()));
|
||||
@ -59,6 +73,8 @@ impl RefundDbExt for Refund {
|
||||
filter = filter.limit(limit).offset(offset);
|
||||
}
|
||||
};
|
||||
}
|
||||
if !search_by_pay_or_ref_id {
|
||||
match &refund_list_details.refund_id {
|
||||
Some(ref_id) => {
|
||||
filter = filter.filter(dsl::refund_id.eq(ref_id.to_owned()));
|
||||
@ -67,6 +83,7 @@ impl RefundDbExt for Refund {
|
||||
filter = filter.limit(limit).offset(offset);
|
||||
}
|
||||
};
|
||||
}
|
||||
match &refund_list_details.profile_id {
|
||||
Some(profile_id) => {
|
||||
filter = filter
|
||||
@ -163,7 +180,7 @@ impl RefundDbExt for Refund {
|
||||
let meta = api_models::refunds::RefundListMetaData {
|
||||
connector: filter_connector,
|
||||
currency: filter_currency,
|
||||
status: filter_status,
|
||||
refund_status: filter_status,
|
||||
};
|
||||
|
||||
Ok(meta)
|
||||
@ -179,13 +196,29 @@ impl RefundDbExt for Refund {
|
||||
.filter(dsl::merchant_id.eq(merchant_id.to_owned()))
|
||||
.into_boxed();
|
||||
|
||||
let mut search_by_pay_or_ref_id = false;
|
||||
|
||||
if let (Some(pid), Some(ref_id)) = (
|
||||
&refund_list_details.payment_id,
|
||||
&refund_list_details.refund_id,
|
||||
) {
|
||||
search_by_pay_or_ref_id = true;
|
||||
filter = filter
|
||||
.filter(dsl::payment_id.eq(pid.to_owned()))
|
||||
.or_filter(dsl::refund_id.eq(ref_id.to_owned()));
|
||||
};
|
||||
|
||||
if !search_by_pay_or_ref_id {
|
||||
if let Some(pay_id) = &refund_list_details.payment_id {
|
||||
filter = filter.filter(dsl::payment_id.eq(pay_id.to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
if !search_by_pay_or_ref_id {
|
||||
if let Some(ref_id) = &refund_list_details.refund_id {
|
||||
filter = filter.filter(dsl::refund_id.eq(ref_id.to_owned()));
|
||||
}
|
||||
}
|
||||
if let Some(profile_id) = &refund_list_details.profile_id {
|
||||
filter = filter.filter(dsl::profile_id.eq(profile_id.to_owned()));
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ use api_models::user::dashboard_metadata::{
|
||||
};
|
||||
use diesel_models::{
|
||||
enums::DashboardMetadata as DBEnum,
|
||||
user::dashboard_metadata::{DashboardMetadata, DashboardMetadataNew},
|
||||
user::dashboard_metadata::{DashboardMetadata, DashboardMetadataNew, DashboardMetadataUpdate},
|
||||
};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::Secret;
|
||||
@ -50,6 +50,40 @@ pub async fn insert_merchant_scoped_metadata_to_db(
|
||||
e.change_context(UserErrors::InternalServerError)
|
||||
})
|
||||
}
|
||||
pub async fn insert_user_scoped_metadata_to_db(
|
||||
state: &AppState,
|
||||
user_id: String,
|
||||
merchant_id: String,
|
||||
org_id: String,
|
||||
metadata_key: DBEnum,
|
||||
metadata_value: impl serde::Serialize,
|
||||
) -> UserResult<DashboardMetadata> {
|
||||
let now = common_utils::date_time::now();
|
||||
let data_value = serde_json::to_value(metadata_value)
|
||||
.into_report()
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("Error Converting Struct To Serde Value")?;
|
||||
state
|
||||
.store
|
||||
.insert_metadata(DashboardMetadataNew {
|
||||
user_id: Some(user_id.clone()),
|
||||
merchant_id,
|
||||
org_id,
|
||||
data_key: metadata_key,
|
||||
data_value,
|
||||
created_by: user_id.clone(),
|
||||
created_at: now,
|
||||
last_modified_by: user_id,
|
||||
last_modified_at: now,
|
||||
})
|
||||
.await
|
||||
.map_err(|e| {
|
||||
if e.current_context().is_db_unique_violation() {
|
||||
return e.change_context(UserErrors::MetadataAlreadySet);
|
||||
}
|
||||
e.change_context(UserErrors::InternalServerError)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_merchant_scoped_metadata_from_db(
|
||||
state: &AppState,
|
||||
@ -73,6 +107,88 @@ pub async fn get_merchant_scoped_metadata_from_db(
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn get_user_scoped_metadata_from_db(
|
||||
state: &AppState,
|
||||
user_id: String,
|
||||
merchant_id: String,
|
||||
org_id: String,
|
||||
metadata_keys: Vec<DBEnum>,
|
||||
) -> UserResult<Vec<DashboardMetadata>> {
|
||||
match state
|
||||
.store
|
||||
.find_user_scoped_dashboard_metadata(&user_id, &merchant_id, &org_id, metadata_keys)
|
||||
.await
|
||||
{
|
||||
Ok(data) => Ok(data),
|
||||
Err(e) => {
|
||||
if e.current_context().is_db_not_found() {
|
||||
return Ok(Vec::with_capacity(0));
|
||||
}
|
||||
Err(e
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("DB Error Fetching DashboardMetaData"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_merchant_scoped_metadata(
|
||||
state: &AppState,
|
||||
user_id: String,
|
||||
merchant_id: String,
|
||||
org_id: String,
|
||||
metadata_key: DBEnum,
|
||||
metadata_value: impl serde::Serialize,
|
||||
) -> UserResult<DashboardMetadata> {
|
||||
let data_value = serde_json::to_value(metadata_value)
|
||||
.into_report()
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("Error Converting Struct To Serde Value")?;
|
||||
|
||||
state
|
||||
.store
|
||||
.update_metadata(
|
||||
None,
|
||||
merchant_id,
|
||||
org_id,
|
||||
metadata_key,
|
||||
DashboardMetadataUpdate::UpdateData {
|
||||
data_key: metadata_key,
|
||||
data_value,
|
||||
last_modified_by: user_id,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
pub async fn update_user_scoped_metadata(
|
||||
state: &AppState,
|
||||
user_id: String,
|
||||
merchant_id: String,
|
||||
org_id: String,
|
||||
metadata_key: DBEnum,
|
||||
metadata_value: impl serde::Serialize,
|
||||
) -> UserResult<DashboardMetadata> {
|
||||
let data_value = serde_json::to_value(metadata_value)
|
||||
.into_report()
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("Error Converting Struct To Serde Value")?;
|
||||
|
||||
state
|
||||
.store
|
||||
.update_metadata(
|
||||
Some(user_id.clone()),
|
||||
merchant_id,
|
||||
org_id,
|
||||
metadata_key,
|
||||
DashboardMetadataUpdate::UpdateData {
|
||||
data_key: metadata_key,
|
||||
data_value,
|
||||
last_modified_by: user_id,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
}
|
||||
|
||||
pub fn deserialize_to_response<T>(data: Option<&DashboardMetadata>) -> UserResult<Option<T>>
|
||||
where
|
||||
@ -87,7 +203,7 @@ where
|
||||
pub fn separate_metadata_type_based_on_scope(
|
||||
metadata_keys: Vec<DBEnum>,
|
||||
) -> (Vec<DBEnum>, Vec<DBEnum>) {
|
||||
let (mut merchant_scoped, user_scoped) = (
|
||||
let (mut merchant_scoped, mut user_scoped) = (
|
||||
Vec::with_capacity(metadata_keys.len()),
|
||||
Vec::with_capacity(metadata_keys.len()),
|
||||
);
|
||||
@ -102,6 +218,7 @@ pub fn separate_metadata_type_based_on_scope(
|
||||
| DBEnum::ConfiguredRouting
|
||||
| DBEnum::TestPayment
|
||||
| DBEnum::IntegrationMethod
|
||||
| DBEnum::ConfigurationType
|
||||
| DBEnum::IntegrationCompleted
|
||||
| DBEnum::StripeConnected
|
||||
| DBEnum::PaypalConnected
|
||||
@ -111,11 +228,19 @@ pub fn separate_metadata_type_based_on_scope(
|
||||
| DBEnum::ConfigureWoocom
|
||||
| DBEnum::SetupWoocomWebhook
|
||||
| DBEnum::IsMultipleConfiguration => merchant_scoped.push(key),
|
||||
DBEnum::Feedback | DBEnum::ProdIntent => user_scoped.push(key),
|
||||
}
|
||||
}
|
||||
(merchant_scoped, user_scoped)
|
||||
}
|
||||
|
||||
pub fn is_update_required(metadata: &UserResult<DashboardMetadata>) -> bool {
|
||||
match metadata {
|
||||
Ok(_) => false,
|
||||
Err(e) => matches!(e.current_context(), UserErrors::MetadataAlreadySet),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_backfill_required(metadata_key: &DBEnum) -> bool {
|
||||
matches!(
|
||||
metadata_key,
|
||||
|
||||
@ -293,6 +293,12 @@ pub enum Flow {
|
||||
UserMerchantAccountList,
|
||||
/// Get users for merchant account
|
||||
GetUserDetails,
|
||||
/// Get reset password link
|
||||
ForgotPassword,
|
||||
/// Reset password using link
|
||||
ResetPassword,
|
||||
/// Invite users
|
||||
InviteUser,
|
||||
/// Incremental Authorization flow
|
||||
PaymentsIncrementalAuthorization,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user