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:
Apoorv Dixit
2023-11-10 11:47:32 +05:30
committed by GitHub
parent 966369b6f2
commit 20c4226a36
19 changed files with 919 additions and 1 deletions

View File

@ -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>;

View 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
}
}

View 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
}
}

View File

@ -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;

View File

@ -0,0 +1 @@
pub use diesel_models::user::*;

View File

@ -0,0 +1 @@
pub use diesel_models::user_role::*;