mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(user): setup user tables (#2803)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Sahkal Poddar <sahkalplanet@gmail.com> Co-authored-by: Sahkal Poddar <sahkal.poddar@juspay.in> Co-authored-by: Sai Harsha Vardhan <56996463+sai-harsha-vardhan@users.noreply.github.com> Co-authored-by: Venkatesh <inventvenkat@gmail.com> Co-authored-by: venkatesh.devendran <venkatesh.devendran@juspay.in> Co-authored-by: Abhishek Marrivagu <68317979+Abhicodes-crypto@users.noreply.github.com>
This commit is contained in:
@ -25,6 +25,8 @@ pub mod payouts;
|
||||
pub mod refund;
|
||||
pub mod reverse_lookup;
|
||||
pub mod routing_algorithm;
|
||||
pub mod user;
|
||||
pub mod user_role;
|
||||
|
||||
use data_models::payments::{
|
||||
payment_attempt::PaymentAttemptInterface, payment_intent::PaymentIntentInterface,
|
||||
@ -80,6 +82,8 @@ pub trait StorageInterface:
|
||||
+ organization::OrganizationInterface
|
||||
+ routing_algorithm::RoutingAlgorithmInterface
|
||||
+ gsm::GsmInterface
|
||||
+ user::UserInterface
|
||||
+ user_role::UserRoleInterface
|
||||
+ 'static
|
||||
{
|
||||
fn get_scheduler_db(&self) -> Box<dyn scheduler::SchedulerInterface>;
|
||||
|
||||
265
crates/router/src/db/user.rs
Normal file
265
crates/router/src/db/user.rs
Normal file
@ -0,0 +1,265 @@
|
||||
use diesel_models::user as storage;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::Secret;
|
||||
|
||||
use super::MockDb;
|
||||
use crate::{
|
||||
connection,
|
||||
core::errors::{self, CustomResult},
|
||||
services::Store,
|
||||
};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait UserInterface {
|
||||
async fn insert_user(
|
||||
&self,
|
||||
user_data: storage::UserNew,
|
||||
) -> CustomResult<storage::User, errors::StorageError>;
|
||||
|
||||
async fn find_user_by_email(
|
||||
&self,
|
||||
user_email: &str,
|
||||
) -> CustomResult<storage::User, errors::StorageError>;
|
||||
|
||||
async fn find_user_by_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<storage::User, errors::StorageError>;
|
||||
|
||||
async fn update_user_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user: storage::UserUpdate,
|
||||
) -> CustomResult<storage::User, errors::StorageError>;
|
||||
|
||||
async fn delete_user_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<bool, errors::StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl UserInterface for Store {
|
||||
async fn insert_user(
|
||||
&self,
|
||||
user_data: storage::UserNew,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
user_data
|
||||
.insert(&conn)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn find_user_by_email(
|
||||
&self,
|
||||
user_email: &str,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::User::find_by_user_email(&conn, user_email)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn find_user_by_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::User::find_by_user_id(&conn, user_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn update_user_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user: storage::UserUpdate,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::User::update_by_user_id(&conn, user_id, user)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn delete_user_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<bool, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::User::delete_by_user_id(&conn, user_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl UserInterface for MockDb {
|
||||
async fn insert_user(
|
||||
&self,
|
||||
user_data: storage::UserNew,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
let mut users = self.users.lock().await;
|
||||
if users
|
||||
.iter()
|
||||
.any(|user| user.email == user_data.email || user.user_id == user_data.user_id)
|
||||
{
|
||||
Err(errors::StorageError::DuplicateValue {
|
||||
entity: "email or user_id",
|
||||
key: None,
|
||||
})?
|
||||
}
|
||||
let time_now = common_utils::date_time::now();
|
||||
let user = storage::User {
|
||||
id: users
|
||||
.len()
|
||||
.try_into()
|
||||
.into_report()
|
||||
.change_context(errors::StorageError::MockDbError)?,
|
||||
user_id: user_data.user_id,
|
||||
email: user_data.email,
|
||||
name: user_data.name,
|
||||
password: user_data.password,
|
||||
is_verified: user_data.is_verified,
|
||||
created_at: user_data.created_at.unwrap_or(time_now),
|
||||
last_modified_at: user_data.created_at.unwrap_or(time_now),
|
||||
};
|
||||
users.push(user.clone());
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn find_user_by_email(
|
||||
&self,
|
||||
user_email: &str,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
let users = self.users.lock().await;
|
||||
let user_email_pii: common_utils::pii::Email = user_email
|
||||
.to_string()
|
||||
.try_into()
|
||||
.map_err(|_| errors::StorageError::MockDbError)?;
|
||||
users
|
||||
.iter()
|
||||
.find(|user| user.email == user_email_pii)
|
||||
.cloned()
|
||||
.ok_or(
|
||||
errors::StorageError::ValueNotFound(format!(
|
||||
"No user available for email = {user_email}"
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn find_user_by_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
let users = self.users.lock().await;
|
||||
users
|
||||
.iter()
|
||||
.find(|user| user.user_id == user_id)
|
||||
.cloned()
|
||||
.ok_or(
|
||||
errors::StorageError::ValueNotFound(format!(
|
||||
"No user available for user_id = {user_id}"
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn update_user_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
update_user: storage::UserUpdate,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
let mut users = self.users.lock().await;
|
||||
users
|
||||
.iter_mut()
|
||||
.find(|user| user.user_id == user_id)
|
||||
.map(|user| {
|
||||
*user = match &update_user {
|
||||
storage::UserUpdate::VerifyUser => storage::User {
|
||||
is_verified: true,
|
||||
..user.to_owned()
|
||||
},
|
||||
storage::UserUpdate::AccountUpdate {
|
||||
name,
|
||||
password,
|
||||
is_verified,
|
||||
} => storage::User {
|
||||
name: name.clone().map(Secret::new).unwrap_or(user.name.clone()),
|
||||
password: password.clone().unwrap_or(user.password.clone()),
|
||||
is_verified: is_verified.unwrap_or(user.is_verified),
|
||||
..user.to_owned()
|
||||
},
|
||||
};
|
||||
user.to_owned()
|
||||
})
|
||||
.ok_or(
|
||||
errors::StorageError::ValueNotFound(format!(
|
||||
"No user available for user_id = {user_id}"
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn delete_user_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<bool, errors::StorageError> {
|
||||
let mut users = self.users.lock().await;
|
||||
let user_index = users
|
||||
.iter()
|
||||
.position(|user| user.user_id == user_id)
|
||||
.ok_or(errors::StorageError::ValueNotFound(format!(
|
||||
"No user available for user_id = {user_id}"
|
||||
)))?;
|
||||
users.remove(user_index);
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "kafka_events")]
|
||||
#[async_trait::async_trait]
|
||||
impl UserInterface for super::KafkaStore {
|
||||
async fn insert_user(
|
||||
&self,
|
||||
user_data: storage::UserNew,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
self.diesel_store.insert_user(user_data).await
|
||||
}
|
||||
|
||||
async fn find_user_by_email(
|
||||
&self,
|
||||
user_email: &str,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
self.diesel_store.find_user_by_email(user_email).await
|
||||
}
|
||||
|
||||
async fn find_user_by_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
self.diesel_store.find_user_by_id(user_id).await
|
||||
}
|
||||
|
||||
async fn update_user_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user: storage::UserUpdate,
|
||||
) -> CustomResult<storage::User, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.update_user_by_user_id(user_id, user)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delete_user_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<bool, errors::StorageError> {
|
||||
self.diesel_store.delete_user_by_user_id(user_id).await
|
||||
}
|
||||
}
|
||||
255
crates/router/src/db/user_role.rs
Normal file
255
crates/router/src/db/user_role.rs
Normal file
@ -0,0 +1,255 @@
|
||||
use diesel_models::user_role as storage;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
|
||||
use super::MockDb;
|
||||
use crate::{
|
||||
connection,
|
||||
core::errors::{self, CustomResult},
|
||||
services::Store,
|
||||
};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait UserRoleInterface {
|
||||
async fn insert_user_role(
|
||||
&self,
|
||||
user_role: storage::UserRoleNew,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||
async fn find_user_role_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||
async fn update_user_role_by_user_id_merchant_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
merchant_id: &str,
|
||||
update: storage::UserRoleUpdate,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError>;
|
||||
async fn delete_user_role(&self, user_id: &str) -> CustomResult<bool, errors::StorageError>;
|
||||
|
||||
async fn list_user_roles_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl UserRoleInterface for Store {
|
||||
async fn insert_user_role(
|
||||
&self,
|
||||
user_role: storage::UserRoleNew,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
user_role
|
||||
.insert(&conn)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn find_user_role_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::UserRole::find_by_user_id(&conn, user_id.to_owned())
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn update_user_role_by_user_id_merchant_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
merchant_id: &str,
|
||||
update: storage::UserRoleUpdate,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::UserRole::update_by_user_id_merchant_id(
|
||||
&conn,
|
||||
user_id.to_owned(),
|
||||
merchant_id.to_owned(),
|
||||
update,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn delete_user_role(&self, user_id: &str) -> CustomResult<bool, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::UserRole::delete_by_user_id(&conn, user_id.to_owned())
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn list_user_roles_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::UserRole::list_by_user_id(&conn, user_id.to_owned())
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl UserRoleInterface for MockDb {
|
||||
async fn insert_user_role(
|
||||
&self,
|
||||
user_role: storage::UserRoleNew,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
let mut user_roles = self.user_roles.lock().await;
|
||||
if user_roles
|
||||
.iter()
|
||||
.any(|user_role_inner| user_role_inner.user_id == user_role.user_id)
|
||||
{
|
||||
Err(errors::StorageError::DuplicateValue {
|
||||
entity: "user_id",
|
||||
key: None,
|
||||
})?
|
||||
}
|
||||
let user_role = storage::UserRole {
|
||||
id: user_roles
|
||||
.len()
|
||||
.try_into()
|
||||
.into_report()
|
||||
.change_context(errors::StorageError::MockDbError)?,
|
||||
user_id: user_role.user_id,
|
||||
merchant_id: user_role.merchant_id,
|
||||
role_id: user_role.role_id,
|
||||
status: user_role.status,
|
||||
created_by: user_role.created_by,
|
||||
created_at: user_role.created_at,
|
||||
last_modified_at: user_role.last_modified_at,
|
||||
last_modified_by: user_role.last_modified_by,
|
||||
org_id: user_role.org_id,
|
||||
};
|
||||
user_roles.push(user_role.clone());
|
||||
Ok(user_role)
|
||||
}
|
||||
|
||||
async fn find_user_role_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
let user_roles = self.user_roles.lock().await;
|
||||
user_roles
|
||||
.iter()
|
||||
.find(|user_role| user_role.user_id == user_id)
|
||||
.cloned()
|
||||
.ok_or(
|
||||
errors::StorageError::ValueNotFound(format!(
|
||||
"No user role available for user_id = {user_id}"
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn update_user_role_by_user_id_merchant_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
merchant_id: &str,
|
||||
update: storage::UserRoleUpdate,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
let mut user_roles = self.user_roles.lock().await;
|
||||
user_roles
|
||||
.iter_mut()
|
||||
.find(|user_role| user_role.user_id == user_id && user_role.merchant_id == merchant_id)
|
||||
.map(|user_role| {
|
||||
*user_role = match &update {
|
||||
storage::UserRoleUpdate::UpdateRole {
|
||||
role_id,
|
||||
modified_by,
|
||||
} => storage::UserRole {
|
||||
role_id: role_id.to_string(),
|
||||
last_modified_by: modified_by.to_string(),
|
||||
..user_role.to_owned()
|
||||
},
|
||||
storage::UserRoleUpdate::UpdateStatus {
|
||||
status,
|
||||
modified_by,
|
||||
} => storage::UserRole {
|
||||
status: status.to_owned(),
|
||||
last_modified_by: modified_by.to_owned(),
|
||||
..user_role.to_owned()
|
||||
},
|
||||
};
|
||||
user_role.to_owned()
|
||||
})
|
||||
.ok_or(
|
||||
errors::StorageError::ValueNotFound(format!(
|
||||
"No user role available for user_id = {user_id} and merchant_id = {merchant_id}"
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn delete_user_role(&self, user_id: &str) -> CustomResult<bool, errors::StorageError> {
|
||||
let mut user_roles = self.user_roles.lock().await;
|
||||
let user_role_index = user_roles
|
||||
.iter()
|
||||
.position(|user_role| user_role.user_id == user_id)
|
||||
.ok_or(errors::StorageError::ValueNotFound(format!(
|
||||
"No user available for user_id = {user_id}"
|
||||
)))?;
|
||||
user_roles.remove(user_role_index);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
async fn list_user_roles_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
||||
let user_roles = self.user_roles.lock().await;
|
||||
|
||||
Ok(user_roles
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter_map(|ele| {
|
||||
if ele.user_id == user_id {
|
||||
return Some(ele);
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kafka_events")]
|
||||
#[async_trait::async_trait]
|
||||
impl UserRoleInterface for super::KafkaStore {
|
||||
async fn insert_user_role(
|
||||
&self,
|
||||
user_role: storage::UserRoleNew,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
self.diesel_store.insert_user_role(user_role).await
|
||||
}
|
||||
async fn update_user_role_by_user_id_merchant_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
merchant_id: &str,
|
||||
update: storage::UserRoleUpdate,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.update_user_role_by_user_id_merchant_id(user_id, merchant_id, update)
|
||||
.await
|
||||
}
|
||||
async fn find_user_role_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<storage::UserRole, errors::StorageError> {
|
||||
self.diesel_store.find_user_role_by_user_id(user_id).await
|
||||
}
|
||||
async fn delete_user_role(&self, user_id: &str) -> CustomResult<bool, errors::StorageError> {
|
||||
self.diesel_store.delete_user_role(user_id).await
|
||||
}
|
||||
async fn list_user_roles_by_user_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<Vec<storage::UserRole>, errors::StorageError> {
|
||||
self.diesel_store.list_user_roles_by_user_id(user_id).await
|
||||
}
|
||||
}
|
||||
@ -32,6 +32,8 @@ pub mod payout_attempt;
|
||||
pub mod payouts;
|
||||
mod query;
|
||||
pub mod refund;
|
||||
pub mod user;
|
||||
pub mod user_role;
|
||||
|
||||
pub use data_models::payments::{
|
||||
payment_attempt::{PaymentAttempt, PaymentAttemptNew, PaymentAttemptUpdate},
|
||||
@ -44,7 +46,7 @@ pub use self::{
|
||||
ephemeral_key::*, events::*, file::*, gsm::*, locker_mock_up::*, mandate::*,
|
||||
merchant_account::*, merchant_connector_account::*, merchant_key_store::*, payment_link::*,
|
||||
payment_method::*, payout_attempt::*, payouts::*, process_tracker::*, refund::*,
|
||||
reverse_lookup::*, routing_algorithm::*,
|
||||
reverse_lookup::*, routing_algorithm::*, user::*, user_role::*,
|
||||
};
|
||||
use crate::types::api::routing;
|
||||
|
||||
|
||||
1
crates/router/src/types/storage/user.rs
Normal file
1
crates/router/src/types/storage/user.rs
Normal file
@ -0,0 +1 @@
|
||||
pub use diesel_models::user::*;
|
||||
1
crates/router/src/types/storage/user_role.rs
Normal file
1
crates/router/src/types/storage/user_role.rs
Normal file
@ -0,0 +1 @@
|
||||
pub use diesel_models::user_role::*;
|
||||
Reference in New Issue
Block a user