mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-31 01:57:45 +08:00
feat(router): add total count for refunds list (#1935)
This commit is contained in:
@ -130,6 +130,8 @@ pub struct RefundResponse {
|
|||||||
pub struct RefundListRequest {
|
pub struct RefundListRequest {
|
||||||
/// The identifier for the payment
|
/// The identifier for the payment
|
||||||
pub payment_id: Option<String>,
|
pub payment_id: Option<String>,
|
||||||
|
/// The identifier for the refund
|
||||||
|
pub refund_id: Option<String>,
|
||||||
/// Limit on the number of objects to return
|
/// Limit on the number of objects to return
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
/// The starting point within a list of objects
|
/// The starting point within a list of objects
|
||||||
@ -160,7 +162,9 @@ pub struct TimeRange {
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
|
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
|
||||||
pub struct RefundListResponse {
|
pub struct RefundListResponse {
|
||||||
/// The number of refunds included in the list
|
/// The number of refunds included in the list
|
||||||
pub size: usize,
|
pub count: usize,
|
||||||
|
/// The total number of refunds in the list
|
||||||
|
pub total_count: i64,
|
||||||
/// The List of refund response object
|
/// The List of refund response object
|
||||||
pub data: Vec<RefundResponse>,
|
pub data: Vec<RefundResponse>,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -665,9 +665,19 @@ pub async fn refund_list(
|
|||||||
.map(ForeignInto::foreign_into)
|
.map(ForeignInto::foreign_into)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let total_count = db
|
||||||
|
.get_total_count_of_refunds(
|
||||||
|
&merchant_account.merchant_id,
|
||||||
|
&req,
|
||||||
|
merchant_account.storage_scheme,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;
|
||||||
|
|
||||||
Ok(services::ApplicationResponse::Json(
|
Ok(services::ApplicationResponse::Json(
|
||||||
api_models::refunds::RefundListResponse {
|
api_models::refunds::RefundListResponse {
|
||||||
size: data.len(),
|
count: data.len(),
|
||||||
|
total_count,
|
||||||
data,
|
data,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|||||||
@ -80,6 +80,14 @@ pub trait RefundInterface {
|
|||||||
refund_details: &api_models::refunds::TimeRange,
|
refund_details: &api_models::refunds::TimeRange,
|
||||||
storage_scheme: enums::MerchantStorageScheme,
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
) -> CustomResult<api_models::refunds::RefundListMetaData, errors::StorageError>;
|
) -> CustomResult<api_models::refunds::RefundListMetaData, errors::StorageError>;
|
||||||
|
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
async fn get_total_count_of_refunds(
|
||||||
|
&self,
|
||||||
|
merchant_id: &str,
|
||||||
|
refund_details: &api_models::refunds::RefundListRequest,
|
||||||
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> CustomResult<i64, errors::StorageError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "kv_store"))]
|
#[cfg(not(feature = "kv_store"))]
|
||||||
@ -236,6 +244,23 @@ mod storage {
|
|||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
.into_report()
|
.into_report()
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
async fn get_total_count_of_refunds(
|
||||||
|
&self,
|
||||||
|
merchant_id: &str,
|
||||||
|
refund_details: &api_models::refunds::RefundListRequest,
|
||||||
|
_storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> CustomResult<i64, errors::StorageError> {
|
||||||
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
|
<diesel_models::refund::Refund as storage_types::RefundDbExt>::get_refunds_count(
|
||||||
|
&conn,
|
||||||
|
merchant_id,
|
||||||
|
refund_details,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
.into_report()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,6 +681,26 @@ mod storage {
|
|||||||
enums::MerchantStorageScheme::RedisKv => Err(errors::StorageError::KVError.into()),
|
enums::MerchantStorageScheme::RedisKv => Err(errors::StorageError::KVError.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
async fn get_total_count_of_refunds(
|
||||||
|
&self,
|
||||||
|
merchant_id: &str,
|
||||||
|
refund_details: &api_models::refunds::RefundListRequest,
|
||||||
|
storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> CustomResult<i64, errors::StorageError> {
|
||||||
|
match storage_scheme {
|
||||||
|
enums::MerchantStorageScheme::PostgresOnly => {
|
||||||
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
|
<diesel_models::refund::Refund as storage_types::RefundDbExt>::get_refunds_count(&conn, merchant_id, refund_details)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
.into_report()
|
||||||
|
}
|
||||||
|
|
||||||
|
enums::MerchantStorageScheme::RedisKv => Err(errors::StorageError::KVError.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -816,21 +861,78 @@ impl RefundInterface for MockDb {
|
|||||||
async fn filter_refund_by_constraints(
|
async fn filter_refund_by_constraints(
|
||||||
&self,
|
&self,
|
||||||
merchant_id: &str,
|
merchant_id: &str,
|
||||||
_refund_details: &api_models::refunds::RefundListRequest,
|
refund_details: &api_models::refunds::RefundListRequest,
|
||||||
_storage_scheme: enums::MerchantStorageScheme,
|
_storage_scheme: enums::MerchantStorageScheme,
|
||||||
limit: i64,
|
limit: i64,
|
||||||
offset: i64,
|
offset: i64,
|
||||||
) -> CustomResult<Vec<diesel_models::refund::Refund>, errors::StorageError> {
|
) -> CustomResult<Vec<diesel_models::refund::Refund>, errors::StorageError> {
|
||||||
Ok(self
|
let mut unique_connectors = HashSet::new();
|
||||||
.refunds
|
let mut unique_currencies = HashSet::new();
|
||||||
.lock()
|
let mut unique_statuses = HashSet::new();
|
||||||
.await
|
|
||||||
|
// Fill the hash sets with data from refund_details
|
||||||
|
if let Some(connectors) = &refund_details.connector {
|
||||||
|
connectors.iter().for_each(|connector| {
|
||||||
|
unique_connectors.insert(connector);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(currencies) = &refund_details.currency {
|
||||||
|
currencies.iter().for_each(|currency| {
|
||||||
|
unique_currencies.insert(currency);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(refund_statuses) = &refund_details.refund_status {
|
||||||
|
refund_statuses.iter().for_each(|refund_status| {
|
||||||
|
unique_statuses.insert(refund_status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let refunds = self.refunds.lock().await;
|
||||||
|
let filtered_refunds = refunds
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|refund| refund.merchant_id == merchant_id)
|
.filter(|refund| refund.merchant_id == merchant_id)
|
||||||
|
.filter(|refund| {
|
||||||
|
refund_details
|
||||||
|
.payment_id
|
||||||
|
.clone()
|
||||||
|
.map_or(true, |id| id == refund.payment_id)
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
refund_details
|
||||||
|
.refund_id
|
||||||
|
.clone()
|
||||||
|
.map_or(true, |id| id == refund.refund_id)
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
refund.created_at
|
||||||
|
>= refund_details.time_range.map_or(
|
||||||
|
common_utils::date_time::now() - time::Duration::days(60),
|
||||||
|
|range| range.start_time,
|
||||||
|
)
|
||||||
|
&& refund.created_at
|
||||||
|
<= refund_details
|
||||||
|
.time_range
|
||||||
|
.map_or(common_utils::date_time::now(), |range| {
|
||||||
|
range.end_time.unwrap_or_else(common_utils::date_time::now)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
unique_connectors.is_empty() || unique_connectors.contains(&refund.connector)
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
unique_currencies.is_empty() || unique_currencies.contains(&refund.currency)
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
unique_statuses.is_empty() || unique_statuses.contains(&refund.refund_status)
|
||||||
|
})
|
||||||
.skip(usize::try_from(offset).unwrap_or_default())
|
.skip(usize::try_from(offset).unwrap_or_default())
|
||||||
.take(usize::try_from(limit).unwrap_or(MAX_LIMIT))
|
.take(usize::try_from(limit).unwrap_or(MAX_LIMIT))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>())
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Ok(filtered_refunds)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
@ -879,4 +981,80 @@ impl RefundInterface for MockDb {
|
|||||||
|
|
||||||
Ok(refund_meta_data)
|
Ok(refund_meta_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "olap")]
|
||||||
|
async fn get_total_count_of_refunds(
|
||||||
|
&self,
|
||||||
|
merchant_id: &str,
|
||||||
|
refund_details: &api_models::refunds::RefundListRequest,
|
||||||
|
_storage_scheme: enums::MerchantStorageScheme,
|
||||||
|
) -> CustomResult<i64, errors::StorageError> {
|
||||||
|
let mut unique_connectors = HashSet::new();
|
||||||
|
let mut unique_currencies = HashSet::new();
|
||||||
|
let mut unique_statuses = HashSet::new();
|
||||||
|
|
||||||
|
// Fill the hash sets with data from refund_details
|
||||||
|
if let Some(connectors) = &refund_details.connector {
|
||||||
|
connectors.iter().for_each(|connector| {
|
||||||
|
unique_connectors.insert(connector);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(currencies) = &refund_details.currency {
|
||||||
|
currencies.iter().for_each(|currency| {
|
||||||
|
unique_currencies.insert(currency);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(refund_statuses) = &refund_details.refund_status {
|
||||||
|
refund_statuses.iter().for_each(|refund_status| {
|
||||||
|
unique_statuses.insert(refund_status);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let refunds = self.refunds.lock().await;
|
||||||
|
let filtered_refunds = refunds
|
||||||
|
.iter()
|
||||||
|
.filter(|refund| refund.merchant_id == merchant_id)
|
||||||
|
.filter(|refund| {
|
||||||
|
refund_details
|
||||||
|
.payment_id
|
||||||
|
.clone()
|
||||||
|
.map_or(true, |id| id == refund.payment_id)
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
refund_details
|
||||||
|
.refund_id
|
||||||
|
.clone()
|
||||||
|
.map_or(true, |id| id == refund.refund_id)
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
refund.created_at
|
||||||
|
>= refund_details.time_range.map_or(
|
||||||
|
common_utils::date_time::now() - time::Duration::days(60),
|
||||||
|
|range| range.start_time,
|
||||||
|
)
|
||||||
|
&& refund.created_at
|
||||||
|
<= refund_details
|
||||||
|
.time_range
|
||||||
|
.map_or(common_utils::date_time::now(), |range| {
|
||||||
|
range.end_time.unwrap_or_else(common_utils::date_time::now)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
unique_connectors.is_empty() || unique_connectors.contains(&refund.connector)
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
unique_currencies.is_empty() || unique_currencies.contains(&refund.currency)
|
||||||
|
})
|
||||||
|
.filter(|refund| {
|
||||||
|
unique_statuses.is_empty() || unique_statuses.contains(&refund.refund_status)
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let filtered_refunds_count = filtered_refunds.len().try_into().unwrap_or_default();
|
||||||
|
|
||||||
|
Ok(filtered_refunds_count)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,12 @@ pub trait RefundDbExt: Sized {
|
|||||||
merchant_id: &str,
|
merchant_id: &str,
|
||||||
refund_list_details: &api_models::refunds::TimeRange,
|
refund_list_details: &api_models::refunds::TimeRange,
|
||||||
) -> CustomResult<api_models::refunds::RefundListMetaData, errors::DatabaseError>;
|
) -> CustomResult<api_models::refunds::RefundListMetaData, errors::DatabaseError>;
|
||||||
|
|
||||||
|
async fn get_refunds_count(
|
||||||
|
conn: &PgPooledConn,
|
||||||
|
merchant_id: &str,
|
||||||
|
refund_list_details: &api_models::refunds::RefundListRequest,
|
||||||
|
) -> CustomResult<i64, errors::DatabaseError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@ -53,6 +59,14 @@ impl RefundDbExt for Refund {
|
|||||||
filter = filter.limit(limit).offset(offset);
|
filter = filter.limit(limit).offset(offset);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
match &refund_list_details.refund_id {
|
||||||
|
Some(ref_id) => {
|
||||||
|
filter = filter.filter(dsl::refund_id.eq(ref_id.to_owned()));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
filter = filter.limit(limit).offset(offset);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(time_range) = refund_list_details.time_range {
|
if let Some(time_range) = refund_list_details.time_range {
|
||||||
filter = filter.filter(dsl::created_at.ge(time_range.start_time));
|
filter = filter.filter(dsl::created_at.ge(time_range.start_time));
|
||||||
@ -143,4 +157,52 @@ impl RefundDbExt for Refund {
|
|||||||
|
|
||||||
Ok(meta)
|
Ok(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_refunds_count(
|
||||||
|
conn: &PgPooledConn,
|
||||||
|
merchant_id: &str,
|
||||||
|
refund_list_details: &api_models::refunds::RefundListRequest,
|
||||||
|
) -> CustomResult<i64, errors::DatabaseError> {
|
||||||
|
let mut filter = <Self as HasTable>::table()
|
||||||
|
.count()
|
||||||
|
.filter(dsl::merchant_id.eq(merchant_id.to_owned()))
|
||||||
|
.into_boxed();
|
||||||
|
|
||||||
|
if let Some(pay_id) = &refund_list_details.payment_id {
|
||||||
|
filter = filter.filter(dsl::payment_id.eq(pay_id.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref_id) = &refund_list_details.refund_id {
|
||||||
|
filter = filter.filter(dsl::refund_id.eq(ref_id.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(time_range) = refund_list_details.time_range {
|
||||||
|
filter = filter.filter(dsl::created_at.ge(time_range.start_time));
|
||||||
|
|
||||||
|
if let Some(end_time) = time_range.end_time {
|
||||||
|
filter = filter.filter(dsl::created_at.le(end_time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(connector) = refund_list_details.clone().connector {
|
||||||
|
filter = filter.filter(dsl::connector.eq_any(connector));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(filter_currency) = &refund_list_details.currency {
|
||||||
|
filter = filter.filter(dsl::currency.eq_any(filter_currency.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(filter_refund_status) = &refund_list_details.refund_status {
|
||||||
|
filter = filter.filter(dsl::refund_status.eq_any(filter_refund_status.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger::debug!(query = %diesel::debug_query::<diesel::pg::Pg, _>(&filter).to_string());
|
||||||
|
|
||||||
|
filter
|
||||||
|
.get_result_async::<i64>(conn)
|
||||||
|
.await
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::DatabaseError::NotFound)
|
||||||
|
.attach_printable_lazy(|| "Error filtering count of refunds")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user