refactor(webhook_events): allow listing unique webhook events based on profile ID (#5598)

This commit is contained in:
Sanchith Hegde
2024-08-14 14:56:18 +05:30
committed by GitHub
parent 67d580c0eb
commit 8bcda2cea4
15 changed files with 233 additions and 298 deletions

View File

@ -15,23 +15,23 @@ const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT: i64 = 100;
#[derive(Debug)]
enum MerchantAccountOrBusinessProfile {
MerchantAccount(domain::MerchantAccount),
#[allow(dead_code)]
BusinessProfile(domain::BusinessProfile),
}
#[instrument(skip(state))]
pub async fn list_initial_delivery_attempts(
state: SessionState,
merchant_id_or_profile_id: String,
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)?;
let store = state.store.as_ref();
let key_manager_state = &(&state).into();
let (account, key_store) =
determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?;
get_account_and_key_store(state.clone(), merchant_id, profile_id).await?;
let events = match constraints {
api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => {
@ -110,38 +110,31 @@ pub async fn list_initial_delivery_attempts(
#[instrument(skip(state))]
pub async fn list_delivery_attempts(
state: SessionState,
merchant_id_or_profile_id: String,
merchant_id: common_utils::id_type::MerchantId,
initial_attempt_id: String,
) -> RouterResponse<Vec<api::webhook_events::EventRetrieveResponse>> {
let store = state.store.as_ref();
let (account, key_store) =
determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?;
let key_manager_state = &(&state).into();
let events = match account {
MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => {
store
.list_events_by_merchant_id_initial_attempt_id(
key_manager_state,
merchant_account.get_id(),
&initial_attempt_id,
&key_store,
)
.await
}
MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => {
store
.list_events_by_profile_id_initial_attempt_id(
key_manager_state,
&business_profile.profile_id,
&initial_attempt_id,
&key_store,
)
.await
}
}
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to list delivery attempts for initial event")?;
let key_store = store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&merchant_id,
&store.get_master_key().to_vec().into(),
)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
let events = store
.list_events_by_merchant_id_initial_attempt_id(
key_manager_state,
&merchant_id,
&initial_attempt_id,
&key_store,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to list delivery attempts for initial event")?;
if events.is_empty() {
Err(error_stack::report!(
@ -161,13 +154,20 @@ pub async fn list_delivery_attempts(
#[instrument(skip(state))]
pub async fn retry_delivery_attempt(
state: SessionState,
merchant_id_or_profile_id: String,
merchant_id: common_utils::id_type::MerchantId,
event_id: String,
) -> RouterResponse<api::webhook_events::EventRetrieveResponse> {
let store = state.store.as_ref();
let key_manager_state = &(&state).into();
let (account, key_store) =
determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?;
let key_store = store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&merchant_id,
&store.get_master_key().to_vec().into(),
)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
let event_to_retry = store
.find_event_by_merchant_id_event_id(
@ -179,25 +179,16 @@ pub async fn retry_delivery_attempt(
.await
.to_not_found_response(errors::ApiErrorResponse::EventNotFound)?;
let business_profile = match account {
MerchantAccountOrBusinessProfile::MerchantAccount(_) => {
let business_profile_id = event_to_retry
.business_profile_id
.get_required_value("business_profile_id")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to read business profile ID from event to retry")?;
store
.find_business_profile_by_profile_id(
key_manager_state,
&key_store,
&business_profile_id,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to find business profile")
}
MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => Ok(business_profile),
}?;
let business_profile_id = event_to_retry
.business_profile_id
.get_required_value("business_profile_id")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to read business profile ID from event to retry")?;
let business_profile = store
.find_business_profile_by_profile_id(key_manager_state, &key_store, &business_profile_id)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to find business profile")?;
let delivery_attempt = storage::enums::WebhookDeliveryAttempt::ManualRetry;
let new_event_id = super::utils::generate_event_id();
@ -271,77 +262,65 @@ pub async fn retry_delivery_attempt(
))
}
async fn determine_identifier_and_get_key_store(
async fn get_account_and_key_store(
state: SessionState,
merchant_id_or_profile_id: String,
merchant_id: common_utils::id_type::MerchantId,
profile_id: Option<String>,
) -> errors::RouterResult<(MerchantAccountOrBusinessProfile, domain::MerchantKeyStore)> {
let store = state.store.as_ref();
let key_manager_state = &(&state).into();
let merchant_id = common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from(
merchant_id_or_profile_id.clone(),
))
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "merchant_id",
})?;
match store
let merchant_key_store = store
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&merchant_id,
&store.get_master_key().to_vec().into(),
)
.await
{
// Since a merchant key store was found with `merchant_id` = `merchant_id_or_profile_id`,
// `merchant_id_or_profile_id` is a valid merchant ID.
// Find a merchant account having `merchant_id` = `merchant_id_or_profile_id`.
Ok(key_store) => {
let merchant_account = store
.find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store)
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
match profile_id {
// If profile ID is specified, return business profile, since a business profile is more
// specific than a merchant account.
Some(profile_id) => {
let business_profile = store
.find_business_profile_by_merchant_id_profile_id(
key_manager_state,
&merchant_key_store,
&merchant_id,
&profile_id,
)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
.attach_printable_lazy(|| {
format!(
"Failed to find business profile by merchant_id `{merchant_id:?}` and profile_id `{profile_id}`. \
The merchant_id associated with the business profile `{profile_id}` may be \
different than the merchant_id specified (`{merchant_id:?}`)."
)
})
.to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound {
id: profile_id,
})?;
Ok((
MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account),
key_store,
MerchantAccountOrBusinessProfile::BusinessProfile(business_profile),
merchant_key_store,
))
}
/*
// Since no merchant key store was found with `merchant_id` = `merchant_id_or_profile_id`,
// `merchant_id_or_profile_id` is not a valid merchant ID.
// Assuming that `merchant_id_or_profile_id` is a business profile ID, try to find a
// business profile having `profile_id` = `merchant_id_or_profile_id`.
Err(error) if error.current_context().is_db_not_found() => {
router_env::logger::debug!(
?error,
%merchant_id_or_profile_id,
"Failed to find merchant key store for the specified merchant ID or business profile ID"
);
let business_profile = store
.find_business_profile_by_profile_id(&merchant_id_or_profile_id)
.await
.to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound {
id: merchant_id_or_profile_id,
})?;
let key_store = store
.get_merchant_key_store_by_merchant_id(
None => {
let merchant_account = store
.find_merchant_account_by_merchant_id(
key_manager_state,
&business_profile.merchant_id,
&store.get_master_key().to_vec().into(),
&merchant_id,
&merchant_key_store,
)
.await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
Ok((
MerchantAccountOrBusinessProfile::BusinessProfile(business_profile),
key_store,
MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account),
merchant_key_store,
))
}
*/
Err(error) => Err(error)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to find merchant key store by merchant ID"),
}
}