mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 12:06:56 +08:00
feat(user): add user_list and switch_list apis (#3033)
Co-authored-by: Mani Chandra Dulam <mani.dchandra@juspay.in>
This commit is contained in:
@ -324,3 +324,29 @@ pub async fn create_merchant_account(
|
||||
|
||||
Ok(ApplicationResponse::StatusOk)
|
||||
}
|
||||
|
||||
pub async fn list_merchant_ids_for_user(
|
||||
state: AppState,
|
||||
user: auth::UserFromToken,
|
||||
) -> UserResponse<Vec<String>> {
|
||||
Ok(ApplicationResponse::Json(
|
||||
utils::user::get_merchant_ids_for_user(state, &user.user_id).await?,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_users_for_merchant_account(
|
||||
state: AppState,
|
||||
user_from_token: auth::UserFromToken,
|
||||
) -> UserResponse<user_api::GetUsersResponse> {
|
||||
let users = state
|
||||
.store
|
||||
.find_users_and_roles_by_merchant_id(user_from_token.merchant_id.as_str())
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)
|
||||
.attach_printable("No users for given merchant id")?
|
||||
.into_iter()
|
||||
.filter_map(|(user, role)| domain::UserAndRoleJoined(user, role).try_into().ok())
|
||||
.collect();
|
||||
|
||||
Ok(ApplicationResponse::Json(user_api::GetUsersResponse(users)))
|
||||
}
|
||||
|
||||
@ -14,6 +14,14 @@ pub trait DashboardMetadataInterface {
|
||||
&self,
|
||||
metadata: storage::DashboardMetadataNew,
|
||||
) -> CustomResult<storage::DashboardMetadata, errors::StorageError>;
|
||||
async fn update_metadata(
|
||||
&self,
|
||||
user_id: Option<String>,
|
||||
merchant_id: String,
|
||||
org_id: String,
|
||||
data_key: enums::DashboardMetadata,
|
||||
dashboard_metadata_update: storage::DashboardMetadataUpdate,
|
||||
) -> CustomResult<storage::DashboardMetadata, errors::StorageError>;
|
||||
|
||||
async fn find_user_scoped_dashboard_metadata(
|
||||
&self,
|
||||
@ -44,6 +52,28 @@ impl DashboardMetadataInterface for Store {
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn update_metadata(
|
||||
&self,
|
||||
user_id: Option<String>,
|
||||
merchant_id: String,
|
||||
org_id: String,
|
||||
data_key: enums::DashboardMetadata,
|
||||
dashboard_metadata_update: storage::DashboardMetadataUpdate,
|
||||
) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::DashboardMetadata::update(
|
||||
&conn,
|
||||
user_id,
|
||||
merchant_id,
|
||||
org_id,
|
||||
data_key,
|
||||
dashboard_metadata_update,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn find_user_scoped_dashboard_metadata(
|
||||
&self,
|
||||
user_id: &str,
|
||||
@ -121,6 +151,41 @@ impl DashboardMetadataInterface for MockDb {
|
||||
Ok(metadata_new)
|
||||
}
|
||||
|
||||
async fn update_metadata(
|
||||
&self,
|
||||
user_id: Option<String>,
|
||||
merchant_id: String,
|
||||
org_id: String,
|
||||
data_key: enums::DashboardMetadata,
|
||||
dashboard_metadata_update: storage::DashboardMetadataUpdate,
|
||||
) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
|
||||
let mut dashboard_metadata = self.dashboard_metadata.lock().await;
|
||||
|
||||
let dashboard_metadata_to_update = dashboard_metadata
|
||||
.iter_mut()
|
||||
.find(|metadata| {
|
||||
metadata.user_id == user_id
|
||||
&& metadata.merchant_id == merchant_id
|
||||
&& metadata.org_id == org_id
|
||||
&& metadata.data_key == data_key
|
||||
})
|
||||
.ok_or(errors::StorageError::MockDbError)?;
|
||||
|
||||
match dashboard_metadata_update {
|
||||
storage::DashboardMetadataUpdate::UpdateData {
|
||||
data_key,
|
||||
data_value,
|
||||
last_modified_by,
|
||||
} => {
|
||||
dashboard_metadata_to_update.data_key = data_key;
|
||||
dashboard_metadata_to_update.data_value = data_value;
|
||||
dashboard_metadata_to_update.last_modified_by = last_modified_by;
|
||||
dashboard_metadata_to_update.last_modified_at = common_utils::date_time::now();
|
||||
}
|
||||
}
|
||||
Ok(dashboard_metadata_to_update.clone())
|
||||
}
|
||||
|
||||
async fn find_user_scoped_dashboard_metadata(
|
||||
&self,
|
||||
user_id: &str,
|
||||
|
||||
@ -1878,6 +1878,15 @@ impl UserInterface for KafkaStore {
|
||||
) -> CustomResult<bool, errors::StorageError> {
|
||||
self.diesel_store.delete_user_by_user_id(user_id).await
|
||||
}
|
||||
|
||||
async fn find_users_and_roles_by_merchant_id(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<Vec<(storage::User, user_storage::UserRole)>, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.find_users_and_roles_by_merchant_id(merchant_id)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl RedisConnInterface for KafkaStore {
|
||||
@ -1930,6 +1939,25 @@ impl DashboardMetadataInterface for KafkaStore {
|
||||
self.diesel_store.insert_metadata(metadata).await
|
||||
}
|
||||
|
||||
async fn update_metadata(
|
||||
&self,
|
||||
user_id: Option<String>,
|
||||
merchant_id: String,
|
||||
org_id: String,
|
||||
data_key: enums::DashboardMetadata,
|
||||
dashboard_metadata_update: storage::DashboardMetadataUpdate,
|
||||
) -> CustomResult<storage::DashboardMetadata, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.update_metadata(
|
||||
user_id,
|
||||
merchant_id,
|
||||
org_id,
|
||||
data_key,
|
||||
dashboard_metadata_update,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn find_user_scoped_dashboard_metadata(
|
||||
&self,
|
||||
user_id: &str,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use diesel_models::user as storage;
|
||||
use diesel_models::{user as storage, user_role::UserRole};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::Secret;
|
||||
|
||||
@ -37,6 +37,11 @@ pub trait UserInterface {
|
||||
&self,
|
||||
user_id: &str,
|
||||
) -> CustomResult<bool, errors::StorageError>;
|
||||
|
||||
async fn find_users_and_roles_by_merchant_id(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<Vec<(storage::User, UserRole)>, errors::StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -97,6 +102,17 @@ impl UserInterface for Store {
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
|
||||
async fn find_users_and_roles_by_merchant_id(
|
||||
&self,
|
||||
merchant_id: &str,
|
||||
) -> CustomResult<Vec<(storage::User, UserRole)>, errors::StorageError> {
|
||||
let conn = connection::pg_connection_write(self).await?;
|
||||
storage::User::find_joined_users_and_roles_by_merchant_id(&conn, merchant_id)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.into_report()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -222,45 +238,11 @@ impl UserInterface for MockDb {
|
||||
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(
|
||||
async fn find_users_and_roles_by_merchant_id(
|
||||
&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
|
||||
_merchant_id: &str,
|
||||
) -> CustomResult<Vec<(storage::User, UserRole)>, errors::StorageError> {
|
||||
Err(errors::StorageError::MockDbError)?
|
||||
}
|
||||
}
|
||||
|
||||
@ -839,6 +839,8 @@ impl User {
|
||||
web::resource("/create_merchant")
|
||||
.route(web::post().to(user_merchant_account_create)),
|
||||
)
|
||||
.service(web::resource("/switch/list").route(web::get().to(list_merchant_ids_for_user)))
|
||||
.service(web::resource("/user/list").route(web::get().to(get_user_details)))
|
||||
// User Role APIs
|
||||
.service(web::resource("/permission_info").route(web::get().to(get_authorization_info)))
|
||||
.service(web::resource("/user/update_role").route(web::post().to(update_user_role)))
|
||||
|
||||
@ -157,7 +157,9 @@ impl From<Flow> for ApiIdentifier {
|
||||
| Flow::SwitchMerchant
|
||||
| Flow::UserMerchantAccountCreate
|
||||
| Flow::GenerateSampleData
|
||||
| Flow::DeleteSampleData => Self::User,
|
||||
| Flow::DeleteSampleData
|
||||
| Flow::UserMerchantAccountList
|
||||
| Flow::GetUserDetails => Self::User,
|
||||
|
||||
Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => {
|
||||
Self::UserRole
|
||||
|
||||
@ -204,3 +204,34 @@ pub async fn delete_sample_data(
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_merchant_ids_for_user(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::UserMerchantAccountList;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
(),
|
||||
|state, user, _| user_core::list_merchant_ids_for_user(state, user),
|
||||
&auth::DashboardNoPermissionAuth,
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_user_details(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
|
||||
let flow = Flow::GetUserDetails;
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state.clone(),
|
||||
&req,
|
||||
(),
|
||||
|state, user, _| user_core::get_users_for_merchant_account(state, user),
|
||||
&auth::JWTAuth(Permission::UsersRead),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ use crate::{
|
||||
routes::AppState,
|
||||
services::{
|
||||
authentication::{AuthToken, UserFromToken},
|
||||
authorization::info,
|
||||
authorization::{info, predefined_permissions},
|
||||
},
|
||||
types::transformers::ForeignFrom,
|
||||
utils::user::password,
|
||||
@ -671,3 +671,30 @@ impl TryFrom<info::PermissionInfo> for user_role_api::PermissionInfo {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UserAndRoleJoined(pub storage_user::User, pub UserRole);
|
||||
|
||||
impl TryFrom<UserAndRoleJoined> for user_api::UserDetails {
|
||||
type Error = ();
|
||||
fn try_from(user_and_role: UserAndRoleJoined) -> Result<Self, Self::Error> {
|
||||
let status = match user_and_role.1.status {
|
||||
UserStatus::Active => user_role_api::UserStatus::Active,
|
||||
UserStatus::InvitationSent => user_role_api::UserStatus::InvitationSent,
|
||||
};
|
||||
|
||||
let role_id = user_and_role.1.role_id;
|
||||
let role_name = predefined_permissions::get_role_name_from_id(role_id.as_str())
|
||||
.ok_or(())?
|
||||
.to_string();
|
||||
|
||||
Ok(Self {
|
||||
user_id: user_and_role.0.user_id,
|
||||
email: user_and_role.0.email,
|
||||
name: user_and_role.0.name,
|
||||
role_id,
|
||||
status,
|
||||
role_name,
|
||||
last_modified_at: user_and_role.1.last_modified_at,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use diesel_models::enums::UserStatus;
|
||||
use error_stack::ResultExt;
|
||||
|
||||
use crate::{
|
||||
@ -51,3 +52,19 @@ impl UserFromToken {
|
||||
Ok(user)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_merchant_ids_for_user(state: AppState, user_id: &str) -> UserResult<Vec<String>> {
|
||||
Ok(state
|
||||
.store
|
||||
.list_user_roles_by_user_id(user_id)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.into_iter()
|
||||
.filter_map(|ele| {
|
||||
if ele.status == UserStatus::Active {
|
||||
return Some(ele.merchant_id);
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user