mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(webhook): Return events list and total_count on list initial delivery attempt call (#7243)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
use common_utils::{self, fp_utils};
|
||||
use error_stack::ResultExt;
|
||||
use masking::PeekInterface;
|
||||
use router_env::{instrument, tracing};
|
||||
@ -11,6 +12,7 @@ use crate::{
|
||||
};
|
||||
|
||||
const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT: i64 = 100;
|
||||
const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS: i64 = 90;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MerchantAccountOrProfile {
|
||||
@ -22,16 +24,21 @@ enum MerchantAccountOrProfile {
|
||||
pub async fn list_initial_delivery_attempts(
|
||||
state: SessionState,
|
||||
merchant_id: common_utils::id_type::MerchantId,
|
||||
constraints: api::webhook_events::EventListConstraints,
|
||||
) -> RouterResponse<Vec<api::webhook_events::EventListItemResponse>> {
|
||||
let profile_id = constraints.profile_id.clone();
|
||||
let constraints =
|
||||
api::webhook_events::EventListConstraintsInternal::foreign_try_from(constraints)?;
|
||||
api_constraints: api::webhook_events::EventListConstraints,
|
||||
) -> RouterResponse<api::webhook_events::TotalEventsResponse> {
|
||||
let profile_id = api_constraints.profile_id.clone();
|
||||
let constraints = api::webhook_events::EventListConstraintsInternal::foreign_try_from(
|
||||
api_constraints.clone(),
|
||||
)?;
|
||||
|
||||
let store = state.store.as_ref();
|
||||
let key_manager_state = &(&state).into();
|
||||
let (account, key_store) =
|
||||
get_account_and_key_store(state.clone(), merchant_id, profile_id).await?;
|
||||
get_account_and_key_store(state.clone(), merchant_id.clone(), profile_id.clone()).await?;
|
||||
|
||||
let now = common_utils::date_time::now();
|
||||
let events_list_begin_time =
|
||||
(now.date() - time::Duration::days(INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS)).midnight();
|
||||
|
||||
let events = match constraints {
|
||||
api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => {
|
||||
@ -72,6 +79,33 @@ pub async fn list_initial_delivery_attempts(
|
||||
_ => None,
|
||||
};
|
||||
|
||||
fp_utils::when(!created_after.zip(created_before).map(|(created_after,created_before)| created_after<=created_before).unwrap_or(true), || {
|
||||
Err(errors::ApiErrorResponse::InvalidRequestData { message: "The `created_after` timestamp must be an earlier timestamp compared to the `created_before` timestamp".to_string() })
|
||||
})?;
|
||||
|
||||
let created_after = match created_after {
|
||||
Some(created_after) => {
|
||||
if created_after < events_list_begin_time {
|
||||
Err(errors::ApiErrorResponse::InvalidRequestData { message: format!("`created_after` must be a timestamp within the past {INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS} days.") })
|
||||
}else{
|
||||
Ok(created_after)
|
||||
}
|
||||
},
|
||||
None => Ok(events_list_begin_time)
|
||||
}?;
|
||||
|
||||
let created_before = match created_before{
|
||||
Some(created_before) => {
|
||||
if created_before < events_list_begin_time{
|
||||
Err(errors::ApiErrorResponse::InvalidRequestData { message: format!("`created_before` must be a timestamp within the past {INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS} days.") })
|
||||
}
|
||||
else{
|
||||
Ok(created_before)
|
||||
}
|
||||
},
|
||||
None => Ok(now)
|
||||
}?;
|
||||
|
||||
match account {
|
||||
MerchantAccountOrProfile::MerchantAccount(merchant_account) => store
|
||||
.list_initial_events_by_merchant_id_constraints(key_manager_state,
|
||||
@ -99,11 +133,29 @@ pub async fn list_initial_delivery_attempts(
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to list events with specified constraints")?;
|
||||
|
||||
let events = events
|
||||
.into_iter()
|
||||
.map(api::webhook_events::EventListItemResponse::try_from)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let created_after = api_constraints
|
||||
.created_after
|
||||
.unwrap_or(events_list_begin_time);
|
||||
let created_before = api_constraints.created_before.unwrap_or(now);
|
||||
|
||||
let total_count = store
|
||||
.count_initial_events_by_constraints(
|
||||
&merchant_id,
|
||||
profile_id,
|
||||
created_after,
|
||||
created_before,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to get total events count")?;
|
||||
|
||||
Ok(ApplicationResponse::Json(
|
||||
events
|
||||
.into_iter()
|
||||
.map(api::webhook_events::EventListItemResponse::try_from)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
api::webhook_events::TotalEventsResponse::new(total_count, events),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@ -49,8 +49,8 @@ where
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
created_after: Option<time::PrimitiveDateTime>,
|
||||
created_before: Option<time::PrimitiveDateTime>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
@ -77,8 +77,8 @@ where
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
profile_id: &common_utils::id_type::ProfileId,
|
||||
created_after: Option<time::PrimitiveDateTime>,
|
||||
created_before: Option<time::PrimitiveDateTime>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
@ -92,6 +92,14 @@ where
|
||||
event: domain::EventUpdate,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
) -> CustomResult<domain::Event, errors::StorageError>;
|
||||
|
||||
async fn count_initial_events_by_constraints(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
profile_id: Option<common_utils::id_type::ProfileId>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
) -> CustomResult<i64, errors::StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -181,8 +189,8 @@ impl EventInterface for Store {
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
created_after: Option<time::PrimitiveDateTime>,
|
||||
created_before: Option<time::PrimitiveDateTime>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
@ -292,8 +300,8 @@ impl EventInterface for Store {
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
profile_id: &common_utils::id_type::ProfileId,
|
||||
created_after: Option<time::PrimitiveDateTime>,
|
||||
created_before: Option<time::PrimitiveDateTime>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
@ -351,6 +359,25 @@ impl EventInterface for Store {
|
||||
.await
|
||||
.change_context(errors::StorageError::DecryptionError)
|
||||
}
|
||||
|
||||
async fn count_initial_events_by_constraints(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
profile_id: Option<common_utils::id_type::ProfileId>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
) -> CustomResult<i64, errors::StorageError> {
|
||||
let conn = connection::pg_connection_read(self).await?;
|
||||
storage::Event::count_initial_attempts_by_constraints(
|
||||
&conn,
|
||||
merchant_id,
|
||||
profile_id,
|
||||
created_after,
|
||||
created_before,
|
||||
)
|
||||
.await
|
||||
.map_err(|error| report!(errors::StorageError::from(error)))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@ -452,28 +479,21 @@ impl EventInterface for MockDb {
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
created_after: Option<time::PrimitiveDateTime>,
|
||||
created_before: Option<time::PrimitiveDateTime>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
) -> CustomResult<Vec<domain::Event>, errors::StorageError> {
|
||||
let locked_events = self.events.lock().await;
|
||||
let events_iter = locked_events.iter().filter(|event| {
|
||||
let mut check = event.merchant_id == Some(merchant_id.to_owned())
|
||||
&& event.initial_attempt_id.as_ref() == Some(&event.event_id);
|
||||
|
||||
if let Some(created_after) = created_after {
|
||||
check = check && (event.created_at >= created_after);
|
||||
}
|
||||
|
||||
if let Some(created_before) = created_before {
|
||||
check = check && (event.created_at <= created_before);
|
||||
}
|
||||
let check = event.merchant_id == Some(merchant_id.to_owned())
|
||||
&& event.initial_attempt_id.as_ref() == Some(&event.event_id)
|
||||
&& (event.created_at >= created_after)
|
||||
&& (event.created_at <= created_before);
|
||||
|
||||
check
|
||||
});
|
||||
|
||||
let offset: usize = if let Some(offset) = offset {
|
||||
if offset < 0 {
|
||||
Err(errors::StorageError::MockDbError)?;
|
||||
@ -590,24 +610,18 @@ impl EventInterface for MockDb {
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
profile_id: &common_utils::id_type::ProfileId,
|
||||
created_after: Option<time::PrimitiveDateTime>,
|
||||
created_before: Option<time::PrimitiveDateTime>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
) -> CustomResult<Vec<domain::Event>, errors::StorageError> {
|
||||
let locked_events = self.events.lock().await;
|
||||
let events_iter = locked_events.iter().filter(|event| {
|
||||
let mut check = event.business_profile_id == Some(profile_id.to_owned())
|
||||
&& event.initial_attempt_id.as_ref() == Some(&event.event_id);
|
||||
|
||||
if let Some(created_after) = created_after {
|
||||
check = check && (event.created_at >= created_after);
|
||||
}
|
||||
|
||||
if let Some(created_before) = created_before {
|
||||
check = check && (event.created_at <= created_before);
|
||||
}
|
||||
let check = event.business_profile_id == Some(profile_id.to_owned())
|
||||
&& event.initial_attempt_id.as_ref() == Some(&event.event_id)
|
||||
&& (event.created_at >= created_after)
|
||||
&& (event.created_at <= created_before);
|
||||
|
||||
check
|
||||
});
|
||||
@ -692,6 +706,32 @@ impl EventInterface for MockDb {
|
||||
.await
|
||||
.change_context(errors::StorageError::DecryptionError)
|
||||
}
|
||||
|
||||
async fn count_initial_events_by_constraints(
|
||||
&self,
|
||||
merchant_id: &common_utils::id_type::MerchantId,
|
||||
profile_id: Option<common_utils::id_type::ProfileId>,
|
||||
created_after: time::PrimitiveDateTime,
|
||||
created_before: time::PrimitiveDateTime,
|
||||
) -> CustomResult<i64, errors::StorageError> {
|
||||
let locked_events = self.events.lock().await;
|
||||
|
||||
let iter_events = locked_events.iter().filter(|event| {
|
||||
let check = event.initial_attempt_id.as_ref() == Some(&event.event_id)
|
||||
&& (event.merchant_id == Some(merchant_id.to_owned()))
|
||||
&& (event.business_profile_id == profile_id)
|
||||
&& (event.created_at >= created_after)
|
||||
&& (event.created_at <= created_before);
|
||||
|
||||
check
|
||||
});
|
||||
|
||||
let events = iter_events.cloned().collect::<Vec<_>>();
|
||||
|
||||
i64::try_from(events.len())
|
||||
.change_context(errors::StorageError::MockDbError)
|
||||
.attach_printable("Failed to convert usize to i64")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -768,8 +768,8 @@ impl EventInterface for KafkaStore {
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
created_after: Option<PrimitiveDateTime>,
|
||||
created_before: Option<PrimitiveDateTime>,
|
||||
created_after: PrimitiveDateTime,
|
||||
created_before: PrimitiveDateTime,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
@ -825,8 +825,8 @@ impl EventInterface for KafkaStore {
|
||||
&self,
|
||||
state: &KeyManagerState,
|
||||
profile_id: &id_type::ProfileId,
|
||||
created_after: Option<PrimitiveDateTime>,
|
||||
created_before: Option<PrimitiveDateTime>,
|
||||
created_after: PrimitiveDateTime,
|
||||
created_before: PrimitiveDateTime,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
merchant_key_store: &domain::MerchantKeyStore,
|
||||
@ -862,6 +862,23 @@ impl EventInterface for KafkaStore {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn count_initial_events_by_constraints(
|
||||
&self,
|
||||
merchant_id: &id_type::MerchantId,
|
||||
profile_id: Option<id_type::ProfileId>,
|
||||
created_after: PrimitiveDateTime,
|
||||
created_before: PrimitiveDateTime,
|
||||
) -> CustomResult<i64, errors::StorageError> {
|
||||
self.diesel_store
|
||||
.count_initial_events_by_constraints(
|
||||
merchant_id,
|
||||
profile_id,
|
||||
created_after,
|
||||
created_before,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
pub use api_models::webhook_events::{
|
||||
EventListConstraints, EventListConstraintsInternal, EventListItemResponse,
|
||||
EventListRequestInternal, EventRetrieveResponse, OutgoingWebhookRequestContent,
|
||||
OutgoingWebhookResponseContent, WebhookDeliveryAttemptListRequestInternal,
|
||||
OutgoingWebhookResponseContent, TotalEventsResponse, WebhookDeliveryAttemptListRequestInternal,
|
||||
WebhookDeliveryRetryRequestInternal,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user