From dd1428cbfbab5ff9361dde57911e4ef21bfce449 Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Wed, 30 Apr 2025 00:04:24 +0530 Subject: [PATCH 01/15] feat(users): store and retrieve lineage_context from DB instead of Redis --- crates/diesel_models/src/schema.rs | 1 + crates/diesel_models/src/user.rs | 23 +++++++ crates/router/src/core/user.rs | 69 ++++++++++++++++--- crates/router/src/db/user.rs | 13 ++++ crates/router/src/types/domain/user.rs | 1 + .../src/types/domain/user/decision_manager.rs | 54 +++++++++++++-- .../down.sql | 2 + .../up.sql | 2 + 8 files changed, 150 insertions(+), 15 deletions(-) create mode 100644 migrations/2025-04-29-144409_add_lineage_context_to_users/down.sql create mode 100644 migrations/2025-04-29-144409_add_lineage_context_to_users/up.sql diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index c25acc386f5..964993a38dd 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1545,6 +1545,7 @@ diesel::table! { totp_secret -> Nullable, totp_recovery_codes -> Nullable>>, last_password_modified_at -> Nullable, + lineage_context -> Nullable, } } diff --git a/crates/diesel_models/src/user.rs b/crates/diesel_models/src/user.rs index cf584c09b12..3f45735b1c2 100644 --- a/crates/diesel_models/src/user.rs +++ b/crates/diesel_models/src/user.rs @@ -24,6 +24,7 @@ pub struct User { #[diesel(deserialize_as = OptionalDieselArray>)] pub totp_recovery_codes: Option>>, pub last_password_modified_at: Option, + pub lineage_context: Option, } #[derive( @@ -42,6 +43,7 @@ pub struct UserNew { pub totp_secret: Option, pub totp_recovery_codes: Option>>, pub last_password_modified_at: Option, + pub lineage_context: Option, } #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] @@ -55,6 +57,7 @@ pub struct UserUpdateInternal { totp_secret: Option, totp_recovery_codes: Option>>, last_password_modified_at: Option, + lineage_context: Option, } #[derive(Debug)] @@ -72,6 +75,9 @@ pub enum UserUpdate { PasswordUpdate { password: Secret, }, + LineageContextUpdate { + lineage_context: Option, + }, } impl From for UserUpdateInternal { @@ -87,6 +93,7 @@ impl From for UserUpdateInternal { totp_secret: None, totp_recovery_codes: None, last_password_modified_at: None, + lineage_context: None, }, UserUpdate::AccountUpdate { name, is_verified } => Self { name, @@ -97,6 +104,7 @@ impl From for UserUpdateInternal { totp_secret: None, totp_recovery_codes: None, last_password_modified_at: None, + lineage_context: None, }, UserUpdate::TotpUpdate { totp_status, @@ -111,6 +119,7 @@ impl From for UserUpdateInternal { totp_secret, totp_recovery_codes, last_password_modified_at: None, + lineage_context: None, }, UserUpdate::PasswordUpdate { password } => Self { name: None, @@ -121,6 +130,20 @@ impl From for UserUpdateInternal { totp_status: None, totp_secret: None, totp_recovery_codes: None, + lineage_context: None, + }, + UserUpdate::LineageContextUpdate { lineage_context } => Self { + name: None, + password: None, + is_verified: None, + // updating last_modified_at because of lineage context update will make it difficult to + // track the last modified time of other operations done by the user + last_modified_at, + last_password_modified_at: None, + totp_status: None, + totp_secret: None, + totp_recovery_codes: None, + lineage_context, }, } } diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 6e5360d8848..4f8ba7e9ff7 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -3182,9 +3182,26 @@ pub async fn switch_org_for_user( .clone(), }; - lineage_context - .try_set_lineage_context_in_cache(&state, user_from_token.user_id.as_str()) - .await; + let _ = state + .global_store + .update_user_by_user_id( + &user_from_token.user_id, + storage_user::UserUpdate::LineageContextUpdate { + lineage_context: Some( + serde_json::to_value(&lineage_context) + .change_context(UserErrors::InternalServerError) + .attach_printable("Failed to serialize LineageContext to JSON")?, + ), + }, + ) + .await + .map_err(|e| { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_from_token.user_id, + e + ) + }); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -3394,9 +3411,26 @@ pub async fn switch_merchant_for_user_in_org( .clone(), }; - lineage_context - .try_set_lineage_context_in_cache(&state, user_from_token.user_id.as_str()) - .await; + let _ = state + .global_store + .update_user_by_user_id( + &user_from_token.user_id, + storage_user::UserUpdate::LineageContextUpdate { + lineage_context: Some( + serde_json::to_value(&lineage_context) + .change_context(UserErrors::InternalServerError) + .attach_printable("Failed to serialize LineageContext to JSON")?, + ), + }, + ) + .await + .map_err(|e| { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_from_token.user_id, + e + ) + }); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -3527,9 +3561,26 @@ pub async fn switch_profile_for_user_in_org_and_merchant( .clone(), }; - lineage_context - .try_set_lineage_context_in_cache(&state, user_from_token.user_id.as_str()) - .await; + let _ = state + .global_store + .update_user_by_user_id( + &user_from_token.user_id, + storage_user::UserUpdate::LineageContextUpdate { + lineage_context: Some( + serde_json::to_value(&lineage_context) + .change_context(UserErrors::InternalServerError) + .attach_printable("Failed to serialize LineageContext to JSON")?, + ), + }, + ) + .await + .map_err(|e| { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_from_token.user_id, + e + ) + }); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, diff --git a/crates/router/src/db/user.rs b/crates/router/src/db/user.rs index 089c9fc6eb7..5fdf7893291 100644 --- a/crates/router/src/db/user.rs +++ b/crates/router/src/db/user.rs @@ -163,6 +163,7 @@ impl UserInterface for MockDb { totp_secret: user_data.totp_secret, totp_recovery_codes: user_data.totp_recovery_codes, last_password_modified_at: user_data.last_password_modified_at, + lineage_context: user_data.lineage_context, }; users.push(user.clone()); Ok(user) @@ -239,6 +240,12 @@ impl UserInterface for MockDb { last_password_modified_at: Some(common_utils::date_time::now()), ..user.to_owned() }, + storage::UserUpdate::LineageContextUpdate { lineage_context } => { + storage::User { + lineage_context: lineage_context.clone(), + ..user.to_owned() + } + } }; user.to_owned() }) @@ -287,6 +294,12 @@ impl UserInterface for MockDb { last_password_modified_at: Some(common_utils::date_time::now()), ..user.to_owned() }, + storage::UserUpdate::LineageContextUpdate { lineage_context } => { + storage::User { + lineage_context: lineage_context.clone(), + ..user.to_owned() + } + } }; user.to_owned() }) diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 3e0ddb971c0..9f86cfa8d85 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -888,6 +888,7 @@ impl TryFrom for storage_user::UserNew { last_password_modified_at: value .password .and_then(|password_inner| password_inner.is_temporary.not().then_some(now)), + lineage_context: None, }) } } diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 0214d3cdd15..8ebeebaac0d 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -6,6 +6,7 @@ use diesel_models::{ }; use error_stack::ResultExt; use masking::Secret; +use router_env::logger; use super::UserFromStorage; use crate::{ @@ -129,10 +130,34 @@ impl JWTFlow { user_role: &UserRole, ) -> UserResult> { let user_id = next_flow.user.get_user_id(); - let cached_lineage_context = - LineageContext::try_get_lineage_context_from_cache(state, user_id).await; + let lineage_context_from_db = state + .global_store + .find_user_by_id(user_id) + .await + .map(|user| user.lineage_context) + .map_err(|e| { + logger::error!( + "Failed to fetch lineage context from DB for user {}: {:?}", + user_id, + e + ); + e + }) + .ok() + .flatten() + .and_then(|val| { + serde_json::from_value::(val.clone()) + .inspect_err(|e| { + logger::error!( + "Failed to deserialize lineage context for user {}: {:?}", + user_id, + e + ); + }) + .ok() + }); - let new_lineage_context = match cached_lineage_context { + let new_lineage_context = match lineage_context_from_db { Some(ctx) => { let tenant_id = ctx.tenant_id.clone(); let user_role_match_v1 = state @@ -179,9 +204,26 @@ impl JWTFlow { } }; - new_lineage_context - .try_set_lineage_context_in_cache(state, user_id) - .await; + let _ = state + .global_store + .update_user_by_user_id( + user_id, + diesel_models::user::UserUpdate::LineageContextUpdate { + lineage_context: Some( + serde_json::to_value(&new_lineage_context) + .change_context(UserErrors::InternalServerError) + .attach_printable("Failed to serialize LineageContext to JSON")?, + ), + }, + ) + .await + .map_err(|e| { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_id, + e + ) + }); auth::AuthToken::new_token( new_lineage_context.user_id, diff --git a/migrations/2025-04-29-144409_add_lineage_context_to_users/down.sql b/migrations/2025-04-29-144409_add_lineage_context_to_users/down.sql new file mode 100644 index 00000000000..989e7ae64a0 --- /dev/null +++ b/migrations/2025-04-29-144409_add_lineage_context_to_users/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE users DROP COLUMN IF EXISTS lineage_context; diff --git a/migrations/2025-04-29-144409_add_lineage_context_to_users/up.sql b/migrations/2025-04-29-144409_add_lineage_context_to_users/up.sql new file mode 100644 index 00000000000..cbae98f97cc --- /dev/null +++ b/migrations/2025-04-29-144409_add_lineage_context_to_users/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE users ADD COLUMN IF NOT EXISTS lineage_context JSONB; From cb18d6b47e02705d5d62dd4d2eaa0cf0e10b8a26 Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Wed, 30 Apr 2025 00:19:49 +0530 Subject: [PATCH 02/15] check v2 user role first before v1 user role --- .../router/src/types/domain/user/decision_manager.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 8ebeebaac0d..77ff30d7500 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -160,7 +160,7 @@ impl JWTFlow { let new_lineage_context = match lineage_context_from_db { Some(ctx) => { let tenant_id = ctx.tenant_id.clone(); - let user_role_match_v1 = state + let user_role_match_v2 = state .global_store .find_user_role_by_user_id_and_lineage( &ctx.user_id, @@ -168,15 +168,15 @@ impl JWTFlow { &ctx.org_id, &ctx.merchant_id, &ctx.profile_id, - UserRoleVersion::V1, + UserRoleVersion::V2, ) .await .is_ok(); - if user_role_match_v1 { + if user_role_match_v2 { ctx } else { - let user_role_match_v2 = state + let user_role_match_v1 = state .global_store .find_user_role_by_user_id_and_lineage( &ctx.user_id, @@ -184,12 +184,12 @@ impl JWTFlow { &ctx.org_id, &ctx.merchant_id, &ctx.profile_id, - UserRoleVersion::V2, + UserRoleVersion::V1, ) .await .is_ok(); - if user_role_match_v2 { + if user_role_match_v1 { ctx } else { // fallback to default lineage if cached context is invalid From b0bd6c1ed90229c9df5d29ffd12c9bc58e9755a9 Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Wed, 30 Apr 2025 00:55:44 +0530 Subject: [PATCH 03/15] add lineage_context column in v2 schema --- crates/diesel_models/src/schema_v2.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 8f857a4db26..5ba8087f3a9 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -1475,6 +1475,7 @@ diesel::table! { totp_secret -> Nullable, totp_recovery_codes -> Nullable>>, last_password_modified_at -> Nullable, + lineage_context -> Nullable, } } From 078716d52fba072130a3510cadbb4e1b5652b88a Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Wed, 30 Apr 2025 01:07:50 +0530 Subject: [PATCH 04/15] add comment --- crates/router/src/types/domain/user/decision_manager.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 77ff30d7500..2b52dc1efa5 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -130,6 +130,7 @@ impl JWTFlow { user_role: &UserRole, ) -> UserResult> { let user_id = next_flow.user.get_user_id(); + // Fetch lineage context from DB let lineage_context_from_db = state .global_store .find_user_by_id(user_id) From 6884b69e8f901268b79d87f668c34854fb956f55 Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Thu, 1 May 2025 00:13:41 +0530 Subject: [PATCH 05/15] resolved comments and refactored code --- crates/api_models/src/user/theme.rs | 2 +- crates/common_utils/src/types.rs | 4 +- crates/common_utils/src/types/user.rs | 8 ++ .../src/types/{ => user}/theme.rs | 0 crates/common_utils/src/types/user/user.rs | 28 +++++++ crates/diesel_models/src/query/user/theme.rs | 2 +- crates/diesel_models/src/user.rs | 12 +-- crates/diesel_models/src/user/theme.rs | 2 +- crates/router/src/configs/settings.rs | 2 +- crates/router/src/consts/user.rs | 3 - crates/router/src/core/recon.rs | 2 +- crates/router/src/core/user.rs | 83 ++++++++----------- crates/router/src/core/user/theme.rs | 2 +- crates/router/src/db/kafka_store.rs | 2 +- crates/router/src/db/user/theme.rs | 2 +- crates/router/src/routes/user/theme.rs | 2 +- crates/router/src/services/email/types.rs | 2 +- crates/router/src/types/domain/user.rs | 55 +----------- .../src/types/domain/user/decision_manager.rs | 28 ++----- crates/router/src/utils/user.rs | 53 +----------- crates/router/src/utils/user/theme.rs | 2 +- crates/router/src/workflows/api_key_expiry.rs | 2 +- 22 files changed, 99 insertions(+), 199 deletions(-) create mode 100644 crates/common_utils/src/types/user.rs rename crates/common_utils/src/types/{ => user}/theme.rs (100%) create mode 100644 crates/common_utils/src/types/user/user.rs diff --git a/crates/api_models/src/user/theme.rs b/crates/api_models/src/user/theme.rs index 29b6a5d81b9..0616a5aa220 100644 --- a/crates/api_models/src/user/theme.rs +++ b/crates/api_models/src/user/theme.rs @@ -2,7 +2,7 @@ use actix_multipart::form::{bytes::Bytes, text::Text, MultipartForm}; use common_enums::EntityType; use common_utils::{ id_type, - types::theme::{EmailThemeConfig, ThemeLineage}, + types::user::{EmailThemeConfig, ThemeLineage}, }; use masking::Secret; use serde::{Deserialize, Serialize}; diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index ab61cf6cca1..55dcc4e5bd2 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -3,8 +3,8 @@ pub mod keymanager; /// Enum for Authentication Level pub mod authentication; -/// Enum for Theme Lineage -pub mod theme; +/// User related types +pub mod user; /// types that are wrappers around primitive types pub mod primitive_wrappers; diff --git a/crates/common_utils/src/types/user.rs b/crates/common_utils/src/types/user.rs new file mode 100644 index 00000000000..f904136342a --- /dev/null +++ b/crates/common_utils/src/types/user.rs @@ -0,0 +1,8 @@ +/// Theme related types +pub mod theme; + +/// User related types +pub mod user; + +pub use theme::*; +pub use user::*; diff --git a/crates/common_utils/src/types/theme.rs b/crates/common_utils/src/types/user/theme.rs similarity index 100% rename from crates/common_utils/src/types/theme.rs rename to crates/common_utils/src/types/user/theme.rs diff --git a/crates/common_utils/src/types/user/user.rs b/crates/common_utils/src/types/user/user.rs new file mode 100644 index 00000000000..81eb410f282 --- /dev/null +++ b/crates/common_utils/src/types/user/user.rs @@ -0,0 +1,28 @@ +use crate::id_type; + +use diesel::{deserialize::FromSqlRow, expression::AsExpression}; + +/// Struct for lineageContext +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, AsExpression, FromSqlRow)] +#[diesel(sql_type = diesel::sql_types::Jsonb)] +pub struct LineageContext { + /// user_id: String + pub user_id: String, + + /// merchant_id: MerchantId + pub merchant_id: id_type::MerchantId, + + /// role_id: String + pub role_id: String, + + /// org_id: OrganizationId + pub org_id: id_type::OrganizationId, + + /// profile_id: ProfileId + pub profile_id: id_type::ProfileId, + + /// tenant_id: TenantId + pub tenant_id: id_type::TenantId, +} + +crate::impl_to_sql_from_sql_json!(LineageContext); diff --git a/crates/diesel_models/src/query/user/theme.rs b/crates/diesel_models/src/query/user/theme.rs index 945ce1a9bb2..e8fcf135770 100644 --- a/crates/diesel_models/src/query/user/theme.rs +++ b/crates/diesel_models/src/query/user/theme.rs @@ -1,5 +1,5 @@ use async_bb8_diesel::AsyncRunQueryDsl; -use common_utils::types::theme::ThemeLineage; +use common_utils::types::user::ThemeLineage; use diesel::{ associations::HasTable, debug_query, diff --git a/crates/diesel_models/src/user.rs b/crates/diesel_models/src/user.rs index 3f45735b1c2..9df05131c28 100644 --- a/crates/diesel_models/src/user.rs +++ b/crates/diesel_models/src/user.rs @@ -1,4 +1,4 @@ -use common_utils::{encryption::Encryption, pii}; +use common_utils::{encryption::Encryption, pii, types::user::LineageContext}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use masking::Secret; use time::PrimitiveDateTime; @@ -24,7 +24,7 @@ pub struct User { #[diesel(deserialize_as = OptionalDieselArray>)] pub totp_recovery_codes: Option>>, pub last_password_modified_at: Option, - pub lineage_context: Option, + pub lineage_context: Option, } #[derive( @@ -43,7 +43,7 @@ pub struct UserNew { pub totp_secret: Option, pub totp_recovery_codes: Option>>, pub last_password_modified_at: Option, - pub lineage_context: Option, + pub lineage_context: Option, } #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] @@ -57,7 +57,7 @@ pub struct UserUpdateInternal { totp_secret: Option, totp_recovery_codes: Option>>, last_password_modified_at: Option, - lineage_context: Option, + lineage_context: Option, } #[derive(Debug)] @@ -76,7 +76,7 @@ pub enum UserUpdate { password: Secret, }, LineageContextUpdate { - lineage_context: Option, + lineage_context: LineageContext, }, } @@ -143,7 +143,7 @@ impl From for UserUpdateInternal { totp_status: None, totp_secret: None, totp_recovery_codes: None, - lineage_context, + lineage_context: Some(lineage_context), }, } } diff --git a/crates/diesel_models/src/user/theme.rs b/crates/diesel_models/src/user/theme.rs index 46cc90a45ec..fad45995649 100644 --- a/crates/diesel_models/src/user/theme.rs +++ b/crates/diesel_models/src/user/theme.rs @@ -1,7 +1,7 @@ use common_enums::EntityType; use common_utils::{ date_time, id_type, - types::theme::{EmailThemeConfig, ThemeLineage}, + types::user::{EmailThemeConfig, ThemeLineage}, }; use diesel::{Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index c5ab8a31219..a9b3e751aa4 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -7,7 +7,7 @@ use std::{ #[cfg(feature = "olap")] use analytics::{opensearch::OpenSearchConfig, ReportConfig}; use api_models::enums; -use common_utils::{ext_traits::ConfigExt, id_type, types::theme::EmailThemeConfig}; +use common_utils::{ext_traits::ConfigExt, id_type, types::user::EmailThemeConfig}; use config::{Environment, File}; use error_stack::ResultExt; #[cfg(feature = "email")] diff --git a/crates/router/src/consts/user.rs b/crates/router/src/consts/user.rs index 649b8820859..4f681cda25b 100644 --- a/crates/router/src/consts/user.rs +++ b/crates/router/src/consts/user.rs @@ -41,6 +41,3 @@ pub const REDIS_SSO_TTL: i64 = 5 * 60; // 5 minutes pub const DEFAULT_PROFILE_NAME: &str = "default"; pub const DEFAULT_PRODUCT_TYPE: common_enums::MerchantProductType = common_enums::MerchantProductType::Orchestration; - -pub const LINEAGE_CONTEXT_TIME_EXPIRY_IN_SECS: i64 = 60 * 60 * 24 * 7; // 7 days -pub const LINEAGE_CONTEXT_PREFIX: &str = "LINEAGE_CONTEXT_"; diff --git a/crates/router/src/core/recon.rs b/crates/router/src/core/recon.rs index 3b2aacd4514..17e86dc41ca 100644 --- a/crates/router/src/core/recon.rs +++ b/crates/router/src/core/recon.rs @@ -1,6 +1,6 @@ use api_models::recon as recon_api; #[cfg(feature = "email")] -use common_utils::{ext_traits::AsyncExt, types::theme::ThemeLineage}; +use common_utils::{ext_traits::AsyncExt, types::user::ThemeLineage}; use error_stack::ResultExt; #[cfg(feature = "email")] use masking::{ExposeInterface, PeekInterface, Secret}; diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 4f8ba7e9ff7..99095abffb0 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -8,7 +8,10 @@ use api_models::{ user::{self as user_api, InviteMultipleUserResponse, NameIdUnit}, }; use common_enums::{EntityType, UserAuthType}; -use common_utils::{type_name, types::keymanager::Identifier}; +use common_utils::{ + type_name, + types::{keymanager::Identifier, user::LineageContext}, +}; #[cfg(feature = "email")] use diesel_models::user_role::UserRoleUpdate; use diesel_models::{ @@ -3169,7 +3172,7 @@ pub async fn switch_org_for_user( } }; - let lineage_context = domain::LineageContext { + let lineage_context = LineageContext { user_id: user_from_token.user_id.clone(), merchant_id: merchant_id.clone(), role_id: role_id.clone(), @@ -3182,26 +3185,20 @@ pub async fn switch_org_for_user( .clone(), }; - let _ = state + if let Err(e) = state .global_store .update_user_by_user_id( &user_from_token.user_id, - storage_user::UserUpdate::LineageContextUpdate { - lineage_context: Some( - serde_json::to_value(&lineage_context) - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to serialize LineageContext to JSON")?, - ), - }, + storage_user::UserUpdate::LineageContextUpdate { lineage_context }, ) .await - .map_err(|e| { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_from_token.user_id, - e - ) - }); + { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_from_token.user_id, + e + ); + } let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -3398,7 +3395,7 @@ pub async fn switch_merchant_for_user_in_org( } }; - let lineage_context = domain::LineageContext { + let lineage_context = LineageContext { user_id: user_from_token.user_id.clone(), merchant_id: merchant_id.clone(), role_id: role_id.clone(), @@ -3411,26 +3408,20 @@ pub async fn switch_merchant_for_user_in_org( .clone(), }; - let _ = state + if let Err(e) = state .global_store .update_user_by_user_id( &user_from_token.user_id, - storage_user::UserUpdate::LineageContextUpdate { - lineage_context: Some( - serde_json::to_value(&lineage_context) - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to serialize LineageContext to JSON")?, - ), - }, + storage_user::UserUpdate::LineageContextUpdate { lineage_context }, ) .await - .map_err(|e| { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_from_token.user_id, - e - ) - }); + { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_from_token.user_id, + e + ); + } let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -3548,7 +3539,7 @@ pub async fn switch_profile_for_user_in_org_and_merchant( } }; - let lineage_context = domain::LineageContext { + let lineage_context = LineageContext { user_id: user_from_token.user_id.clone(), merchant_id: user_from_token.merchant_id.clone(), role_id: role_id.clone(), @@ -3561,26 +3552,20 @@ pub async fn switch_profile_for_user_in_org_and_merchant( .clone(), }; - let _ = state + if let Err(e) = state .global_store .update_user_by_user_id( &user_from_token.user_id, - storage_user::UserUpdate::LineageContextUpdate { - lineage_context: Some( - serde_json::to_value(&lineage_context) - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to serialize LineageContext to JSON")?, - ), - }, + storage_user::UserUpdate::LineageContextUpdate { lineage_context }, ) .await - .map_err(|e| { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_from_token.user_id, - e - ) - }); + { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_from_token.user_id, + e + ); + } let token = utils::user::generate_jwt_auth_token_with_attributes( &state, diff --git a/crates/router/src/core/user/theme.rs b/crates/router/src/core/user/theme.rs index 2a896b7158f..d5c9e653066 100644 --- a/crates/router/src/core/user/theme.rs +++ b/crates/router/src/core/user/theme.rs @@ -1,7 +1,7 @@ use api_models::user::theme as theme_api; use common_utils::{ ext_traits::{ByteSliceExt, Encode}, - types::theme::ThemeLineage, + types::user::ThemeLineage, }; use diesel_models::user::theme::ThemeNew; use error_stack::ResultExt; diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 0e39cbf841a..de54437a762 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -5,7 +5,7 @@ use common_enums::enums::MerchantStorageScheme; use common_utils::{ errors::CustomResult, id_type, - types::{keymanager::KeyManagerState, theme::ThemeLineage}, + types::{keymanager::KeyManagerState, user::ThemeLineage}, }; #[cfg(feature = "v2")] use diesel_models::ephemeral_key::{ClientSecretType, ClientSecretTypeNew}; diff --git a/crates/router/src/db/user/theme.rs b/crates/router/src/db/user/theme.rs index 51c0a52b3a0..4202b69d99d 100644 --- a/crates/router/src/db/user/theme.rs +++ b/crates/router/src/db/user/theme.rs @@ -1,4 +1,4 @@ -use common_utils::types::theme::ThemeLineage; +use common_utils::types::user::ThemeLineage; use diesel_models::user::theme as storage; use error_stack::report; diff --git a/crates/router/src/routes/user/theme.rs b/crates/router/src/routes/user/theme.rs index 69a8e9a5378..6f6926dadc3 100644 --- a/crates/router/src/routes/user/theme.rs +++ b/crates/router/src/routes/user/theme.rs @@ -1,7 +1,7 @@ use actix_multipart::form::MultipartForm; use actix_web::{web, HttpRequest, HttpResponse}; use api_models::user::theme as theme_api; -use common_utils::types::theme::ThemeLineage; +use common_utils::types::user::ThemeLineage; use masking::Secret; use router_env::Flow; diff --git a/crates/router/src/services/email/types.rs b/crates/router/src/services/email/types.rs index d663f72cec7..0328ea988d4 100644 --- a/crates/router/src/services/email/types.rs +++ b/crates/router/src/services/email/types.rs @@ -1,6 +1,6 @@ use api_models::user::dashboard_metadata::ProdIntent; use common_enums::{EntityType, MerchantProductType}; -use common_utils::{errors::CustomResult, pii, types::theme::EmailThemeConfig}; +use common_utils::{errors::CustomResult, pii, types::user::EmailThemeConfig}; use error_stack::ResultExt; use external_services::email::{EmailContents, EmailData, EmailError}; use masking::{ExposeInterface, Secret}; diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 9f86cfa8d85..5747a27a2a8 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -23,7 +23,7 @@ use hyperswitch_domain_models::api::ApplicationResponse; use masking::{ExposeInterface, PeekInterface, Secret}; use once_cell::sync::Lazy; use rand::distributions::{Alphanumeric, DistString}; -use router_env::{env, logger}; +use router_env::env; use time::PrimitiveDateTime; use unicode_segmentation::UnicodeSegmentation; #[cfg(feature = "keymanager_create")] @@ -39,7 +39,7 @@ use crate::{ routes::SessionState, services::{self, authentication::UserFromToken}, types::{domain, transformers::ForeignFrom}, - utils::{self, user::password}, + utils::user::password, }; pub mod dashboard_metadata; @@ -1463,54 +1463,3 @@ where .change_context(UserErrors::InternalServerError) } } - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub struct LineageContext { - pub user_id: String, - pub merchant_id: id_type::MerchantId, - pub role_id: String, - pub org_id: id_type::OrganizationId, - pub profile_id: id_type::ProfileId, - pub tenant_id: id_type::TenantId, -} - -impl LineageContext { - pub async fn try_get_lineage_context_from_cache( - state: &SessionState, - user_id: &str, - ) -> Option { - // The errors are not handled here because we don't want to fail the request if the cache operation fails. - // The errors are logged for debugging purposes. - match utils::user::get_lineage_context_from_cache(state, user_id).await { - Ok(Some(ctx)) => Some(ctx), - Ok(None) => { - logger::debug!("Lineage context not found in Redis for user {}", user_id); - None - } - Err(e) => { - logger::error!( - "Failed to retrieve lineage context from Redis for user {}: {:?}", - user_id, - e - ); - None - } - } - } - - pub async fn try_set_lineage_context_in_cache(&self, state: &SessionState, user_id: &str) { - // The errors are not handled here because we don't want to fail the request if the cache operation fails. - // The errors are logged for debugging purposes. - if let Err(e) = - utils::user::set_lineage_context_in_cache(state, user_id, self.clone()).await - { - logger::error!( - "Failed to set lineage context in Redis for user {}: {:?}", - user_id, - e - ); - } else { - logger::debug!("Lineage context cached for user {}", user_id); - } - } -} diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 2b52dc1efa5..e575b265ef0 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -1,5 +1,5 @@ use common_enums::TokenPurpose; -use common_utils::id_type; +use common_utils::{id_type, types::user::LineageContext}; use diesel_models::{ enums::{UserRoleVersion, UserStatus}, user_role::UserRole, @@ -14,7 +14,6 @@ use crate::{ db::user_role::ListUserRolesByUserIdPayload, routes::SessionState, services::authentication as auth, - types::domain::LineageContext, utils, }; @@ -135,28 +134,15 @@ impl JWTFlow { .global_store .find_user_by_id(user_id) .await - .map(|user| user.lineage_context) - .map_err(|e| { + .inspect_err(|e| { logger::error!( "Failed to fetch lineage context from DB for user {}: {:?}", user_id, e - ); - e + ) }) .ok() - .flatten() - .and_then(|val| { - serde_json::from_value::(val.clone()) - .inspect_err(|e| { - logger::error!( - "Failed to deserialize lineage context for user {}: {:?}", - user_id, - e - ); - }) - .ok() - }); + .and_then(|user| user.lineage_context); let new_lineage_context = match lineage_context_from_db { Some(ctx) => { @@ -210,11 +196,7 @@ impl JWTFlow { .update_user_by_user_id( user_id, diesel_models::user::UserUpdate::LineageContextUpdate { - lineage_context: Some( - serde_json::to_value(&new_lineage_context) - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to serialize LineageContext to JSON")?, - ), + lineage_context: new_lineage_context.clone(), }, ) .await diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index 13190125d7d..046d830e183 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -12,10 +12,7 @@ use redis_interface::RedisConnectionPool; use router_env::env; use crate::{ - consts::user::{ - LINEAGE_CONTEXT_PREFIX, LINEAGE_CONTEXT_TIME_EXPIRY_IN_SECS, REDIS_SSO_PREFIX, - REDIS_SSO_TTL, - }, + consts::user::{REDIS_SSO_PREFIX, REDIS_SSO_TTL}, core::errors::{StorageError, UserErrors, UserResult}, routes::SessionState, services::{ @@ -23,7 +20,7 @@ use crate::{ authorization::roles::RoleInfo, }, types::{ - domain::{self, LineageContext, MerchantAccount, UserFromStorage}, + domain::{self, MerchantAccount, UserFromStorage}, transformers::ForeignFrom, }, }; @@ -363,49 +360,3 @@ pub async fn validate_email_domain_auth_type_using_db( .then_some(()) .ok_or(UserErrors::InvalidUserAuthMethodOperation.into()) } - -pub async fn get_lineage_context_from_cache( - state: &SessionState, - user_id: &str, -) -> UserResult> { - let connection = get_redis_connection_for_global_tenant(state)?; - let key = format!("{}{}", LINEAGE_CONTEXT_PREFIX, user_id); - let lineage_context = connection - .get_key::>(&key.into()) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to get lineage context from redis")?; - - match lineage_context { - Some(json_str) => { - let ctx = serde_json::from_str::(&json_str) - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to deserialize LineageContext from JSON")?; - Ok(Some(ctx)) - } - None => Ok(None), - } -} - -pub async fn set_lineage_context_in_cache( - state: &SessionState, - user_id: &str, - lineage_context: LineageContext, -) -> UserResult<()> { - let connection = get_redis_connection_for_global_tenant(state)?; - let key = format!("{}{}", LINEAGE_CONTEXT_PREFIX, user_id); - let serialized_lineage_context: String = serde_json::to_string(&lineage_context) - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to serialize LineageContext")?; - connection - .set_key_with_expiry( - &key.into(), - serialized_lineage_context, - LINEAGE_CONTEXT_TIME_EXPIRY_IN_SECS, - ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Failed to set lineage context in redis")?; - - Ok(()) -} diff --git a/crates/router/src/utils/user/theme.rs b/crates/router/src/utils/user/theme.rs index 0b9d4e2829a..33e1450f097 100644 --- a/crates/router/src/utils/user/theme.rs +++ b/crates/router/src/utils/user/theme.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use common_enums::EntityType; -use common_utils::{ext_traits::AsyncExt, id_type, types::theme::ThemeLineage}; +use common_utils::{ext_traits::AsyncExt, id_type, types::user::ThemeLineage}; use diesel_models::user::theme::Theme; use error_stack::ResultExt; use hyperswitch_domain_models::merchant_key_store::MerchantKeyStore; diff --git a/crates/router/src/workflows/api_key_expiry.rs b/crates/router/src/workflows/api_key_expiry.rs index 2ff527a046e..4df78847b39 100644 --- a/crates/router/src/workflows/api_key_expiry.rs +++ b/crates/router/src/workflows/api_key_expiry.rs @@ -1,4 +1,4 @@ -use common_utils::{errors::ValidationError, ext_traits::ValueExt, types::theme::ThemeLineage}; +use common_utils::{errors::ValidationError, ext_traits::ValueExt, types::user::ThemeLineage}; use diesel_models::{ enums as storage_enums, process_tracker::business_status, ApiKeyExpiryTrackingData, }; From 87ef4608daf67bbef49061e2bee15b594709c812 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 18:44:33 +0000 Subject: [PATCH 06/15] chore: run formatter --- crates/common_utils/src/types/user/user.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/common_utils/src/types/user/user.rs b/crates/common_utils/src/types/user/user.rs index 81eb410f282..6bdecb1283b 100644 --- a/crates/common_utils/src/types/user/user.rs +++ b/crates/common_utils/src/types/user/user.rs @@ -1,7 +1,7 @@ -use crate::id_type; - use diesel::{deserialize::FromSqlRow, expression::AsExpression}; +use crate::id_type; + /// Struct for lineageContext #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, AsExpression, FromSqlRow)] #[diesel(sql_type = diesel::sql_types::Jsonb)] From e84b153f03b287067c00417791405d76a2df209f Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Thu, 1 May 2025 00:33:55 +0530 Subject: [PATCH 07/15] fixed clippy errors --- crates/common_utils/src/types/user.rs | 8 +++---- crates/common_utils/src/types/user/core.rs | 28 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 crates/common_utils/src/types/user/core.rs diff --git a/crates/common_utils/src/types/user.rs b/crates/common_utils/src/types/user.rs index f904136342a..36722e61a06 100644 --- a/crates/common_utils/src/types/user.rs +++ b/crates/common_utils/src/types/user.rs @@ -1,8 +1,8 @@ +/// User related types +pub mod core; + /// Theme related types pub mod theme; -/// User related types -pub mod user; - +pub use core::*; pub use theme::*; -pub use user::*; diff --git a/crates/common_utils/src/types/user/core.rs b/crates/common_utils/src/types/user/core.rs new file mode 100644 index 00000000000..81eb410f282 --- /dev/null +++ b/crates/common_utils/src/types/user/core.rs @@ -0,0 +1,28 @@ +use crate::id_type; + +use diesel::{deserialize::FromSqlRow, expression::AsExpression}; + +/// Struct for lineageContext +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, AsExpression, FromSqlRow)] +#[diesel(sql_type = diesel::sql_types::Jsonb)] +pub struct LineageContext { + /// user_id: String + pub user_id: String, + + /// merchant_id: MerchantId + pub merchant_id: id_type::MerchantId, + + /// role_id: String + pub role_id: String, + + /// org_id: OrganizationId + pub org_id: id_type::OrganizationId, + + /// profile_id: ProfileId + pub profile_id: id_type::ProfileId, + + /// tenant_id: TenantId + pub tenant_id: id_type::TenantId, +} + +crate::impl_to_sql_from_sql_json!(LineageContext); From 07a5d92017b2378bec8740db33a2cd7baa130c24 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 19:04:56 +0000 Subject: [PATCH 08/15] chore: run formatter --- crates/common_utils/src/types/user.rs | 1 + crates/common_utils/src/types/user/core.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/common_utils/src/types/user.rs b/crates/common_utils/src/types/user.rs index 36722e61a06..e00f07309d1 100644 --- a/crates/common_utils/src/types/user.rs +++ b/crates/common_utils/src/types/user.rs @@ -5,4 +5,5 @@ pub mod core; pub mod theme; pub use core::*; + pub use theme::*; diff --git a/crates/common_utils/src/types/user/core.rs b/crates/common_utils/src/types/user/core.rs index 81eb410f282..6bdecb1283b 100644 --- a/crates/common_utils/src/types/user/core.rs +++ b/crates/common_utils/src/types/user/core.rs @@ -1,7 +1,7 @@ -use crate::id_type; - use diesel::{deserialize::FromSqlRow, expression::AsExpression}; +use crate::id_type; + /// Struct for lineageContext #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, AsExpression, FromSqlRow)] #[diesel(sql_type = diesel::sql_types::Jsonb)] From a716d299dff181a72b3a163c5129464db0acff7d Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Thu, 1 May 2025 00:48:56 +0530 Subject: [PATCH 09/15] fixed clippy errors --- crates/router/src/db/user.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/router/src/db/user.rs b/crates/router/src/db/user.rs index 5fdf7893291..23a0eafb004 100644 --- a/crates/router/src/db/user.rs +++ b/crates/router/src/db/user.rs @@ -242,7 +242,7 @@ impl UserInterface for MockDb { }, storage::UserUpdate::LineageContextUpdate { lineage_context } => { storage::User { - lineage_context: lineage_context.clone(), + lineage_context: Some(lineage_context.clone()), ..user.to_owned() } } @@ -296,7 +296,7 @@ impl UserInterface for MockDb { }, storage::UserUpdate::LineageContextUpdate { lineage_context } => { storage::User { - lineage_context: lineage_context.clone(), + lineage_context: Some(lineage_context.clone()), ..user.to_owned() } } From 187002be2473649b78f94ace6471c52106970486 Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Thu, 1 May 2025 00:52:13 +0530 Subject: [PATCH 10/15] removed comment --- crates/diesel_models/src/user.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/diesel_models/src/user.rs b/crates/diesel_models/src/user.rs index 9df05131c28..a05e9768c09 100644 --- a/crates/diesel_models/src/user.rs +++ b/crates/diesel_models/src/user.rs @@ -136,8 +136,6 @@ impl From for UserUpdateInternal { name: None, password: None, is_verified: None, - // updating last_modified_at because of lineage context update will make it difficult to - // track the last modified time of other operations done by the user last_modified_at, last_password_modified_at: None, totp_status: None, From c3ed3a8f00363dfc9caa73948cf70e64cecda625 Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Fri, 2 May 2025 12:41:57 +0530 Subject: [PATCH 11/15] update last_modified_at for MockDb --- crates/router/src/db/user.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/router/src/db/user.rs b/crates/router/src/db/user.rs index 23a0eafb004..008c1f721d0 100644 --- a/crates/router/src/db/user.rs +++ b/crates/router/src/db/user.rs @@ -209,17 +209,20 @@ impl UserInterface for MockDb { update_user: storage::UserUpdate, ) -> CustomResult { let mut users = self.users.lock().await; + let last_modified_at = common_utils::date_time::now(); users .iter_mut() .find(|user| user.user_id == user_id) .map(|user| { *user = match &update_user { storage::UserUpdate::VerifyUser => storage::User { + last_modified_at, is_verified: true, ..user.to_owned() }, storage::UserUpdate::AccountUpdate { name, is_verified } => storage::User { name: name.clone().map(Secret::new).unwrap_or(user.name.clone()), + last_modified_at, is_verified: is_verified.unwrap_or(user.is_verified), ..user.to_owned() }, @@ -228,6 +231,7 @@ impl UserInterface for MockDb { totp_secret, totp_recovery_codes, } => storage::User { + last_modified_at, totp_status: totp_status.unwrap_or(user.totp_status), totp_secret: totp_secret.clone().or(user.totp_secret.clone()), totp_recovery_codes: totp_recovery_codes @@ -242,6 +246,7 @@ impl UserInterface for MockDb { }, storage::UserUpdate::LineageContextUpdate { lineage_context } => { storage::User { + last_modified_at, lineage_context: Some(lineage_context.clone()), ..user.to_owned() } @@ -263,17 +268,20 @@ impl UserInterface for MockDb { update_user: storage::UserUpdate, ) -> CustomResult { let mut users = self.users.lock().await; + let last_modified_at = common_utils::date_time::now(); users .iter_mut() .find(|user| user.email.eq(user_email.get_inner())) .map(|user| { *user = match &update_user { storage::UserUpdate::VerifyUser => storage::User { + last_modified_at, is_verified: true, ..user.to_owned() }, storage::UserUpdate::AccountUpdate { name, is_verified } => storage::User { name: name.clone().map(Secret::new).unwrap_or(user.name.clone()), + last_modified_at, is_verified: is_verified.unwrap_or(user.is_verified), ..user.to_owned() }, @@ -282,6 +290,7 @@ impl UserInterface for MockDb { totp_secret, totp_recovery_codes, } => storage::User { + last_modified_at, totp_status: totp_status.unwrap_or(user.totp_status), totp_secret: totp_secret.clone().or(user.totp_secret.clone()), totp_recovery_codes: totp_recovery_codes @@ -296,6 +305,7 @@ impl UserInterface for MockDb { }, storage::UserUpdate::LineageContextUpdate { lineage_context } => { storage::User { + last_modified_at, lineage_context: Some(lineage_context.clone()), ..user.to_owned() } From e80d1270c14f944c849d4dd9f82736cc10704ea5 Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Fri, 2 May 2025 16:19:46 +0530 Subject: [PATCH 12/15] move lineage_context DB update to background using tokio::spawn --- crates/router/src/core/user.rs | 105 +++++++++++------- .../src/types/domain/user/decision_manager.rs | 39 ++++--- 2 files changed, 86 insertions(+), 58 deletions(-) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 99095abffb0..e035a89dd51 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -3185,20 +3185,27 @@ pub async fn switch_org_for_user( .clone(), }; - if let Err(e) = state - .global_store - .update_user_by_user_id( - &user_from_token.user_id, - storage_user::UserUpdate::LineageContextUpdate { lineage_context }, - ) - .await - { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_from_token.user_id, - e - ); - } + tokio::spawn({ + let state = state.clone(); + let lineage_context = lineage_context.clone(); + let user_id = user_from_token.user_id.clone(); + async move { + let _ = state + .global_store + .update_user_by_user_id( + &user_id, + diesel_models::user::UserUpdate::LineageContextUpdate { lineage_context }, + ) + .await + .map_err(|e| { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_id, + e + ); + }); + } + }); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -3408,20 +3415,27 @@ pub async fn switch_merchant_for_user_in_org( .clone(), }; - if let Err(e) = state - .global_store - .update_user_by_user_id( - &user_from_token.user_id, - storage_user::UserUpdate::LineageContextUpdate { lineage_context }, - ) - .await - { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_from_token.user_id, - e - ); - } + tokio::spawn({ + let state = state.clone(); + let lineage_context = lineage_context.clone(); + let user_id = user_from_token.user_id.clone(); + async move { + let _ = state + .global_store + .update_user_by_user_id( + &user_id, + diesel_models::user::UserUpdate::LineageContextUpdate { lineage_context }, + ) + .await + .map_err(|e| { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_id, + e + ); + }); + } + }); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -3552,20 +3566,27 @@ pub async fn switch_profile_for_user_in_org_and_merchant( .clone(), }; - if let Err(e) = state - .global_store - .update_user_by_user_id( - &user_from_token.user_id, - storage_user::UserUpdate::LineageContextUpdate { lineage_context }, - ) - .await - { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_from_token.user_id, - e - ); - } + tokio::spawn({ + let state = state.clone(); + let lineage_context = lineage_context.clone(); + let user_id = user_from_token.user_id.clone(); + async move { + let _ = state + .global_store + .update_user_by_user_id( + &user_id, + diesel_models::user::UserUpdate::LineageContextUpdate { lineage_context }, + ) + .await + .map_err(|e| { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_id, + e + ); + }); + } + }); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index e575b265ef0..04183c1e727 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -191,22 +191,29 @@ impl JWTFlow { } }; - let _ = state - .global_store - .update_user_by_user_id( - user_id, - diesel_models::user::UserUpdate::LineageContextUpdate { - lineage_context: new_lineage_context.clone(), - }, - ) - .await - .map_err(|e| { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_id, - e - ) - }); + tokio::spawn({ + let state = state.clone(); + let user_id = user_id.to_string(); + let new_lineage_context = new_lineage_context.clone(); + async move { + let _ = state + .global_store + .update_user_by_user_id( + &user_id, + diesel_models::user::UserUpdate::LineageContextUpdate { + lineage_context: new_lineage_context, + }, + ) + .await + .map_err(|e| { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_id, + e + ); + }); + } + }); auth::AuthToken::new_token( new_lineage_context.user_id, From 670c2e98990bc533b6754e2e673b22860b35d453 Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Fri, 2 May 2025 16:42:50 +0530 Subject: [PATCH 13/15] added logging message for successfully updating lineage context --- crates/router/src/core/user.rs | 18 ++++++++++-------- .../src/types/domain/user/decision_manager.rs | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index e035a89dd51..7e3a53d23a7 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -3190,20 +3190,22 @@ pub async fn switch_org_for_user( let lineage_context = lineage_context.clone(); let user_id = user_from_token.user_id.clone(); async move { - let _ = state + if let Err(e) = state .global_store .update_user_by_user_id( &user_id, diesel_models::user::UserUpdate::LineageContextUpdate { lineage_context }, ) .await - .map_err(|e| { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_id, - e - ); - }); + { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_id, + e + ); + } else { + logger::debug!("Successfully updated lineage context for user {}", user_id); + } } }); diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 04183c1e727..63e464dc077 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -196,7 +196,7 @@ impl JWTFlow { let user_id = user_id.to_string(); let new_lineage_context = new_lineage_context.clone(); async move { - let _ = state + if let Err(e) = state .global_store .update_user_by_user_id( &user_id, @@ -205,13 +205,15 @@ impl JWTFlow { }, ) .await - .map_err(|e| { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_id, - e - ); - }); + { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_id, + e + ); + } else { + logger::debug!("Lineage context updated for user {}", user_id); + } } }); From 4ca1ce6b974f830af54294afaefb48e41eaecfad Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Fri, 2 May 2025 16:46:15 +0530 Subject: [PATCH 14/15] deleted file --- crates/common_utils/src/types/user/user.rs | 28 ---------------------- 1 file changed, 28 deletions(-) delete mode 100644 crates/common_utils/src/types/user/user.rs diff --git a/crates/common_utils/src/types/user/user.rs b/crates/common_utils/src/types/user/user.rs deleted file mode 100644 index 6bdecb1283b..00000000000 --- a/crates/common_utils/src/types/user/user.rs +++ /dev/null @@ -1,28 +0,0 @@ -use diesel::{deserialize::FromSqlRow, expression::AsExpression}; - -use crate::id_type; - -/// Struct for lineageContext -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, AsExpression, FromSqlRow)] -#[diesel(sql_type = diesel::sql_types::Jsonb)] -pub struct LineageContext { - /// user_id: String - pub user_id: String, - - /// merchant_id: MerchantId - pub merchant_id: id_type::MerchantId, - - /// role_id: String - pub role_id: String, - - /// org_id: OrganizationId - pub org_id: id_type::OrganizationId, - - /// profile_id: ProfileId - pub profile_id: id_type::ProfileId, - - /// tenant_id: TenantId - pub tenant_id: id_type::TenantId, -} - -crate::impl_to_sql_from_sql_json!(LineageContext); From cdb7ce6122863a5dcdb9c709271e0aa93879f3da Mon Sep 17 00:00:00 2001 From: Sandeep Kumar Date: Tue, 6 May 2025 13:32:37 +0530 Subject: [PATCH 15/15] moved the lineage_db_update to a util function --- crates/router/src/core/user.rs | 80 ++++--------------- .../src/types/domain/user/decision_manager.rs | 30 ++----- crates/router/src/utils/user.rs | 38 ++++++++- 3 files changed, 56 insertions(+), 92 deletions(-) diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 7e3a53d23a7..cad8feef70e 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -3185,29 +3185,11 @@ pub async fn switch_org_for_user( .clone(), }; - tokio::spawn({ - let state = state.clone(); - let lineage_context = lineage_context.clone(); - let user_id = user_from_token.user_id.clone(); - async move { - if let Err(e) = state - .global_store - .update_user_by_user_id( - &user_id, - diesel_models::user::UserUpdate::LineageContextUpdate { lineage_context }, - ) - .await - { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_id, - e - ); - } else { - logger::debug!("Successfully updated lineage context for user {}", user_id); - } - } - }); + utils::user::spawn_async_lineage_context_update_to_db( + &state, + &user_from_token.user_id, + lineage_context, + ); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -3417,27 +3399,11 @@ pub async fn switch_merchant_for_user_in_org( .clone(), }; - tokio::spawn({ - let state = state.clone(); - let lineage_context = lineage_context.clone(); - let user_id = user_from_token.user_id.clone(); - async move { - let _ = state - .global_store - .update_user_by_user_id( - &user_id, - diesel_models::user::UserUpdate::LineageContextUpdate { lineage_context }, - ) - .await - .map_err(|e| { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_id, - e - ); - }); - } - }); + utils::user::spawn_async_lineage_context_update_to_db( + &state, + &user_from_token.user_id, + lineage_context, + ); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, @@ -3568,27 +3534,11 @@ pub async fn switch_profile_for_user_in_org_and_merchant( .clone(), }; - tokio::spawn({ - let state = state.clone(); - let lineage_context = lineage_context.clone(); - let user_id = user_from_token.user_id.clone(); - async move { - let _ = state - .global_store - .update_user_by_user_id( - &user_id, - diesel_models::user::UserUpdate::LineageContextUpdate { lineage_context }, - ) - .await - .map_err(|e| { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_id, - e - ); - }); - } - }); + utils::user::spawn_async_lineage_context_update_to_db( + &state, + &user_from_token.user_id, + lineage_context, + ); let token = utils::user::generate_jwt_auth_token_with_attributes( &state, diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 63e464dc077..7bc11383da0 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -191,31 +191,11 @@ impl JWTFlow { } }; - tokio::spawn({ - let state = state.clone(); - let user_id = user_id.to_string(); - let new_lineage_context = new_lineage_context.clone(); - async move { - if let Err(e) = state - .global_store - .update_user_by_user_id( - &user_id, - diesel_models::user::UserUpdate::LineageContextUpdate { - lineage_context: new_lineage_context, - }, - ) - .await - { - logger::error!( - "Failed to update lineage context for user {}: {:?}", - user_id, - e - ); - } else { - logger::debug!("Lineage context updated for user {}", user_id); - } - } - }); + utils::user::spawn_async_lineage_context_update_to_db( + state, + user_id, + new_lineage_context.clone(), + ); auth::AuthToken::new_token( new_lineage_context.user_id, diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index 046d830e183..fb4e4d1d35f 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -3,13 +3,16 @@ use std::sync::Arc; use api_models::user as user_api; use common_enums::UserAuthType; use common_utils::{ - encryption::Encryption, errors::CustomResult, id_type, type_name, types::keymanager::Identifier, + encryption::Encryption, + errors::CustomResult, + id_type, type_name, + types::{keymanager::Identifier, user::LineageContext}, }; use diesel_models::organization::{self, OrganizationBridge}; use error_stack::ResultExt; use masking::{ExposeInterface, Secret}; use redis_interface::RedisConnectionPool; -use router_env::env; +use router_env::{env, logger}; use crate::{ consts::user::{REDIS_SSO_PREFIX, REDIS_SSO_TTL}, @@ -360,3 +363,34 @@ pub async fn validate_email_domain_auth_type_using_db( .then_some(()) .ok_or(UserErrors::InvalidUserAuthMethodOperation.into()) } + +pub fn spawn_async_lineage_context_update_to_db( + state: &SessionState, + user_id: &str, + lineage_context: LineageContext, +) { + let state = state.clone(); + let lineage_context = lineage_context.clone(); + let user_id = user_id.to_owned(); + tokio::spawn(async move { + match state + .global_store + .update_user_by_user_id( + &user_id, + diesel_models::user::UserUpdate::LineageContextUpdate { lineage_context }, + ) + .await + { + Ok(_) => { + logger::debug!("Successfully updated lineage context for user {}", user_id); + } + Err(e) => { + logger::error!( + "Failed to update lineage context for user {}: {:?}", + user_id, + e + ); + } + } + }); +}