WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3998895
refactor(webhook): event filtering to support separate query paths fo…
VenuMadhav2541 Nov 23, 2025
078db2d
chore: run formatter
hyperswitch-bot[bot] Nov 23, 2025
220c75b
fix(webhooks): replace unreachable!() with explicit error for clippy …
VenuMadhav2541 Nov 23, 2025
23b39d2
fix(webhooks): replace unreachable!() with explicit error for clippy …
VenuMadhav2541 Nov 23, 2025
0bb4043
refactor: Enhance webhook_events core logic for targeted event searches
VenuMadhav2541 Nov 25, 2025
d883c5e
chore: run formatter
hyperswitch-bot[bot] Nov 25, 2025
16910a0
fix(webhooks): improve error message clarity for event list constraints
VenuMadhav2541 Nov 25, 2025
4a52e7b
Merge branch 'webhooks_selective_search' of https://github.com/juspay…
VenuMadhav2541 Nov 25, 2025
6b9bfbe
refactor(webhooks): enhance event query tests and add clippy allowance
VenuMadhav2541 Nov 25, 2025
a34f2fe
refactor(webhooks): change event lookup from list to single find oper…
VenuMadhav2541 Nov 28, 2025
b461072
refactor(webhooks): add event_id = initial_attempt_id constraint to f…
VenuMadhav2541 Dec 1, 2025
36e4ad3
fix(webhooks): ensure event queries return only initial event, not re…
VenuMadhav2541 Dec 1, 2025
2b446fd
Merge branch 'main' of https://github.com/juspay/hyperswitch into web…
VenuMadhav2541 Dec 2, 2025
e109fe7
refactor(webhooks): improve event processing with concurrent executio…
VenuMadhav2541 Dec 5, 2025
8624c5d
chore: run formatter
hyperswitch-bot[bot] Dec 5, 2025
b2b1f29
refactor(diesel_models): consolidate duplicate imports in events module
VenuMadhav2541 Dec 5, 2025
a7aa0ef
chore: run formatter
hyperswitch-bot[bot] Dec 5, 2025
f07e8e1
refactor(router): simplify event conversion by removing intermediate …
VenuMadhav2541 Dec 8, 2025
d137169
Merge branch 'webhooks_selective_search' of https://github.com/juspay…
VenuMadhav2541 Dec 8, 2025
e3ff628
chore: run formatter
hyperswitch-bot[bot] Dec 8, 2025
7700a55
refactor(router): remove redundant async blocks in event handlers
VenuMadhav2541 Dec 8, 2025
db0787f
Merge branch 'webhooks_selective_search' of https://github.com/juspay…
VenuMadhav2541 Dec 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/api_models/src/webhook_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pub enum EventListConstraintsInternal {
},
ObjectIdFilter {
object_id: String,
},
EventIdFilter {
event_id: String,
},
}
Expand Down
72 changes: 58 additions & 14 deletions crates/diesel_models/src/query/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,52 @@ impl Event {
.await
}

pub async fn list_initial_attempts_by_merchant_id_primary_object_id_or_initial_attempt_id(
pub async fn list_initial_attempts_by_merchant_id_primary_object_id(
conn: &PgPooledConn,
merchant_id: &common_utils::id_type::MerchantId,
primary_object_id: &str,
initial_attempt_id: &str,
) -> StorageResult<Vec<Self>> {
generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
conn,
dsl::event_id
.nullable()
.eq(dsl::initial_attempt_id) // Filter initial attempts only
.and(dsl::merchant_id.eq(merchant_id.to_owned()))
.and(
dsl::primary_object_id
.eq(primary_object_id.to_owned())
.or(dsl::initial_attempt_id.eq(initial_attempt_id.to_owned())),
),
.and(dsl::primary_object_id.eq(primary_object_id.to_owned())),
None,
None,
Some(dsl::created_at.desc()),
)
.await
}

pub async fn find_initial_attempt_by_merchant_id_initial_attempt_id(
conn: &PgPooledConn,
merchant_id: &common_utils::id_type::MerchantId,
initial_attempt_id: &str,
) -> StorageResult<Option<Self>> {
use diesel::{BoolExpressionMethods, ExpressionMethods};
use error_stack::ResultExt;

use super::generics;
use crate::errors::DatabaseError;

let predicate = dsl::merchant_id
.eq(merchant_id.to_owned())
.and(dsl::event_id.eq(initial_attempt_id.to_owned()));

let result =
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(conn, predicate).await;

match result {
Ok(event) => Ok(Some(event)),
Err(err) => match err.current_context() {
DatabaseError::NotFound => Ok(None),
_ => Err(err).attach_printable("Error finding event by initial_attempt_id"),
},
}
}

#[allow(clippy::too_many_arguments)]
pub async fn list_initial_attempts_by_merchant_id_constraints(
conn: &PgPooledConn,
Expand Down Expand Up @@ -134,30 +156,52 @@ impl Event {
.await
}

pub async fn list_initial_attempts_by_profile_id_primary_object_id_or_initial_attempt_id(
pub async fn list_initial_attempts_by_profile_id_primary_object_id(
conn: &PgPooledConn,
profile_id: &common_utils::id_type::ProfileId,
primary_object_id: &str,
initial_attempt_id: &str,
) -> StorageResult<Vec<Self>> {
generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
conn,
dsl::event_id
.nullable()
.eq(dsl::initial_attempt_id) // Filter initial attempts only
.and(dsl::business_profile_id.eq(profile_id.to_owned()))
.and(
dsl::primary_object_id
.eq(primary_object_id.to_owned())
.or(dsl::initial_attempt_id.eq(initial_attempt_id.to_owned())),
),
.and(dsl::primary_object_id.eq(primary_object_id.to_owned())),
None,
None,
Some(dsl::created_at.desc()),
)
.await
}

pub async fn find_initial_attempt_by_profile_id_initial_attempt_id(
conn: &PgPooledConn,
profile_id: &common_utils::id_type::ProfileId,
initial_attempt_id: &str,
) -> StorageResult<Option<Self>> {
use diesel::{BoolExpressionMethods, ExpressionMethods};
use error_stack::ResultExt;

use super::generics;
use crate::errors::DatabaseError;

let predicate = dsl::business_profile_id
.eq(profile_id.to_owned())
.and(dsl::event_id.eq(initial_attempt_id.to_owned()));

let result =
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(conn, predicate).await;

match result {
Ok(event) => Ok(Some(event)),
Err(err) => match err.current_context() {
DatabaseError::NotFound => Ok(None),
_ => Err(err).attach_printable("Error finding event by initial_attempt_id"),
},
}
}

#[allow(clippy::too_many_arguments)]
pub async fn list_initial_attempts_by_profile_id_constraints(
conn: &PgPooledConn,
Expand Down
72 changes: 49 additions & 23 deletions crates/router/src/core/webhooks/webhook_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,39 +42,65 @@ pub async fn list_initial_delivery_attempts(
(now.date() - time::Duration::days(INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_DAYS)).midnight();

let (events, total_count) = match constraints {
api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter {
object_id,
event_id,
} => {
let events =
match account {
MerchantAccountOrProfile::MerchantAccount(merchant_account) => store
.list_initial_events_by_merchant_id_primary_object_or_initial_attempt_id(
api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => {
let events = match account {
MerchantAccountOrProfile::MerchantAccount(merchant_account) => {
store
.list_initial_events_by_merchant_id_primary_object_id(
merchant_account.get_id(),
&object_id,
&event_id,
object_id.as_str(),
&key_store,
)
.await,
MerchantAccountOrProfile::Profile(business_profile) => {
store
.list_initial_events_by_profile_id_primary_object_or_initial_attempt_id(
business_profile.get_id(),
&object_id,
&event_id,
&key_store,
)
.await
}
.await
}
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to list events with specified constraints")?;
MerchantAccountOrProfile::Profile(business_profile) => {
store
.list_initial_events_by_profile_id_primary_object_id(
business_profile.get_id(),
object_id.as_str(),
&key_store,
)
.await
}
}
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to list events with specified constraints")?;

let total_count = i64::try_from(events.len())
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Error while converting from usize to i64")?;
(events, total_count)
}
api_models::webhook_events::EventListConstraintsInternal::EventIdFilter { event_id } => {
let event_opt = match account {
MerchantAccountOrProfile::MerchantAccount(merchant_account) => {
store
.find_initial_event_by_merchant_id_initial_attempt_id(
merchant_account.get_id(),
event_id.as_str(),
&key_store,
)
.await
}
MerchantAccountOrProfile::Profile(business_profile) => {
store
.find_initial_event_by_profile_id_initial_attempt_id(
business_profile.get_id(),
event_id.as_str(),
&key_store,
)
.await
}
}
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to find event with specified event_id")?;

let (events, total_count) = match event_opt {
Some(event) => (vec![event], 1),
None => (vec![], 0),
};
(events, total_count)
}
api_models::webhook_events::EventListConstraintsInternal::GenericFilter {
created_after,
created_before,
Expand Down
Loading
Loading