feat: list of refunds (#284)

This commit is contained in:
Sangamesh Kulkarni
2023-01-06 14:33:30 +05:30
committed by GitHub
parent 38649130bb
commit e5330528fa
7 changed files with 219 additions and 3 deletions

View File

@ -455,6 +455,40 @@ pub async fn validate_and_create_refund(
Ok(refund.foreign_into())
}
// ********************************************** Refund list **********************************************
/// If payment-id is provided, lists all the refunds associated with that particular payment-id
/// If payment-id is not provided, lists the refunds associated with that particular merchant - to the limit specified,if no limits given, it is 10 by default
#[instrument(skip_all)]
pub async fn refund_list(
db: &dyn db::StorageInterface,
merchant_account: storage::merchant_account::MerchantAccount,
req: api_models::refunds::RefundListRequest,
) -> RouterResponse<api_models::refunds::RefundListResponse> {
let limit = validator::validate_refund_list(req.limit)?;
let refund_list = db
.filter_refund_by_constraints(
&merchant_account.merchant_id,
&req,
merchant_account.storage_scheme,
limit,
)
.await
.change_context(errors::ApiErrorResponse::RefundNotFound)?;
let data: Vec<refunds::RefundResponse> = refund_list
.into_iter()
.map(ForeignInto::foreign_into)
.collect();
utils::when(data.is_empty(), || {
Err(errors::ApiErrorResponse::RefundNotFound)
})?;
Ok(services::BachResponse::Json(
api_models::refunds::RefundListResponse { data },
))
}
// ********************************************** UTILS **********************************************
// FIXME: function should not have more than 3 arguments.

View File

@ -122,3 +122,19 @@ pub async fn validate_uniqueness_of_refund_id_against_merchant_id(
}
}
}
pub fn validate_refund_list(limit: Option<i64>) -> CustomResult<i64, errors::ApiErrorResponse> {
match limit {
Some(limit_val) => {
if !(1..=100).contains(&limit_val) {
Err(errors::ApiErrorResponse::InvalidRequestData {
message: "limit should be in between 1 and 100".to_string(),
}
.into())
} else {
Ok(limit_val)
}
}
None => Ok(10),
}
}

View File

@ -55,6 +55,14 @@ pub trait RefundInterface {
new: storage_types::RefundNew,
storage_scheme: enums::MerchantStorageScheme,
) -> CustomResult<storage_types::Refund, errors::StorageError>;
async fn filter_refund_by_constraints(
&self,
merchant_id: &str,
refund_details: &api_models::refunds::RefundListRequest,
storage_scheme: enums::MerchantStorageScheme,
limit: i64,
) -> CustomResult<Vec<storage_models::refund::Refund>, errors::StorageError>;
}
#[cfg(not(feature = "kv_store"))]
@ -163,6 +171,25 @@ mod storage {
.map_err(Into::into)
.into_report()
}
async fn filter_refund_by_constraints(
&self,
merchant_id: &str,
refund_details: &api_models::refunds::RefundListRequest,
_storage_scheme: enums::MerchantStorageScheme,
limit: i64,
) -> CustomResult<Vec<storage_models::refund::Refund>, errors::StorageError> {
let conn = pg_connection(&self.master_pool).await;
<storage_models::refund::Refund as storage_types::RefundDbExt>::filter_by_constraints(
&conn,
merchant_id,
refund_details,
limit,
)
.await
.map_err(Into::into)
.into_report()
}
}
}
@ -546,6 +573,26 @@ mod storage {
}
}
}
async fn filter_refund_by_constraints(
&self,
merchant_id: &str,
refund_details: &api_models::refunds::RefundListRequest,
storage_scheme: enums::MerchantStorageScheme,
limit: i64,
) -> CustomResult<Vec<storage_models::refund::Refund>, errors::StorageError> {
match storage_scheme {
enums::MerchantStorageScheme::PostgresOnly => {
let conn = pg_connection(&self.master_pool).await;
<storage_models::refund::Refund as storage_types::RefundDbExt>::filter_by_constraints(&conn, merchant_id, refund_details, limit)
.await
.map_err(Into::into)
.into_report()
}
enums::MerchantStorageScheme::RedisKv => Err(errors::StorageError::KVError.into()),
}
}
}
}
@ -651,4 +698,15 @@ impl RefundInterface for MockDb {
// [#172]: Implement function for `MockDb`
Err(errors::StorageError::MockDbError)?
}
async fn filter_refund_by_constraints(
&self,
_merchant_id: &str,
_refund_details: &api_models::refunds::RefundListRequest,
_storage_scheme: enums::MerchantStorageScheme,
_limit: i64,
) -> CustomResult<Vec<storage_models::refund::Refund>, errors::StorageError> {
// [#172]: Implement function for `MockDb`
Err(errors::StorageError::MockDbError)?
}
}

View File

@ -113,12 +113,12 @@ impl Refunds {
web::scope("/refunds")
.app_data(web::Data::new(state))
.service(web::resource("").route(web::post().to(refunds_create)))
.service(web::resource("/list").route(web::get().to(refunds_list)))
.service(
web::resource("/{id}")
.route(web::get().to(refunds_retrieve))
.route(web::post().to(refunds_update)),
)
.service(web::resource("/list").route(web::get().to(refunds_list)))
}
}

View File

@ -68,6 +68,17 @@ pub async fn refunds_update(
#[instrument(skip_all, fields(flow = ?Flow::RefundsList))]
// #[get("/list")]
pub async fn refunds_list() -> HttpResponse {
api::http_response_json("list")
pub async fn refunds_list(
state: web::Data<AppState>,
req: HttpRequest,
payload: web::Query<api_models::refunds::RefundListRequest>,
) -> HttpResponse {
api::server_wrap(
&state,
&req,
payload.into_inner(),
|state, merchant_account, req| refund_list(&*state.store, merchant_account, req),
api::MerchantAuthentication::ApiKey,
)
.await
}

View File

@ -1,6 +1,72 @@
use async_bb8_diesel::AsyncRunQueryDsl;
use common_utils::errors::CustomResult;
use diesel::{associations::HasTable, ExpressionMethods, QueryDsl};
use error_stack::{IntoReport, ResultExt};
pub use storage_models::refund::{
Refund, RefundCoreWorkflow, RefundNew, RefundUpdate, RefundUpdateInternal,
};
use storage_models::{errors, schema::refund::dsl};
use crate::{connection::PgPooledConn, logger};
#[cfg(feature = "kv_store")]
impl crate::utils::storage_partitioning::KvStorePartition for Refund {}
#[async_trait::async_trait]
pub trait RefundDbExt: Sized {
async fn filter_by_constraints(
conn: &PgPooledConn,
merchant_id: &str,
refund_list_details: &api_models::refunds::RefundListRequest,
limit: i64,
) -> CustomResult<Vec<Self>, errors::DatabaseError>;
}
#[async_trait::async_trait]
impl RefundDbExt for Refund {
async fn filter_by_constraints(
conn: &PgPooledConn,
merchant_id: &str,
refund_list_details: &api_models::refunds::RefundListRequest,
limit: i64,
) -> CustomResult<Vec<Self>, errors::DatabaseError> {
let mut filter = <Self as HasTable>::table()
.filter(dsl::merchant_id.eq(merchant_id.to_owned()))
.order_by(dsl::id)
.into_boxed();
match &refund_list_details.payment_id {
Some(pid) => {
filter = filter.filter(dsl::payment_id.eq(pid.to_owned()));
}
None => {
filter = filter.limit(limit);
}
};
if let Some(created) = refund_list_details.created {
filter = filter.filter(dsl::created_at.eq(created));
}
if let Some(created_lt) = refund_list_details.created_lt {
filter = filter.filter(dsl::created_at.lt(created_lt));
}
if let Some(created_gt) = refund_list_details.created_gt {
filter = filter.filter(dsl::created_at.gt(created_gt));
}
if let Some(created_lte) = refund_list_details.created_lte {
filter = filter.filter(dsl::created_at.le(created_lte));
}
if let Some(created_gte) = refund_list_details.created_gte {
filter = filter.filter(dsl::created_at.gt(created_gte));
}
logger::debug!(query = %diesel::debug_query::<diesel::pg::Pg, _>(&filter).to_string());
filter
.get_results_async(conn)
.await
.into_report()
.change_context(errors::DatabaseError::NotFound)
.attach_printable_lazy(|| "Error filtering records by predicate")
}
}