From 2b4300c521e27df1f76b6723c9d88dc7ff112b6e Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 12:34:39 +0530 Subject: [PATCH 01/15] wip --- crates/api_models/src/user.rs | 11 ++++ crates/router/Cargo.toml | 2 +- crates/router/src/core/errors/user.rs | 13 ++++ crates/router/src/core/user.rs | 63 ++++++++++++++++++- .../src/core/user/dashboard_metadata.rs | 8 ++- .../src/utils/user/dashboard_metadata.rs | 7 +++ 6 files changed, 98 insertions(+), 6 deletions(-) diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 36d730f5118e..61ed5d2ddbee 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -31,6 +31,17 @@ pub struct ChangePasswordRequest { pub old_password: Secret, } +#[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, + pub password: Secret, +} + #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct SwitchMerchantIdRequest { pub merchant_id: String, diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index f508460574dd..ab9ef3f4e469 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" license.workspace = true [features] -default = ["kv_store", "stripe", "oltp", "olap", "backwards_compatibility", "accounts_cache", "dummy_connector", "payouts", "profile_specific_fallback_routing", "retry"] +default = ["kv_store", "stripe", "oltp", "olap", "backwards_compatibility", "accounts_cache", "dummy_connector", "payouts", "profile_specific_fallback_routing", "retry", "email"] s3 = ["dep:aws-sdk-s3", "dep:aws-config"] kms = ["external_services/kms", "dep:aws-config"] email = ["external_services/email", "dep:aws-config", "olap"] diff --git a/crates/router/src/core/errors/user.rs b/crates/router/src/core/errors/user.rs index 5e580b003408..9a5308852229 100644 --- a/crates/router/src/core/errors/user.rs +++ b/crates/router/src/core/errors/user.rs @@ -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 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, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 7d0d599cc4ed..4fcec8336fc9 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -9,7 +9,7 @@ use crate::{ consts, db::user::UserInterface, routes::AppState, - services::{authentication as auth, ApplicationResponse}, + services::{authentication as auth, email::types::EmailToken, ApplicationResponse}, types::domain, utils, }; @@ -130,8 +130,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, @@ -148,6 +147,64 @@ pub async fn change_password( Ok(ApplicationResponse::StatusOk) } +#[cfg(feature = "email")] +pub async fn forgot_password( + state: AppState, + request: user_api::ForgotPasswordRequest, +) -> UserResponse<()> { + use crate::services::email::types as email_types; + + 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) +} + +pub async fn reset_password( + state: AppState, + request: user_api::ResetPasswordRequest, +) -> UserResponse<()> { + let token = auth::decode_jwt::(request.token.expose().as_str(), &state) + .await + .map_err(|e| e.change_context(UserErrors::LinkInvalid))?; + + let password = domain::UserPassword::new(request.password)?; + + let hash_password = utils::user::password::generate_password_hash(password.get_secret())?; + + + + Ok(ApplicationResponse::StatusOk) +} + pub async fn create_internal_user( state: AppState, request: user_api::CreateInternalUserRequest, diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index de385fb8ed65..9a9cf458ff32 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -381,7 +381,7 @@ async fn insert_metadata( .await } types::MetaData::IsMultipleConfiguration(data) => { - utils::insert_merchant_scoped_metadata_to_db( + let metadata = utils::insert_merchant_scoped_metadata_to_db( state, user.user_id, user.merchant_id, @@ -389,7 +389,11 @@ async fn insert_metadata( metadata_key, data, ) - .await + .await; + if utils::is_update_required(&metadata) { + todo!() + } + metadata } } } diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index 5f354e613f95..00da52064700 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -116,6 +116,13 @@ pub fn separate_metadata_type_based_on_scope( (merchant_scoped, user_scoped) } +pub fn is_update_required(metadata: &UserResult) -> 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, From 6d6a9efba865c4b23eed4105d0c97dba020aaac3 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Mon, 4 Dec 2023 14:46:06 +0530 Subject: [PATCH 02/15] call update function --- .../api_models/src/user/dashboard_metadata.rs | 2 +- .../src/core/user/dashboard_metadata.rs | 40 +++++++++++++++---- .../src/utils/user/dashboard_metadata.rs | 1 + 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/crates/api_models/src/user/dashboard_metadata.rs b/crates/api_models/src/user/dashboard_metadata.rs index 04cda3bd7075..d25aee2f70fb 100644 --- a/crates/api_models/src/user/dashboard_metadata.rs +++ b/crates/api_models/src/user/dashboard_metadata.rs @@ -49,7 +49,7 @@ 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, } diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 9a9cf458ff32..6debe48268b7 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -1,8 +1,9 @@ use api_models::user::dashboard_metadata::{self as api, GetMultipleMetaDataPayload}; use diesel_models::{ - enums::DashboardMetadata as DBEnum, user::dashboard_metadata::DashboardMetadata, + enums::DashboardMetadata as DBEnum, + user::dashboard_metadata::{DashboardMetadata, DashboardMetadataUpdate}, }; -use error_stack::ResultExt; +use error_stack::{ResultExt, IntoReport}; use crate::{ core::errors::{UserErrors, UserResponse, UserResult}, @@ -282,15 +283,38 @@ async fn insert_metadata( .await } types::MetaData::IntegrationMethod(data) => { - utils::insert_merchant_scoped_metadata_to_db( + let metadata = utils::insert_merchant_scoped_metadata_to_db( state, - user.user_id, - user.merchant_id, - user.org_id, + user.user_id.clone(), + user.merchant_id.clone(), + user.org_id.clone(), metadata_key, - data, + data.clone(), ) - .await + .await; + if utils::is_update_required(&metadata) { + println!("apoorv here2"); + let data_value = serde_json::to_value(data) + .into_report() + .change_context(UserErrors::InternalServerError) + .attach_printable("Error Converting Struct To Serde Value")?; + + let a = state + .store + .update_metadata( + None, + user.merchant_id, + user.org_id, + metadata_key, + DashboardMetadataUpdate::UpdateData { + data_key: metadata_key, + data_value, + last_modified_by: user.user_id, + }, + ) + .await; + } + metadata } types::MetaData::IntegrationCompleted(data) => { utils::insert_merchant_scoped_metadata_to_db( diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index 00da52064700..a5fda0e5e8bc 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -117,6 +117,7 @@ pub fn separate_metadata_type_based_on_scope( } pub fn is_update_required(metadata: &UserResult) -> bool { + println!("apoorv here1"); match metadata { Ok(_) => false, Err(e) => matches!(e.current_context(), UserErrors::MetadataAlreadySet), From a099d1c12bcb16dd883cad8d4617bfa2bc861133 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 14:45:54 +0530 Subject: [PATCH 03/15] wip2 --- crates/api_models/src/user.rs | 7 +++++++ crates/router/src/core/user.rs | 25 +++++++++++++++++++++-- crates/router/src/services/email/types.rs | 4 ++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 61ed5d2ddbee..11d3865b855b 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -42,6 +42,13 @@ pub struct ResetPasswordRequest { pub password: Secret, } +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct InviteUserRequest { + pub email: pii::Email, + pub name: Secret, + pub role_id: String, +} + #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct SwitchMerchantIdRequest { pub merchant_id: String, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 4fcec8336fc9..55a0f7e2028e 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -199,8 +199,29 @@ pub async fn reset_password( 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 + .map_err(|e| e.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 + .map_err(|e| e.change_context(UserErrors::InternalServerError))?; + + //TODO: Update User role status for invited user Ok(ApplicationResponse::StatusOk) } diff --git a/crates/router/src/services/email/types.rs b/crates/router/src/services/email/types.rs index a4a4681c6001..ad91edd8c364 100644 --- a/crates/router/src/services/email/types.rs +++ b/crates/router/src/services/email/types.rs @@ -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( From 003485a4405041b5b199dfd3bfc4b7cdb9d0383c Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 15:08:47 +0530 Subject: [PATCH 04/15] update query updated --- .../src/query/dashboard_metadata.rs | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/crates/diesel_models/src/query/dashboard_metadata.rs b/crates/diesel_models/src/query/dashboard_metadata.rs index 44fd24c7acf2..678bcc2fd1f6 100644 --- a/crates/diesel_models/src/query/dashboard_metadata.rs +++ b/crates/diesel_models/src/query/dashboard_metadata.rs @@ -28,21 +28,36 @@ impl DashboardMetadata { data_key: enums::DashboardMetadata, dashboard_metadata_update: DashboardMetadataUpdate, ) -> StorageResult { - generics::generic_update_with_unique_predicate_get_result::< - ::Table, - _, - _, - _, - >( - 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())), - DashboardMetadataUpdateInternal::from(dashboard_metadata_update), - ) - .await + 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::< + ::Table, + _, + _, + _, + >( + conn, + predicate.and(dsl::user_id.eq(uid)), + DashboardMetadataUpdateInternal::from(dashboard_metadata_update), + ) + .await + } else { + generics::generic_update_with_unique_predicate_get_result::< + ::Table, + _, + _, + _, + >( + conn, + predicate.and(dsl::user_id.is_null()), + DashboardMetadataUpdateInternal::from(dashboard_metadata_update), + ) + .await + } } pub async fn find_user_scoped_dashboard_metadata( From c00d9932dc7bbad6a9f39a6c35b3524ff7074b06 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Mon, 4 Dec 2023 15:18:53 +0530 Subject: [PATCH 05/15] add new enum configuration type --- .../api_models/src/user/dashboard_metadata.rs | 7 +++++++ crates/diesel_models/src/enums.rs | 1 + .../src/core/user/dashboard_metadata.rs | 19 +++++++++++++++++++ .../types/domain/user/dashboard_metadata.rs | 2 ++ .../src/utils/user/dashboard_metadata.rs | 1 + 5 files changed, 30 insertions(+) diff --git a/crates/api_models/src/user/dashboard_metadata.rs b/crates/api_models/src/user/dashboard_metadata.rs index d25aee2f70fb..4e50f641dc60 100644 --- a/crates/api_models/src/user/dashboard_metadata.rs +++ b/crates/api_models/src/user/dashboard_metadata.rs @@ -12,6 +12,7 @@ pub enum SetMetaDataRequest { ConfiguredRouting(ConfiguredRouting), TestPayment(TestPayment), IntegrationMethod(IntegrationMethod), + ConfigurationType(ConfigurationType), IntegrationCompleted, SPRoutingConfigured(ConfiguredRouting), SPTestPayment, @@ -53,6 +54,10 @@ pub struct TestPayment { pub struct IntegrationMethod { pub integration_type: String, } +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ConfigurationType { + pub configurtion_type: String, +} #[derive(Debug, serde::Deserialize, EnumString, serde::Serialize)] pub enum GetMetaDataRequest { @@ -65,6 +70,7 @@ pub enum GetMetaDataRequest { ConfiguredRouting, TestPayment, IntegrationMethod, + ConfigurationType, IntegrationCompleted, StripeConnected, PaypalConnected, @@ -98,6 +104,7 @@ pub enum GetMetaDataResponse { ConfiguredRouting(Option), TestPayment(Option), IntegrationMethod(Option), + ConfigurationType(Option), IntegrationCompleted(bool), StripeConnected(Option), PaypalConnected(Option), diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index 3f8b37cd03f7..75d39078d239 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -452,6 +452,7 @@ pub enum DashboardMetadata { ConfiguredRouting, TestPayment, IntegrationMethod, + ConfigurationType, IntegrationCompleted, StripeConnected, PaypalConnected, diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 6debe48268b7..9883e1310a7c 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -82,6 +82,9 @@ fn parse_set_request(data_enum: api::SetMetaDataRequest) -> UserResult { Ok(types::MetaData::IntegrationMethod(req)) } + api::SetMetaDataRequest::ConfigurationType(req) => { + Ok(types::MetaData::ConfigurationType(req)) + } api::SetMetaDataRequest::IntegrationCompleted => { Ok(types::MetaData::IntegrationCompleted(true)) } @@ -111,6 +114,7 @@ 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, @@ -159,6 +163,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(), )), @@ -316,6 +324,17 @@ async fn insert_metadata( } metadata } + types::MetaData::ConfigurationType(data) => { + utils::insert_merchant_scoped_metadata_to_db( + state, + user.user_id, + user.merchant_id, + user.org_id, + metadata_key, + data, + ) + .await + } types::MetaData::IntegrationCompleted(data) => { utils::insert_merchant_scoped_metadata_to_db( state, diff --git a/crates/router/src/types/domain/user/dashboard_metadata.rs b/crates/router/src/types/domain/user/dashboard_metadata.rs index e65379346ac9..33530b4ffd87 100644 --- a/crates/router/src/types/domain/user/dashboard_metadata.rs +++ b/crates/router/src/types/domain/user/dashboard_metadata.rs @@ -13,6 +13,7 @@ pub enum MetaData { ConfiguredRouting(api::ConfiguredRouting), TestPayment(api::TestPayment), IntegrationMethod(api::IntegrationMethod), + ConfigurationType(api::ConfigurationType), IntegrationCompleted(bool), StripeConnected(api::ProcessorConnected), PaypalConnected(api::ProcessorConnected), @@ -36,6 +37,7 @@ 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, diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index a5fda0e5e8bc..9ea594d6d6ca 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -102,6 +102,7 @@ pub fn separate_metadata_type_based_on_scope( | DBEnum::ConfiguredRouting | DBEnum::TestPayment | DBEnum::IntegrationMethod + | DBEnum::ConfigurationType | DBEnum::IntegrationCompleted | DBEnum::StripeConnected | DBEnum::PaypalConnected From edd12d671f9fdb3daf0e758ada3e6772ac6b6305 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 15:29:15 +0530 Subject: [PATCH 06/15] invite wip --- crates/api_models/src/user.rs | 5 +++++ crates/router/src/core/user.rs | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 11d3865b855b..c1c1e613b3d3 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -49,6 +49,11 @@ pub struct InviteUserRequest { 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, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 55a0f7e2028e..ca399ecf7692 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -226,6 +226,16 @@ pub async fn reset_password( Ok(ApplicationResponse::StatusOk) } +pub async fn invite_user( + state: AppState, + request: user_api::InviteUserRequest, + user_from_token: auth::UserFromToken +) -> UserResponse { + let inviter_user = state.store.find_user_by_id(user_from_token.user_id.as_str()).await.map_err(|e| e.change_context(UserErrors::InternalServerError))?; + + todo!() +} + pub async fn create_internal_user( state: AppState, request: user_api::CreateInternalUserRequest, From 978c924eb00a5709d1c5e1827a37fc76b75b5320 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 17:45:02 +0530 Subject: [PATCH 07/15] update is updating --- .../src/core/user/dashboard_metadata.rs | 33 +++++++------------ .../src/utils/user/dashboard_metadata.rs | 32 +++++++++++++++++- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 9883e1310a7c..98f558cc0bed 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -3,7 +3,7 @@ use diesel_models::{ enums::DashboardMetadata as DBEnum, user::dashboard_metadata::{DashboardMetadata, DashboardMetadataUpdate}, }; -use error_stack::{ResultExt, IntoReport}; +use error_stack::{IntoReport, ResultExt}; use crate::{ core::errors::{UserErrors, UserResponse, UserResult}, @@ -291,7 +291,7 @@ async fn insert_metadata( .await } types::MetaData::IntegrationMethod(data) => { - let metadata = 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(), @@ -300,27 +300,16 @@ async fn insert_metadata( data.clone(), ) .await; - if utils::is_update_required(&metadata) { - println!("apoorv here2"); - let data_value = serde_json::to_value(data) - .into_report() - .change_context(UserErrors::InternalServerError) - .attach_printable("Error Converting Struct To Serde Value")?; - let a = state - .store - .update_metadata( - None, - user.merchant_id, - user.org_id, - metadata_key, - DashboardMetadataUpdate::UpdateData { - data_key: metadata_key, - data_value, - last_modified_by: user.user_id, - }, - ) - .await; + if utils::is_update_required(&metadata) { + metadata = utils::update_metadata( + state, + user.user_id, + user.merchant_id, + user.org_id, + metadata_key, + data, + ).await; } metadata } diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index 9ea594d6d6ca..87282ee5e56d 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -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; @@ -74,6 +74,36 @@ pub async fn get_merchant_scoped_metadata_from_db( } } +pub async fn update_metadata( + state: &AppState, + user_id: String, + merchant_id: String, + org_id: String, + metadata_key: DBEnum, + metadata_value: impl serde::Serialize, +) -> UserResult { + 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 fn deserialize_to_response(data: Option<&DashboardMetadata>) -> UserResult> where T: serde::de::DeserializeOwned, From 5db57a0f35968402db30f2d4fd70e3d5f211b525 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Mon, 4 Dec 2023 17:59:28 +0530 Subject: [PATCH 08/15] fix refund list after filters --- crates/api_models/src/refunds.rs | 2 +- .../src/core/user/dashboard_metadata.rs | 28 +++++-- crates/router/src/db/refund.rs | 4 +- .../types/domain/user/dashboard_metadata.rs | 2 +- crates/router/src/types/storage/refund.rs | 75 +++++++++++++------ 5 files changed, 78 insertions(+), 33 deletions(-) diff --git a/crates/api_models/src/refunds.rs b/crates/api_models/src/refunds.rs index 6fe8be8b5291..e89de9c58934 100644 --- a/crates/api_models/src/refunds.rs +++ b/crates/api_models/src/refunds.rs @@ -174,7 +174,7 @@ pub struct RefundListMetaData { pub currency: Vec, /// The list of available refund status filters #[schema(value_type = Vec)] - pub status: Vec, + pub refund_status: Vec, } /// The status for refunds diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 98f558cc0bed..0b244fd5798a 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -302,14 +302,26 @@ async fn insert_metadata( .await; if utils::is_update_required(&metadata) { - metadata = utils::update_metadata( - state, - user.user_id, - user.merchant_id, - user.org_id, - metadata_key, - data, - ).await; + println!("apoorv here2"); + let data_value = serde_json::to_value(data) + .into_report() + .change_context(UserErrors::InternalServerError) + .attach_printable("Error Converting Struct To Serde Value")?; + + let a = state + .store + .update_metadata( + None, + user.merchant_id, + user.org_id, + metadata_key, + DashboardMetadataUpdate::UpdateData { + data_key: metadata_key, + data_value, + last_modified_by: user.user_id, + }, + ) + .await; } metadata } diff --git a/crates/router/src/db/refund.rs b/crates/router/src/db/refund.rs index 8ac8bd106eff..173062f2aeb1 100644 --- a/crates/router/src/db/refund.rs +++ b/crates/router/src/db/refund.rs @@ -998,7 +998,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(); @@ -1017,7 +1017,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) } diff --git a/crates/router/src/types/domain/user/dashboard_metadata.rs b/crates/router/src/types/domain/user/dashboard_metadata.rs index 33530b4ffd87..49483346c8ab 100644 --- a/crates/router/src/types/domain/user/dashboard_metadata.rs +++ b/crates/router/src/types/domain/user/dashboard_metadata.rs @@ -37,7 +37,7 @@ impl From<&MetaData> for DBEnum { MetaData::ConfiguredRouting(_) => Self::ConfiguredRouting, MetaData::TestPayment(_) => Self::TestPayment, MetaData::IntegrationMethod(_) => Self::IntegrationMethod, - MetaData::ConfigurationType(_) =>Self::ConfigurationType, + MetaData::ConfigurationType(_) => Self::ConfigurationType, MetaData::IntegrationCompleted(_) => Self::IntegrationCompleted, MetaData::StripeConnected(_) => Self::StripeConnected, MetaData::PaypalConnected(_) => Self::PaypalConnected, diff --git a/crates/router/src/types/storage/refund.rs b/crates/router/src/types/storage/refund.rs index 4d5667700122..bb05233173c8 100644 --- a/crates/router/src/types/storage/refund.rs +++ b/crates/router/src/types/storage/refund.rs @@ -50,23 +50,40 @@ impl RefundDbExt for Refund { .filter(dsl::merchant_id.eq(merchant_id.to_owned())) .order(dsl::modified_at.desc()) .into_boxed(); - - match &refund_list_details.payment_id { - Some(pid) => { - filter = filter.filter(dsl::payment_id.eq(pid.to_owned())); - } - None => { - filter = filter.limit(limit).offset(offset); - } - }; - match &refund_list_details.refund_id { - Some(ref_id) => { - filter = filter.filter(dsl::refund_id.eq(ref_id.to_owned())); - } - None => { - filter = filter.limit(limit).offset(offset); - } + 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())); + } + None => { + 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())); + } + None => { + 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,12 +196,28 @@ impl RefundDbExt for Refund { .filter(dsl::merchant_id.eq(merchant_id.to_owned())) .into_boxed(); - if let Some(pay_id) = &refund_list_details.payment_id { - filter = filter.filter(dsl::payment_id.eq(pay_id.to_owned())); + 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 let Some(ref_id) = &refund_list_details.refund_id { - filter = filter.filter(dsl::refund_id.eq(ref_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())); From 2a76cd13e19c0e659c1b80964d319800fd5bcc05 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Mon, 4 Dec 2023 19:05:05 +0530 Subject: [PATCH 09/15] add enum configuration type --- .../api_models/src/user/dashboard_metadata.rs | 5 +- crates/router/src/core/user.rs | 8 ++- .../src/core/user/dashboard_metadata.rs | 56 ++++++++++--------- .../src/utils/user/dashboard_metadata.rs | 1 - 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/crates/api_models/src/user/dashboard_metadata.rs b/crates/api_models/src/user/dashboard_metadata.rs index 4e50f641dc60..e9aed602f5ec 100644 --- a/crates/api_models/src/user/dashboard_metadata.rs +++ b/crates/api_models/src/user/dashboard_metadata.rs @@ -55,8 +55,9 @@ pub struct IntegrationMethod { pub integration_type: String, } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub struct ConfigurationType { - pub configurtion_type: String, +pub enum ConfigurationType { + Single, + Multiple, } #[derive(Debug, serde::Deserialize, EnumString, serde::Serialize)] diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 4317545fc874..b1ea4e82bac0 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -334,9 +334,13 @@ pub async fn reset_password( pub async fn invite_user( state: AppState, request: user_api::InviteUserRequest, - user_from_token: auth::UserFromToken + user_from_token: auth::UserFromToken, ) -> UserResponse { - let inviter_user = state.store.find_user_by_id(user_from_token.user_id.as_str()).await.map_err(|e| e.change_context(UserErrors::InternalServerError))?; + let inviter_user = state + .store + .find_user_by_id(user_from_token.user_id.as_str()) + .await + .map_err(|e| e.change_context(UserErrors::InternalServerError))?; todo!() } diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 0b244fd5798a..9c231d48c82a 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -302,39 +302,43 @@ async fn insert_metadata( .await; if utils::is_update_required(&metadata) { - println!("apoorv here2"); - let data_value = serde_json::to_value(data) - .into_report() - .change_context(UserErrors::InternalServerError) - .attach_printable("Error Converting Struct To Serde Value")?; - - let a = state - .store - .update_metadata( - None, - user.merchant_id, - user.org_id, - metadata_key, - DashboardMetadataUpdate::UpdateData { - data_key: metadata_key, - data_value, - last_modified_by: user.user_id, - }, - ) - .await; + metadata = utils::update_metadata( + state, + user.user_id, + user.merchant_id, + user.org_id, + metadata_key, + data, + ) + .await + .change_context(UserErrors::InternalServerError); } metadata } types::MetaData::ConfigurationType(data) => { - utils::insert_merchant_scoped_metadata_to_db( + let mut metadata = utils::insert_merchant_scoped_metadata_to_db( state, - user.user_id, - user.merchant_id, - user.org_id, + user.user_id.clone(), + user.merchant_id.clone(), + user.org_id.clone(), metadata_key, - data, + data.clone(), ) - .await + .await; + + if utils::is_update_required(&metadata) { + metadata = utils::update_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( diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index 87282ee5e56d..cdd846356a61 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -148,7 +148,6 @@ pub fn separate_metadata_type_based_on_scope( } pub fn is_update_required(metadata: &UserResult) -> bool { - println!("apoorv here1"); match metadata { Ok(_) => false, Err(e) => matches!(e.current_context(), UserErrors::MetadataAlreadySet), From 180a21cc7450a059ddafc408b80cfd3cd2a57456 Mon Sep 17 00:00:00 2001 From: Apoorv Dixit Date: Mon, 4 Dec 2023 20:43:30 +0530 Subject: [PATCH 10/15] add feedback and prodintent enums --- .../api_models/src/user/dashboard_metadata.rs | 32 +++++++ crates/diesel_models/src/enums.rs | 2 + .../src/core/user/dashboard_metadata.rs | 86 ++++++++++++++++- .../types/domain/user/dashboard_metadata.rs | 4 + .../src/utils/user/dashboard_metadata.rs | 92 ++++++++++++++++++- 5 files changed, 211 insertions(+), 5 deletions(-) diff --git a/crates/api_models/src/user/dashboard_metadata.rs b/crates/api_models/src/user/dashboard_metadata.rs index e9aed602f5ec..8c7e942b8f6e 100644 --- a/crates/api_models/src/user/dashboard_metadata.rs +++ b/crates/api_models/src/user/dashboard_metadata.rs @@ -1,3 +1,5 @@ +use common_enums::CountryAlpha2; +use common_utils::pii; use masking::Secret; use strum::EnumString; @@ -15,6 +17,8 @@ pub enum SetMetaDataRequest { ConfigurationType(ConfigurationType), IntegrationCompleted, SPRoutingConfigured(ConfiguredRouting), + Feedback(Feedback), + ProdIntent(ProdIntent), SPTestPayment, DownloadWoocom, ConfigureWoocom, @@ -60,6 +64,30 @@ pub enum ConfigurationType { Multiple, } +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct Feedback { + pub email: pii::Email, + pub description: Option, + pub rating: Option, + pub category: Option, +} +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct ProdIntent { + pub legal_business_name: Option, + pub business_label: Option, + pub business_location: Option, + pub display_name: Option, + pub poc_email: Option, + pub business_type: Option, + pub business_identifier: Option, + pub business_website: Option, + pub poc_name: Option, + pub poc_contact: Option, + pub comments: Option, + pub is_completed: bool, +} + + #[derive(Debug, serde::Deserialize, EnumString, serde::Serialize)] pub enum GetMetaDataRequest { ProductionAgreement, @@ -76,6 +104,8 @@ pub enum GetMetaDataRequest { StripeConnected, PaypalConnected, SPRoutingConfigured, + Feedback, + ProdIntent, SPTestPayment, DownloadWoocom, ConfigureWoocom, @@ -110,6 +140,8 @@ pub enum GetMetaDataResponse { StripeConnected(Option), PaypalConnected(Option), SPRoutingConfigured(Option), + Feedback(Option), + ProdIntent(Option), SPTestPayment(bool), DownloadWoocom(bool), ConfigureWoocom(bool), diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index 75d39078d239..17837d2ce5c7 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -457,6 +457,8 @@ pub enum DashboardMetadata { StripeConnected, PaypalConnected, SpRoutingConfigured, + Feedback, + ProdIntent, SpTestPayment, DownloadWoocom, ConfigureWoocom, diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 9c231d48c82a..8d6a22af2dec 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -91,6 +91,12 @@ fn parse_set_request(data_enum: api::SetMetaDataRequest) -> UserResult { 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)), @@ -119,6 +125,8 @@ fn parse_get_request(data_enum: api::GetMetaDataRequest) -> DBEnum { 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, @@ -182,6 +190,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())), @@ -302,7 +318,7 @@ async fn insert_metadata( .await; if utils::is_update_required(&metadata) { - metadata = utils::update_metadata( + metadata = utils::update_merchant_scoped_metadata( state, user.user_id, user.merchant_id, @@ -327,7 +343,7 @@ async fn insert_metadata( .await; if utils::is_update_required(&metadata) { - metadata = utils::update_metadata( + metadata = utils::update_merchant_scoped_metadata( state, user.user_id, user.merchant_id, @@ -384,6 +400,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, @@ -452,7 +518,7 @@ async fn fetch_metadata( metadata_keys: Vec, ) -> UserResult> { 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( @@ -465,6 +531,20 @@ 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) } diff --git a/crates/router/src/types/domain/user/dashboard_metadata.rs b/crates/router/src/types/domain/user/dashboard_metadata.rs index 49483346c8ab..5e4017a3cb1a 100644 --- a/crates/router/src/types/domain/user/dashboard_metadata.rs +++ b/crates/router/src/types/domain/user/dashboard_metadata.rs @@ -18,6 +18,8 @@ pub enum MetaData { StripeConnected(api::ProcessorConnected), PaypalConnected(api::ProcessorConnected), SPRoutingConfigured(api::ConfiguredRouting), + Feedback(api::Feedback), + ProdIntent(api::ProdIntent), SPTestPayment(bool), DownloadWoocom(bool), ConfigureWoocom(bool), @@ -42,6 +44,8 @@ impl From<&MetaData> for DBEnum { 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, diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index cdd846356a61..9602252b2f38 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -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 { + 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,8 +107,31 @@ 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, +) -> UserResult> { + 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_metadata( +pub async fn update_merchant_scoped_metadata( state: &AppState, user_id: String, merchant_id: String, @@ -103,6 +160,35 @@ pub async fn update_metadata( .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 { + 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(data: Option<&DashboardMetadata>) -> UserResult> where @@ -117,7 +203,7 @@ where pub fn separate_metadata_type_based_on_scope( metadata_keys: Vec, ) -> (Vec, Vec) { - 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()), ); @@ -142,6 +228,8 @@ 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) From 5416b57519b4d2d46de0c06c9be9704848bd8d51 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 20:42:59 +0530 Subject: [PATCH 11/15] invite --- crates/api_models/src/user.rs | 2 +- crates/router/src/core/user.rs | 107 +++++++++++++++++- .../src/core/user/dashboard_metadata.rs | 4 +- crates/router/src/types/domain/user.rs | 42 +++++++ 4 files changed, 146 insertions(+), 9 deletions(-) diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 3c295ab8b1dc..0e30e9b7c24d 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -76,7 +76,7 @@ pub struct ResetPasswordRequest { pub password: Secret, } -#[derive(Debug, serde::Deserialize, serde::Serialize)] +#[derive(Debug, serde::Deserialize, serde::Serialize,Clone)] pub struct InviteUserRequest { pub email: pii::Email, pub name: Secret, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index b1ea4e82bac0..d90fdff53cda 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -257,8 +257,6 @@ pub async fn forgot_password( state: AppState, request: user_api::ForgotPasswordRequest, ) -> UserResponse<()> { - use crate::services::email::types as email_types; - let user_email = domain::UserEmail::from_pii_email(request.email)?; let user_from_db = state @@ -293,13 +291,14 @@ pub async fn forgot_password( Ok(ApplicationResponse::StatusOk) } +#[cfg(feature = "email")] pub async fn reset_password( state: AppState, request: user_api::ResetPasswordRequest, ) -> UserResponse<()> { let token = auth::decode_jwt::(request.token.expose().as_str(), &state) .await - .map_err(|e| e.change_context(UserErrors::LinkInvalid))?; + .change_context(UserErrors::LinkInvalid)?; let password = domain::UserPassword::new(request.password)?; @@ -310,7 +309,7 @@ pub async fn reset_password( .store .find_user_by_email(token.get_email()) .await - .map_err(|e| e.change_context(UserErrors::InternalServerError))? + .change_context(UserErrors::InternalServerError)? .user_id; state @@ -324,7 +323,7 @@ pub async fn reset_password( }, ) .await - .map_err(|e| e.change_context(UserErrors::InternalServerError))?; + .change_context(UserErrors::InternalServerError)?; //TODO: Update User role status for invited user @@ -342,7 +341,103 @@ pub async fn invite_user( .await .map_err(|e| e.change_context(UserErrors::InternalServerError))?; - todo!() + 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(); + + 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 is_email_sent = if cfg!(feature="email") + { + 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); + + send_email_result.is_ok() + } else { + false + }; + + Ok(ApplicationResponse::Json(user_api::InviteUserResponse { + is_email_sent + })) + } else { + Err(UserErrors::InternalServerError.into()) + } } pub async fn create_internal_user( diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 8d6a22af2dec..f919b63d3fee 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -1,9 +1,9 @@ use api_models::user::dashboard_metadata::{self as api, GetMultipleMetaDataPayload}; use diesel_models::{ enums::DashboardMetadata as DBEnum, - user::dashboard_metadata::{DashboardMetadata, DashboardMetadataUpdate}, + user::dashboard_metadata::{DashboardMetadata}, }; -use error_stack::{IntoReport, ResultExt}; +use error_stack::ResultExt; use crate::{ core::errors::{UserErrors, UserResponse, UserResult}, diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 592195922493..16a00f117034 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -259,6 +259,15 @@ impl From for NewUserOrganization { } } +type InviteeUserRequestWithInvitedUserToken = (user_api::InviteUserRequest, UserFromToken); +impl From 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 for NewUserMerchant { } } +impl TryFrom for NewUserMerchant { + type Error = error_stack::Report; + fn try_from(value: InviteeUserRequestWithInvitedUserToken) -> UserResult { + 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 for NewUser { } } +impl TryFrom for NewUser { + type Error = error_stack::Report; + fn try_from(value: InviteeUserRequestWithInvitedUserToken) -> UserResult { + 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); From 0032e5f48016ede9e8235d5eff222394cb913cb4 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 20:49:23 +0530 Subject: [PATCH 12/15] compiles --- crates/router/src/core/user.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index d90fdff53cda..f5c88474fbd3 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1,4 +1,5 @@ use api_models::user as user_api; +use diesel_models::user_role::UserRoleNew; use diesel_models::{enums::UserStatus, user as storage_user}; #[cfg(feature = "email")] use error_stack::IntoReport; @@ -335,12 +336,6 @@ pub async fn invite_user( request: user_api::InviteUserRequest, user_from_token: auth::UserFromToken, ) -> UserResponse { - let inviter_user = state - .store - .find_user_by_id(user_from_token.user_id.as_str()) - .await - .map_err(|e| e.change_context(UserErrors::InternalServerError))?; - let inviter_user = state .store .find_user_by_id(user_from_token.user_id.as_str()) @@ -408,8 +403,7 @@ pub async fn invite_user( .await .change_context(UserErrors::InternalServerError)?; - let is_email_sent = if cfg!(feature="email") - { + let is_email_sent = if cfg!(feature = "email") { let email_contents = email_types::InviteUser { recipient_email: invitee_email, user_name: domain::UserName::new(new_user.get_name())?, @@ -433,7 +427,7 @@ pub async fn invite_user( }; Ok(ApplicationResponse::Json(user_api::InviteUserResponse { - is_email_sent + is_email_sent, })) } else { Err(UserErrors::InternalServerError.into()) From 9a7ec0eaf974543d52f2a5dd071673b397ee92ee Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 15:35:19 +0000 Subject: [PATCH 13/15] chore: run formatter --- crates/api_models/src/user.rs | 2 +- crates/api_models/src/user/dashboard_metadata.rs | 5 ++--- crates/router/src/core/user.rs | 3 +-- .../router/src/core/user/dashboard_metadata.rs | 16 +++++----------- .../router/src/utils/user/dashboard_metadata.rs | 3 +-- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/crates/api_models/src/user.rs b/crates/api_models/src/user.rs index 0e30e9b7c24d..e5f06fdbfae3 100644 --- a/crates/api_models/src/user.rs +++ b/crates/api_models/src/user.rs @@ -76,7 +76,7 @@ pub struct ResetPasswordRequest { pub password: Secret, } -#[derive(Debug, serde::Deserialize, serde::Serialize,Clone)] +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct InviteUserRequest { pub email: pii::Email, pub name: Secret, diff --git a/crates/api_models/src/user/dashboard_metadata.rs b/crates/api_models/src/user/dashboard_metadata.rs index 8c7e942b8f6e..11588bbfbafe 100644 --- a/crates/api_models/src/user/dashboard_metadata.rs +++ b/crates/api_models/src/user/dashboard_metadata.rs @@ -60,8 +60,8 @@ pub struct IntegrationMethod { } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub enum ConfigurationType { - Single, - Multiple, + Single, + Multiple, } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] @@ -87,7 +87,6 @@ pub struct ProdIntent { pub is_completed: bool, } - #[derive(Debug, serde::Deserialize, EnumString, serde::Serialize)] pub enum GetMetaDataRequest { ProductionAgreement, diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index f5c88474fbd3..6f831634b09f 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1,6 +1,5 @@ use api_models::user as user_api; -use diesel_models::user_role::UserRoleNew; -use diesel_models::{enums::UserStatus, user as storage_user}; +use diesel_models::{enums::UserStatus, user as storage_user, user_role::UserRoleNew}; #[cfg(feature = "email")] use error_stack::IntoReport; use error_stack::ResultExt; diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index f919b63d3fee..36b4ff0495dd 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -1,7 +1,6 @@ use api_models::user::dashboard_metadata::{self as api, GetMultipleMetaDataPayload}; use diesel_models::{ - enums::DashboardMetadata as DBEnum, - user::dashboard_metadata::{DashboardMetadata}, + enums::DashboardMetadata as DBEnum, user::dashboard_metadata::DashboardMetadata, }; use error_stack::ResultExt; @@ -91,12 +90,8 @@ fn parse_set_request(data_enum: api::SetMetaDataRequest) -> UserResult { 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::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)), @@ -518,7 +513,8 @@ async fn fetch_metadata( metadata_keys: Vec, ) -> UserResult> { let mut dashboard_metadata = Vec::with_capacity(metadata_keys.len()); - let (merchant_scoped_enums, user_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( @@ -541,10 +537,8 @@ async fn fetch_metadata( ) .await?; dashboard_metadata.append(&mut res); - } - Ok(dashboard_metadata) } diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index 9602252b2f38..40594a6e49f6 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -228,8 +228,7 @@ 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) + DBEnum::Feedback | DBEnum::ProdIntent => user_scoped.push(key), } } (merchant_scoped, user_scoped) From f4e7b19e2d6d3c8349591853e83d007d9e92eef3 Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 22:24:50 +0530 Subject: [PATCH 14/15] routes --- crates/api_models/src/events/user.rs | 8 ++- crates/router/Cargo.toml | 2 +- crates/router/src/core/user.rs | 18 +++--- .../src/core/user/dashboard_metadata.rs | 8 +-- crates/router/src/routes/app.rs | 15 +++-- crates/router/src/routes/lock_utils.rs | 3 + crates/router/src/routes/user.rs | 57 +++++++++++++++++++ crates/router_env/src/logger/types.rs | 6 ++ 8 files changed, 91 insertions(+), 26 deletions(-) diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index 3634b51e0cc0..c069dd291a0c 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -8,7 +8,7 @@ use crate::user::{ }, AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest, DashboardEntryResponse, GetUsersResponse, SignUpRequest, SignUpWithMerchantIdRequest, - SwitchMerchantIdRequest, UserMerchantCreate, + SwitchMerchantIdRequest, UserMerchantCreate, ForgotPasswordRequest, ResetPasswordRequest, InviteUserRequest, InviteUserResponse, }; impl ApiEventMetric for DashboardEntryResponse { @@ -33,7 +33,11 @@ common_utils::impl_misc_api_event_type!( UserMerchantCreate, GetUsersResponse, AuthorizeResponse, - ConnectAccountRequest + ConnectAccountRequest, + ForgotPasswordRequest, + ResetPasswordRequest, + InviteUserRequest, + InviteUserResponse ); #[cfg(feature = "dummy_connector")] diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index ab9ef3f4e469..f508460574dd 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" license.workspace = true [features] -default = ["kv_store", "stripe", "oltp", "olap", "backwards_compatibility", "accounts_cache", "dummy_connector", "payouts", "profile_specific_fallback_routing", "retry", "email"] +default = ["kv_store", "stripe", "oltp", "olap", "backwards_compatibility", "accounts_cache", "dummy_connector", "payouts", "profile_specific_fallback_routing", "retry"] s3 = ["dep:aws-sdk-s3", "dep:aws-config"] kms = ["external_services/kms", "dep:aws-config"] email = ["external_services/email", "dep:aws-config", "olap"] diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 6f831634b09f..f61dec128b8f 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1,5 +1,5 @@ use api_models::user as user_api; -use diesel_models::{enums::UserStatus, user as storage_user, user_role::UserRoleNew}; +use diesel_models::{enums::UserStatus, user as storage_user}; #[cfg(feature = "email")] use error_stack::IntoReport; use error_stack::ResultExt; @@ -11,12 +11,13 @@ 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, routes::AppState, - services::{authentication as auth, email::types::EmailToken, ApplicationResponse}, + services::{authentication as auth, ApplicationResponse}, types::domain, utils, }; @@ -330,6 +331,7 @@ pub async fn reset_password( Ok(ApplicationResponse::StatusOk) } +#[cfg(feature = "email")] pub async fn invite_user( state: AppState, request: user_api::InviteUserRequest, @@ -358,7 +360,7 @@ pub async fn invite_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 { @@ -402,7 +404,6 @@ pub async fn invite_user( .await .change_context(UserErrors::InternalServerError)?; - let is_email_sent = if cfg!(feature = "email") { let email_contents = email_types::InviteUser { recipient_email: invitee_email, user_name: domain::UserName::new(new_user.get_name())?, @@ -420,13 +421,8 @@ pub async fn invite_user( logger::info!(?send_email_result); - send_email_result.is_ok() - } else { - false - }; - Ok(ApplicationResponse::Json(user_api::InviteUserResponse { - is_email_sent, + is_email_sent: send_email_result.is_ok() })) } else { Err(UserErrors::InternalServerError.into()) diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 36b4ff0495dd..b537aa3ec732 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -490,7 +490,7 @@ async fn insert_metadata( .await } types::MetaData::IsMultipleConfiguration(data) => { - let metadata = utils::insert_merchant_scoped_metadata_to_db( + utils::insert_merchant_scoped_metadata_to_db( state, user.user_id, user.merchant_id, @@ -498,11 +498,7 @@ async fn insert_metadata( metadata_key, data, ) - .await; - if utils::is_update_required(&metadata) { - todo!() - } - metadata + .await } } } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 88806b565d3a..8506c1be33f9 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -842,21 +842,24 @@ impl User { .service(web::resource("/user/update_role").route(web::post().to(update_user_role))) .service(web::resource("/role/list").route(web::get().to(list_roles))) .service(web::resource("/role/{role_id}").route(web::get().to(get_role))); - + #[cfg(feature = "dummy_connector")] { route = route.service( web::resource("/sample_data") - .route(web::post().to(generate_sample_data)) - .route(web::delete().to(delete_sample_data)), + .route(web::post().to(generate_sample_data)) + .route(web::delete().to(delete_sample_data)), ) } #[cfg(feature = "email")] { route = route - .service( - web::resource("/connect_account").route(web::post().to(user_connect_account)), - ) + .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)), diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index b32dbe3d4b6a..63b749f20e85 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -162,6 +162,9 @@ impl From 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 => { diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index 45fa0ba35c59..0c489e43bf7f 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -294,3 +294,60 @@ pub async fn get_user_details(state: web::Data, req: HttpRequest) -> H )) .await } + +#[cfg(feature = "email")] +pub async fn forgot_password( + state: web::Data, + req: HttpRequest, + payload: web::Json, +) -> 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, + req: HttpRequest, + payload: web::Json, +) -> 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, + req: HttpRequest, + payload: web::Json, +) -> 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 +} \ No newline at end of file diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 606ae1f2f169..a1a50c91b579 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -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 } /// From 95d5f5aca882742af34a8840364cd1f86f36893a Mon Sep 17 00:00:00 2001 From: Rachit Naithani Date: Mon, 4 Dec 2023 22:28:21 +0530 Subject: [PATCH 15/15] fmt --- crates/api_models/src/events/user.rs | 5 +++-- crates/router/src/core/user.rs | 31 +++++++++++++-------------- crates/router/src/routes/app.rs | 18 ++++++++-------- crates/router/src/routes/user.rs | 2 +- crates/router_env/src/logger/types.rs | 2 +- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/crates/api_models/src/events/user.rs b/crates/api_models/src/events/user.rs index c069dd291a0c..ca2932725317 100644 --- a/crates/api_models/src/events/user.rs +++ b/crates/api_models/src/events/user.rs @@ -7,8 +7,9 @@ use crate::user::{ GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest, }, AuthorizeResponse, ChangePasswordRequest, ConnectAccountRequest, CreateInternalUserRequest, - DashboardEntryResponse, GetUsersResponse, SignUpRequest, SignUpWithMerchantIdRequest, - SwitchMerchantIdRequest, UserMerchantCreate, ForgotPasswordRequest, ResetPasswordRequest, InviteUserRequest, InviteUserResponse, + DashboardEntryResponse, ForgotPasswordRequest, GetUsersResponse, InviteUserRequest, + InviteUserResponse, ResetPasswordRequest, SignUpRequest, SignUpWithMerchantIdRequest, + SwitchMerchantIdRequest, UserMerchantCreate, }; impl ApiEventMetric for DashboardEntryResponse { diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index f61dec128b8f..01947d08d1f9 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -12,7 +12,6 @@ use router_env::logger; use super::errors::{UserErrors, UserResponse}; #[cfg(feature = "email")] use crate::services::email::{types as email_types, types::EmailToken}; - use crate::{ consts, db::user::UserInterface, @@ -404,25 +403,25 @@ pub async fn invite_user( .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 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; + 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); + logger::info!(?send_email_result); Ok(ApplicationResponse::Json(user_api::InviteUserResponse { - is_email_sent: send_email_result.is_ok() + is_email_sent: send_email_result.is_ok(), })) } else { Err(UserErrors::InternalServerError.into()) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 8506c1be33f9..06f1ba867df6 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -842,24 +842,24 @@ impl User { .service(web::resource("/user/update_role").route(web::post().to(update_user_role))) .service(web::resource("/role/list").route(web::get().to(list_roles))) .service(web::resource("/role/{role_id}").route(web::get().to(get_role))); - + #[cfg(feature = "dummy_connector")] { route = route.service( web::resource("/sample_data") - .route(web::post().to(generate_sample_data)) - .route(web::delete().to(delete_sample_data)), + .route(web::post().to(generate_sample_data)) + .route(web::delete().to(delete_sample_data)), ) } #[cfg(feature = "email")] { route = route - .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("/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)), diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index 0c489e43bf7f..c4476d6ed710 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -350,4 +350,4 @@ pub async fn invite_user( api_locking::LockAction::NotApplicable, )) .await -} \ No newline at end of file +} diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index a1a50c91b579..e7b0f67dca44 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -298,7 +298,7 @@ pub enum Flow { /// Reset password using link ResetPassword, /// Invite users - InviteUser + InviteUser, } ///