mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +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:
@ -7,7 +7,7 @@ use crate::user::{
|
|||||||
GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest,
|
GetMetaDataRequest, GetMetaDataResponse, GetMultipleMetaDataPayload, SetMetaDataRequest,
|
||||||
},
|
},
|
||||||
ChangePasswordRequest, ConnectAccountRequest, ConnectAccountResponse,
|
ChangePasswordRequest, ConnectAccountRequest, ConnectAccountResponse,
|
||||||
CreateInternalUserRequest, SwitchMerchantIdRequest, UserMerchantCreate,
|
CreateInternalUserRequest, GetUsersResponse, SwitchMerchantIdRequest, UserMerchantCreate,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl ApiEventMetric for ConnectAccountResponse {
|
impl ApiEventMetric for ConnectAccountResponse {
|
||||||
@ -29,7 +29,8 @@ common_utils::impl_misc_api_event_type!(
|
|||||||
SetMetaDataRequest,
|
SetMetaDataRequest,
|
||||||
SwitchMerchantIdRequest,
|
SwitchMerchantIdRequest,
|
||||||
CreateInternalUserRequest,
|
CreateInternalUserRequest,
|
||||||
UserMerchantCreate
|
UserMerchantCreate,
|
||||||
|
GetUsersResponse
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "dummy_connector")]
|
#[cfg(feature = "dummy_connector")]
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
use common_utils::pii;
|
use common_utils::pii;
|
||||||
use masking::Secret;
|
use masking::Secret;
|
||||||
|
|
||||||
|
use crate::user_role::UserStatus;
|
||||||
pub mod dashboard_metadata;
|
pub mod dashboard_metadata;
|
||||||
#[cfg(feature = "dummy_connector")]
|
#[cfg(feature = "dummy_connector")]
|
||||||
pub mod sample_data;
|
pub mod sample_data;
|
||||||
@ -45,3 +47,18 @@ pub struct CreateInternalUserRequest {
|
|||||||
pub struct UserMerchantCreate {
|
pub struct UserMerchantCreate {
|
||||||
pub company_name: String,
|
pub company_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
pub struct GetUsersResponse(pub Vec<UserDetails>);
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
pub struct UserDetails {
|
||||||
|
pub user_id: String,
|
||||||
|
pub email: pii::Email,
|
||||||
|
pub name: Secret<String>,
|
||||||
|
pub role_id: String,
|
||||||
|
pub role_name: String,
|
||||||
|
pub status: UserStatus,
|
||||||
|
#[serde(with = "common_utils::custom_serde::iso8601")]
|
||||||
|
pub last_modified_at: time::PrimitiveDateTime,
|
||||||
|
}
|
||||||
|
|||||||
@ -80,3 +80,9 @@ pub struct UpdateUserRoleRequest {
|
|||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub role_id: String,
|
pub role_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
pub enum UserStatus {
|
||||||
|
Active,
|
||||||
|
InvitationSent,
|
||||||
|
}
|
||||||
|
|||||||
@ -5,7 +5,10 @@ use crate::{
|
|||||||
enums,
|
enums,
|
||||||
query::generics,
|
query::generics,
|
||||||
schema::dashboard_metadata::dsl,
|
schema::dashboard_metadata::dsl,
|
||||||
user::dashboard_metadata::{DashboardMetadata, DashboardMetadataNew},
|
user::dashboard_metadata::{
|
||||||
|
DashboardMetadata, DashboardMetadataNew, DashboardMetadataUpdate,
|
||||||
|
DashboardMetadataUpdateInternal,
|
||||||
|
},
|
||||||
PgPooledConn, StorageResult,
|
PgPooledConn, StorageResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,6 +20,31 @@ impl DashboardMetadataNew {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DashboardMetadata {
|
impl DashboardMetadata {
|
||||||
|
pub async fn update(
|
||||||
|
conn: &PgPooledConn,
|
||||||
|
user_id: Option<String>,
|
||||||
|
merchant_id: String,
|
||||||
|
org_id: String,
|
||||||
|
data_key: enums::DashboardMetadata,
|
||||||
|
dashboard_metadata_update: DashboardMetadataUpdate,
|
||||||
|
) -> StorageResult<Self> {
|
||||||
|
generics::generic_update_with_unique_predicate_get_result::<
|
||||||
|
<Self as HasTable>::Table,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
>(
|
||||||
|
conn,
|
||||||
|
dsl::user_id
|
||||||
|
.eq(user_id.to_owned())
|
||||||
|
.and(dsl::merchant_id.eq(merchant_id.to_owned()))
|
||||||
|
.and(dsl::org_id.eq(org_id.to_owned()))
|
||||||
|
.and(dsl::data_key.eq(data_key.to_owned())),
|
||||||
|
DashboardMetadataUpdateInternal::from(dashboard_metadata_update),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn find_user_scoped_dashboard_metadata(
|
pub async fn find_user_scoped_dashboard_metadata(
|
||||||
conn: &PgPooledConn,
|
conn: &PgPooledConn,
|
||||||
user_id: String,
|
user_id: String,
|
||||||
|
|||||||
@ -1,13 +1,24 @@
|
|||||||
use diesel::{associations::HasTable, ExpressionMethods};
|
use async_bb8_diesel::AsyncRunQueryDsl;
|
||||||
use error_stack::report;
|
use diesel::{
|
||||||
use router_env::tracing::{self, instrument};
|
associations::HasTable, debug_query, result::Error as DieselError, ExpressionMethods,
|
||||||
|
JoinOnDsl, QueryDsl,
|
||||||
|
};
|
||||||
|
use error_stack::{report, IntoReport};
|
||||||
|
use router_env::{
|
||||||
|
logger,
|
||||||
|
tracing::{self, instrument},
|
||||||
|
};
|
||||||
pub mod sample_data;
|
pub mod sample_data;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{self},
|
errors::{self},
|
||||||
query::generics,
|
query::generics,
|
||||||
schema::users::dsl,
|
schema::{
|
||||||
|
user_roles::{self, dsl as user_roles_dsl},
|
||||||
|
users::dsl as users_dsl,
|
||||||
|
},
|
||||||
user::*,
|
user::*,
|
||||||
|
user_role::UserRole,
|
||||||
PgPooledConn, StorageResult,
|
PgPooledConn, StorageResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,7 +33,7 @@ impl User {
|
|||||||
pub async fn find_by_user_email(conn: &PgPooledConn, user_email: &str) -> StorageResult<Self> {
|
pub async fn find_by_user_email(conn: &PgPooledConn, user_email: &str) -> StorageResult<Self> {
|
||||||
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
||||||
conn,
|
conn,
|
||||||
dsl::email.eq(user_email.to_owned()),
|
users_dsl::email.eq(user_email.to_owned()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -30,7 +41,7 @@ impl User {
|
|||||||
pub async fn find_by_user_id(conn: &PgPooledConn, user_id: &str) -> StorageResult<Self> {
|
pub async fn find_by_user_id(conn: &PgPooledConn, user_id: &str) -> StorageResult<Self> {
|
||||||
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
|
||||||
conn,
|
conn,
|
||||||
dsl::user_id.eq(user_id.to_owned()),
|
users_dsl::user_id.eq(user_id.to_owned()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -42,7 +53,7 @@ impl User {
|
|||||||
) -> StorageResult<Self> {
|
) -> StorageResult<Self> {
|
||||||
generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
|
generics::generic_update_with_results::<<Self as HasTable>::Table, _, _, _>(
|
||||||
conn,
|
conn,
|
||||||
dsl::user_id.eq(user_id.to_owned()),
|
users_dsl::user_id.eq(user_id.to_owned()),
|
||||||
UserUpdateInternal::from(user),
|
UserUpdateInternal::from(user),
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
@ -56,8 +67,28 @@ impl User {
|
|||||||
pub async fn delete_by_user_id(conn: &PgPooledConn, user_id: &str) -> StorageResult<bool> {
|
pub async fn delete_by_user_id(conn: &PgPooledConn, user_id: &str) -> StorageResult<bool> {
|
||||||
generics::generic_delete::<<Self as HasTable>::Table, _>(
|
generics::generic_delete::<<Self as HasTable>::Table, _>(
|
||||||
conn,
|
conn,
|
||||||
dsl::user_id.eq(user_id.to_owned()),
|
users_dsl::user_id.eq(user_id.to_owned()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_joined_users_and_roles_by_merchant_id(
|
||||||
|
conn: &PgPooledConn,
|
||||||
|
mid: &str,
|
||||||
|
) -> StorageResult<Vec<(Self, UserRole)>> {
|
||||||
|
let query = Self::table()
|
||||||
|
.inner_join(user_roles::table.on(user_roles_dsl::user_id.eq(users_dsl::user_id)))
|
||||||
|
.filter(user_roles_dsl::merchant_id.eq(mid.to_owned()));
|
||||||
|
|
||||||
|
logger::debug!(query = %debug_query::<diesel::pg::Pg,_>(&query).to_string());
|
||||||
|
|
||||||
|
query
|
||||||
|
.get_results_async::<(Self, UserRole)>(conn)
|
||||||
|
.await
|
||||||
|
.into_report()
|
||||||
|
.map_err(|err| match err.current_context() {
|
||||||
|
DieselError::NotFound => err.change_context(errors::DatabaseError::NotFound),
|
||||||
|
_ => err.change_context(errors::DatabaseError::Others),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,3 +33,40 @@ pub struct DashboardMetadataNew {
|
|||||||
pub last_modified_by: String,
|
pub last_modified_by: String,
|
||||||
pub last_modified_at: PrimitiveDateTime,
|
pub last_modified_at: PrimitiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay, AsChangeset,
|
||||||
|
)]
|
||||||
|
#[diesel(table_name = dashboard_metadata)]
|
||||||
|
pub struct DashboardMetadataUpdateInternal {
|
||||||
|
pub data_key: enums::DashboardMetadata,
|
||||||
|
pub data_value: serde_json::Value,
|
||||||
|
pub last_modified_by: String,
|
||||||
|
pub last_modified_at: PrimitiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DashboardMetadataUpdate {
|
||||||
|
UpdateData {
|
||||||
|
data_key: enums::DashboardMetadata,
|
||||||
|
data_value: serde_json::Value,
|
||||||
|
last_modified_by: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DashboardMetadataUpdate> for DashboardMetadataUpdateInternal {
|
||||||
|
fn from(metadata_update: DashboardMetadataUpdate) -> Self {
|
||||||
|
let last_modified_at = common_utils::date_time::now();
|
||||||
|
match metadata_update {
|
||||||
|
DashboardMetadataUpdate::UpdateData {
|
||||||
|
data_key,
|
||||||
|
data_value,
|
||||||
|
last_modified_by,
|
||||||
|
} => Self {
|
||||||
|
data_key,
|
||||||
|
data_value,
|
||||||
|
last_modified_by,
|
||||||
|
last_modified_at,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -324,3 +324,29 @@ pub async fn create_merchant_account(
|
|||||||
|
|
||||||
Ok(ApplicationResponse::StatusOk)
|
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,
|
&self,
|
||||||
metadata: storage::DashboardMetadataNew,
|
metadata: storage::DashboardMetadataNew,
|
||||||
) -> CustomResult<storage::DashboardMetadata, errors::StorageError>;
|
) -> 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(
|
async fn find_user_scoped_dashboard_metadata(
|
||||||
&self,
|
&self,
|
||||||
@ -44,6 +52,28 @@ impl DashboardMetadataInterface for Store {
|
|||||||
.into_report()
|
.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(
|
async fn find_user_scoped_dashboard_metadata(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
@ -121,6 +151,41 @@ impl DashboardMetadataInterface for MockDb {
|
|||||||
Ok(metadata_new)
|
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(
|
async fn find_user_scoped_dashboard_metadata(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
|
|||||||
@ -1878,6 +1878,15 @@ impl UserInterface for KafkaStore {
|
|||||||
) -> CustomResult<bool, errors::StorageError> {
|
) -> CustomResult<bool, errors::StorageError> {
|
||||||
self.diesel_store.delete_user_by_user_id(user_id).await
|
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 {
|
impl RedisConnInterface for KafkaStore {
|
||||||
@ -1930,6 +1939,25 @@ impl DashboardMetadataInterface for KafkaStore {
|
|||||||
self.diesel_store.insert_metadata(metadata).await
|
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(
|
async fn find_user_scoped_dashboard_metadata(
|
||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
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 error_stack::{IntoReport, ResultExt};
|
||||||
use masking::Secret;
|
use masking::Secret;
|
||||||
|
|
||||||
@ -37,6 +37,11 @@ pub trait UserInterface {
|
|||||||
&self,
|
&self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
) -> CustomResult<bool, errors::StorageError>;
|
) -> 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]
|
#[async_trait::async_trait]
|
||||||
@ -97,6 +102,17 @@ impl UserInterface for Store {
|
|||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.into_report()
|
.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]
|
#[async_trait::async_trait]
|
||||||
@ -222,45 +238,11 @@ impl UserInterface for MockDb {
|
|||||||
users.remove(user_index);
|
users.remove(user_index);
|
||||||
Ok(true)
|
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,
|
&self,
|
||||||
user_email: &str,
|
_merchant_id: &str,
|
||||||
) -> CustomResult<storage::User, errors::StorageError> {
|
) -> CustomResult<Vec<(storage::User, UserRole)>, errors::StorageError> {
|
||||||
self.diesel_store.find_user_by_email(user_email).await
|
Err(errors::StorageError::MockDbError)?
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -839,6 +839,8 @@ impl User {
|
|||||||
web::resource("/create_merchant")
|
web::resource("/create_merchant")
|
||||||
.route(web::post().to(user_merchant_account_create)),
|
.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
|
// User Role APIs
|
||||||
.service(web::resource("/permission_info").route(web::get().to(get_authorization_info)))
|
.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)))
|
.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::SwitchMerchant
|
||||||
| Flow::UserMerchantAccountCreate
|
| Flow::UserMerchantAccountCreate
|
||||||
| Flow::GenerateSampleData
|
| Flow::GenerateSampleData
|
||||||
| Flow::DeleteSampleData => Self::User,
|
| Flow::DeleteSampleData
|
||||||
|
| Flow::UserMerchantAccountList
|
||||||
|
| Flow::GetUserDetails => Self::User,
|
||||||
|
|
||||||
Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => {
|
Flow::ListRoles | Flow::GetRole | Flow::UpdateUserRole | Flow::GetAuthorizationInfo => {
|
||||||
Self::UserRole
|
Self::UserRole
|
||||||
|
|||||||
@ -204,3 +204,34 @@ pub async fn delete_sample_data(
|
|||||||
))
|
))
|
||||||
.await
|
.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,
|
routes::AppState,
|
||||||
services::{
|
services::{
|
||||||
authentication::{AuthToken, UserFromToken},
|
authentication::{AuthToken, UserFromToken},
|
||||||
authorization::info,
|
authorization::{info, predefined_permissions},
|
||||||
},
|
},
|
||||||
types::transformers::ForeignFrom,
|
types::transformers::ForeignFrom,
|
||||||
utils::user::password,
|
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 error_stack::ResultExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -51,3 +52,19 @@ impl UserFromToken {
|
|||||||
Ok(user)
|
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())
|
||||||
|
}
|
||||||
|
|||||||
@ -283,6 +283,10 @@ pub enum Flow {
|
|||||||
GenerateSampleData,
|
GenerateSampleData,
|
||||||
/// Delete Sample Data
|
/// Delete Sample Data
|
||||||
DeleteSampleData,
|
DeleteSampleData,
|
||||||
|
/// List merchant accounts for user
|
||||||
|
UserMerchantAccountList,
|
||||||
|
/// Get users for merchant account
|
||||||
|
GetUserDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user