mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 01:57:45 +08:00 
			
		
		
		
	feat(webhook): Add is_delivered filter to list initial attempts endpoint (#7344)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
		| @ -9566,7 +9566,6 @@ | |||||||
|           "object_id", |           "object_id", | ||||||
|           "event_type", |           "event_type", | ||||||
|           "event_class", |           "event_class", | ||||||
|           "is_delivery_successful", |  | ||||||
|           "initial_attempt_id", |           "initial_attempt_id", | ||||||
|           "created" |           "created" | ||||||
|         ], |         ], | ||||||
| @ -9603,7 +9602,8 @@ | |||||||
|           }, |           }, | ||||||
|           "is_delivery_successful": { |           "is_delivery_successful": { | ||||||
|             "type": "boolean", |             "type": "boolean", | ||||||
|             "description": "Indicates whether the webhook delivery attempt was successful." |             "description": "Indicates whether the webhook was ultimately delivered or not.", | ||||||
|  |             "nullable": true | ||||||
|           }, |           }, | ||||||
|           "initial_attempt_id": { |           "initial_attempt_id": { | ||||||
|             "type": "string", |             "type": "string", | ||||||
|  | |||||||
| @ -5387,6 +5387,16 @@ | |||||||
|               "type": "string", |               "type": "string", | ||||||
|               "nullable": true |               "nullable": true | ||||||
|             } |             } | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "name": "is_delivered", | ||||||
|  |             "in": "query", | ||||||
|  |             "description": "Only include Events which are ultimately delivered to the merchant.", | ||||||
|  |             "required": false, | ||||||
|  |             "schema": { | ||||||
|  |               "type": "boolean", | ||||||
|  |               "nullable": true | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|         ], |         ], | ||||||
|         "responses": { |         "responses": { | ||||||
| @ -11751,7 +11761,6 @@ | |||||||
|           "object_id", |           "object_id", | ||||||
|           "event_type", |           "event_type", | ||||||
|           "event_class", |           "event_class", | ||||||
|           "is_delivery_successful", |  | ||||||
|           "initial_attempt_id", |           "initial_attempt_id", | ||||||
|           "created" |           "created" | ||||||
|         ], |         ], | ||||||
| @ -11788,7 +11797,8 @@ | |||||||
|           }, |           }, | ||||||
|           "is_delivery_successful": { |           "is_delivery_successful": { | ||||||
|             "type": "boolean", |             "type": "boolean", | ||||||
|             "description": "Indicates whether the webhook delivery attempt was successful." |             "description": "Indicates whether the webhook was ultimately delivered or not.", | ||||||
|  |             "nullable": true | ||||||
|           }, |           }, | ||||||
|           "initial_attempt_id": { |           "initial_attempt_id": { | ||||||
|             "type": "string", |             "type": "string", | ||||||
|  | |||||||
| @ -28,6 +28,9 @@ pub struct EventListConstraints { | |||||||
|     /// Filter all events associated with the specified business profile ID. |     /// Filter all events associated with the specified business profile ID. | ||||||
|     #[schema(value_type = Option<String>)] |     #[schema(value_type = Option<String>)] | ||||||
|     pub profile_id: Option<common_utils::id_type::ProfileId>, |     pub profile_id: Option<common_utils::id_type::ProfileId>, | ||||||
|  |  | ||||||
|  |     /// Filter all events by `is_overall_delivery_successful` field of the event. | ||||||
|  |     pub is_delivered: Option<bool>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @ -37,6 +40,7 @@ pub enum EventListConstraintsInternal { | |||||||
|         created_before: Option<PrimitiveDateTime>, |         created_before: Option<PrimitiveDateTime>, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     }, |     }, | ||||||
|     ObjectIdFilter { |     ObjectIdFilter { | ||||||
|         object_id: String, |         object_id: String, | ||||||
| @ -68,8 +72,8 @@ pub struct EventListItemResponse { | |||||||
|     /// Specifies the class of event (the type of object: Payment, Refund, etc.) |     /// Specifies the class of event (the type of object: Payment, Refund, etc.) | ||||||
|     pub event_class: EventClass, |     pub event_class: EventClass, | ||||||
|  |  | ||||||
|     /// Indicates whether the webhook delivery attempt was successful. |     /// Indicates whether the webhook was ultimately delivered or not. | ||||||
|     pub is_delivery_successful: bool, |     pub is_delivery_successful: Option<bool>, | ||||||
|  |  | ||||||
|     /// The identifier for the initial delivery attempt. This will be the same as `event_id` for |     /// The identifier for the initial delivery attempt. This will be the same as `event_id` for | ||||||
|     /// the initial delivery attempt. |     /// the initial delivery attempt. | ||||||
|  | |||||||
| @ -26,6 +26,7 @@ pub struct EventNew { | |||||||
|     pub response: Option<Encryption>, |     pub response: Option<Encryption>, | ||||||
|     pub delivery_attempt: Option<storage_enums::WebhookDeliveryAttempt>, |     pub delivery_attempt: Option<storage_enums::WebhookDeliveryAttempt>, | ||||||
|     pub metadata: Option<EventMetadata>, |     pub metadata: Option<EventMetadata>, | ||||||
|  |     pub is_overall_delivery_successful: Option<bool>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] | #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] | ||||||
| @ -33,6 +34,7 @@ pub struct EventNew { | |||||||
| pub struct EventUpdateInternal { | pub struct EventUpdateInternal { | ||||||
|     pub is_webhook_notified: Option<bool>, |     pub is_webhook_notified: Option<bool>, | ||||||
|     pub response: Option<Encryption>, |     pub response: Option<Encryption>, | ||||||
|  |     pub is_overall_delivery_successful: Option<bool>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable, Selectable)] | #[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable, Selectable)] | ||||||
| @ -57,6 +59,7 @@ pub struct Event { | |||||||
|     pub response: Option<Encryption>, |     pub response: Option<Encryption>, | ||||||
|     pub delivery_attempt: Option<storage_enums::WebhookDeliveryAttempt>, |     pub delivery_attempt: Option<storage_enums::WebhookDeliveryAttempt>, | ||||||
|     pub metadata: Option<EventMetadata>, |     pub metadata: Option<EventMetadata>, | ||||||
|  |     pub is_overall_delivery_successful: Option<bool>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone, Debug, Deserialize, Serialize, AsExpression, diesel::FromSqlRow)] | #[derive(Clone, Debug, Deserialize, Serialize, AsExpression, diesel::FromSqlRow)] | ||||||
|  | |||||||
| @ -56,6 +56,7 @@ impl Event { | |||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     ) -> StorageResult<Vec<Self>> { |     ) -> StorageResult<Vec<Self>> { | ||||||
|         use async_bb8_diesel::AsyncRunQueryDsl; |         use async_bb8_diesel::AsyncRunQueryDsl; | ||||||
|         use diesel::{debug_query, pg::Pg, QueryDsl}; |         use diesel::{debug_query, pg::Pg, QueryDsl}; | ||||||
| @ -81,6 +82,7 @@ impl Event { | |||||||
|             (dsl::created_at, created_after, created_before), |             (dsl::created_at, created_after, created_before), | ||||||
|             limit, |             limit, | ||||||
|             offset, |             offset, | ||||||
|  |             is_delivered, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         logger::debug!(query = %debug_query::<Pg, _>(&query).to_string()); |         logger::debug!(query = %debug_query::<Pg, _>(&query).to_string()); | ||||||
| @ -134,6 +136,7 @@ impl Event { | |||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     ) -> StorageResult<Vec<Self>> { |     ) -> StorageResult<Vec<Self>> { | ||||||
|         use async_bb8_diesel::AsyncRunQueryDsl; |         use async_bb8_diesel::AsyncRunQueryDsl; | ||||||
|         use diesel::{debug_query, pg::Pg, QueryDsl}; |         use diesel::{debug_query, pg::Pg, QueryDsl}; | ||||||
| @ -159,6 +162,7 @@ impl Event { | |||||||
|             (dsl::created_at, created_after, created_before), |             (dsl::created_at, created_after, created_before), | ||||||
|             limit, |             limit, | ||||||
|             offset, |             offset, | ||||||
|  |             is_delivered, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         logger::debug!(query = %debug_query::<Pg, _>(&query).to_string()); |         logger::debug!(query = %debug_query::<Pg, _>(&query).to_string()); | ||||||
| @ -217,6 +221,7 @@ impl Event { | |||||||
|         ), |         ), | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     ) -> T |     ) -> T | ||||||
|     where |     where | ||||||
|         T: diesel::query_dsl::methods::LimitDsl<Output = T> |         T: diesel::query_dsl::methods::LimitDsl<Output = T> | ||||||
| @ -233,6 +238,10 @@ impl Event { | |||||||
|             diesel::dsl::Eq<dsl::business_profile_id, common_utils::id_type::ProfileId>, |             diesel::dsl::Eq<dsl::business_profile_id, common_utils::id_type::ProfileId>, | ||||||
|             Output = T, |             Output = T, | ||||||
|         >, |         >, | ||||||
|  |         T: diesel::query_dsl::methods::FilterDsl< | ||||||
|  |             diesel::dsl::Eq<dsl::is_overall_delivery_successful, bool>, | ||||||
|  |             Output = T, | ||||||
|  |         >, | ||||||
|     { |     { | ||||||
|         if let Some(profile_id) = profile_id { |         if let Some(profile_id) = profile_id { | ||||||
|             query = query.filter(dsl::business_profile_id.eq(profile_id)); |             query = query.filter(dsl::business_profile_id.eq(profile_id)); | ||||||
| @ -250,6 +259,10 @@ impl Event { | |||||||
|             query = query.offset(offset); |             query = query.offset(offset); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if let Some(is_delivered) = is_delivered { | ||||||
|  |             query = query.filter(dsl::is_overall_delivery_successful.eq(is_delivered)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         query |         query | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -259,6 +272,7 @@ impl Event { | |||||||
|         profile_id: Option<common_utils::id_type::ProfileId>, |         profile_id: Option<common_utils::id_type::ProfileId>, | ||||||
|         created_after: time::PrimitiveDateTime, |         created_after: time::PrimitiveDateTime, | ||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     ) -> StorageResult<i64> { |     ) -> StorageResult<i64> { | ||||||
|         use async_bb8_diesel::AsyncRunQueryDsl; |         use async_bb8_diesel::AsyncRunQueryDsl; | ||||||
|         use diesel::{debug_query, pg::Pg, QueryDsl}; |         use diesel::{debug_query, pg::Pg, QueryDsl}; | ||||||
| @ -284,6 +298,7 @@ impl Event { | |||||||
|             (dsl::created_at, created_after, created_before), |             (dsl::created_at, created_after, created_before), | ||||||
|             None, |             None, | ||||||
|             None, |             None, | ||||||
|  |             is_delivered, | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         logger::debug!(query = %debug_query::<Pg, _>(&query).to_string()); |         logger::debug!(query = %debug_query::<Pg, _>(&query).to_string()); | ||||||
|  | |||||||
| @ -474,6 +474,7 @@ diesel::table! { | |||||||
|         response -> Nullable<Bytea>, |         response -> Nullable<Bytea>, | ||||||
|         delivery_attempt -> Nullable<WebhookDeliveryAttempt>, |         delivery_attempt -> Nullable<WebhookDeliveryAttempt>, | ||||||
|         metadata -> Nullable<Jsonb>, |         metadata -> Nullable<Jsonb>, | ||||||
|  |         is_overall_delivery_successful -> Nullable<Bool>, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -485,6 +485,7 @@ diesel::table! { | |||||||
|         response -> Nullable<Bytea>, |         response -> Nullable<Bytea>, | ||||||
|         delivery_attempt -> Nullable<WebhookDeliveryAttempt>, |         delivery_attempt -> Nullable<WebhookDeliveryAttempt>, | ||||||
|         metadata -> Nullable<Jsonb>, |         metadata -> Nullable<Jsonb>, | ||||||
|  |         is_overall_delivery_successful -> Nullable<Bool>, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -45,6 +45,11 @@ | |||||||
|             Query, |             Query, | ||||||
|             description = "Only include Events associated with the Profile identified by the specified Profile ID." |             description = "Only include Events associated with the Profile identified by the specified Profile ID." | ||||||
|         ), |         ), | ||||||
|  |         ( | ||||||
|  |             "is_delivered" = Option<bool>, | ||||||
|  |             Query, | ||||||
|  |             description = "Only include Events which are ultimately delivered to the merchant." | ||||||
|  |         ), | ||||||
|     ), |     ), | ||||||
|     responses( |     responses( | ||||||
|         (status = 200, description = "List of Events retrieved successfully", body = TotalEventsResponse), |         (status = 200, description = "List of Events retrieved successfully", body = TotalEventsResponse), | ||||||
|  | |||||||
| @ -133,6 +133,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( | |||||||
|         response: None, |         response: None, | ||||||
|         delivery_attempt: Some(delivery_attempt), |         delivery_attempt: Some(delivery_attempt), | ||||||
|         metadata: Some(event_metadata), |         metadata: Some(event_metadata), | ||||||
|  |         is_overall_delivery_successful: Some(false), | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let event_insert_result = state |     let event_insert_result = state | ||||||
| @ -312,7 +313,7 @@ async fn trigger_webhook_to_merchant( | |||||||
|             } |             } | ||||||
|             Ok(response) => { |             Ok(response) => { | ||||||
|                 let status_code = response.status(); |                 let status_code = response.status(); | ||||||
|                 let _updated_event = update_event_in_storage( |                 let updated_event = update_event_in_storage( | ||||||
|                     state.clone(), |                     state.clone(), | ||||||
|                     merchant_key_store.clone(), |                     merchant_key_store.clone(), | ||||||
|                     &business_profile.merchant_id, |                     &business_profile.merchant_id, | ||||||
| @ -322,6 +323,14 @@ async fn trigger_webhook_to_merchant( | |||||||
|                 .await?; |                 .await?; | ||||||
|  |  | ||||||
|                 if status_code.is_success() { |                 if status_code.is_success() { | ||||||
|  |                     update_overall_delivery_status_in_storage( | ||||||
|  |                         state.clone(), | ||||||
|  |                         merchant_key_store.clone(), | ||||||
|  |                         &business_profile.merchant_id, | ||||||
|  |                         updated_event, | ||||||
|  |                     ) | ||||||
|  |                     .await?; | ||||||
|  |  | ||||||
|                     success_response_handler( |                     success_response_handler( | ||||||
|                         state.clone(), |                         state.clone(), | ||||||
|                         &business_profile.merchant_id, |                         &business_profile.merchant_id, | ||||||
| @ -362,7 +371,7 @@ async fn trigger_webhook_to_merchant( | |||||||
|                 } |                 } | ||||||
|                 Ok(response) => { |                 Ok(response) => { | ||||||
|                     let status_code = response.status(); |                     let status_code = response.status(); | ||||||
|                     let _updated_event = update_event_in_storage( |                     let updated_event = update_event_in_storage( | ||||||
|                         state.clone(), |                         state.clone(), | ||||||
|                         merchant_key_store.clone(), |                         merchant_key_store.clone(), | ||||||
|                         &business_profile.merchant_id, |                         &business_profile.merchant_id, | ||||||
| @ -372,6 +381,14 @@ async fn trigger_webhook_to_merchant( | |||||||
|                     .await?; |                     .await?; | ||||||
|  |  | ||||||
|                     if status_code.is_success() { |                     if status_code.is_success() { | ||||||
|  |                         update_overall_delivery_status_in_storage( | ||||||
|  |                             state.clone(), | ||||||
|  |                             merchant_key_store.clone(), | ||||||
|  |                             &business_profile.merchant_id, | ||||||
|  |                             updated_event, | ||||||
|  |                         ) | ||||||
|  |                         .await?; | ||||||
|  |  | ||||||
|                         success_response_handler( |                         success_response_handler( | ||||||
|                             state.clone(), |                             state.clone(), | ||||||
|                             &business_profile.merchant_id, |                             &business_profile.merchant_id, | ||||||
| @ -837,6 +854,44 @@ async fn update_event_in_storage( | |||||||
|         .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed) |         .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | async fn update_overall_delivery_status_in_storage( | ||||||
|  |     state: SessionState, | ||||||
|  |     merchant_key_store: domain::MerchantKeyStore, | ||||||
|  |     merchant_id: &common_utils::id_type::MerchantId, | ||||||
|  |     updated_event: domain::Event, | ||||||
|  | ) -> CustomResult<(), errors::WebhooksFlowError> { | ||||||
|  |     let key_manager_state = &(&state).into(); | ||||||
|  |  | ||||||
|  |     let update_overall_delivery_status = domain::EventUpdate::OverallDeliveryStatusUpdate { | ||||||
|  |         is_overall_delivery_successful: true, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let initial_attempt_id = updated_event.initial_attempt_id.as_ref(); | ||||||
|  |     let delivery_attempt = updated_event.delivery_attempt; | ||||||
|  |  | ||||||
|  |     if let Some(( | ||||||
|  |         initial_attempt_id, | ||||||
|  |         enums::WebhookDeliveryAttempt::InitialAttempt | ||||||
|  |         | enums::WebhookDeliveryAttempt::AutomaticRetry, | ||||||
|  |     )) = initial_attempt_id.zip(delivery_attempt) | ||||||
|  |     { | ||||||
|  |         state | ||||||
|  |             .store | ||||||
|  |             .update_event_by_merchant_id_event_id( | ||||||
|  |                 key_manager_state, | ||||||
|  |                 merchant_id, | ||||||
|  |                 initial_attempt_id.as_str(), | ||||||
|  |                 update_overall_delivery_status, | ||||||
|  |                 &merchant_key_store, | ||||||
|  |             ) | ||||||
|  |             .await | ||||||
|  |             .change_context(errors::WebhooksFlowError::WebhookEventUpdationFailed) | ||||||
|  |             .attach_printable("Failed to update initial delivery attempt")?; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  |  | ||||||
| fn increment_webhook_outgoing_received_count(merchant_id: &common_utils::id_type::MerchantId) { | fn increment_webhook_outgoing_received_count(merchant_id: &common_utils::id_type::MerchantId) { | ||||||
|     metrics::WEBHOOK_OUTGOING_RECEIVED_COUNT.add( |     metrics::WEBHOOK_OUTGOING_RECEIVED_COUNT.add( | ||||||
|         1, |         1, | ||||||
|  | |||||||
| @ -64,6 +64,7 @@ pub async fn list_initial_delivery_attempts( | |||||||
|             created_before, |             created_before, | ||||||
|             limit, |             limit, | ||||||
|             offset, |             offset, | ||||||
|  |             is_delivered | ||||||
|         } => { |         } => { | ||||||
|             let limit = match limit { |             let limit = match limit { | ||||||
|                 Some(limit) if  limit <= INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT => Ok(Some(limit)), |                 Some(limit) if  limit <= INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT => Ok(Some(limit)), | ||||||
| @ -114,6 +115,7 @@ pub async fn list_initial_delivery_attempts( | |||||||
|                     created_before, |                     created_before, | ||||||
|                     limit, |                     limit, | ||||||
|                     offset, |                     offset, | ||||||
|  |                     is_delivered, | ||||||
|                     &key_store, |                     &key_store, | ||||||
|                 ) |                 ) | ||||||
|                 .await, |                 .await, | ||||||
| @ -124,6 +126,7 @@ pub async fn list_initial_delivery_attempts( | |||||||
|                     created_before, |                     created_before, | ||||||
|                     limit, |                     limit, | ||||||
|                     offset, |                     offset, | ||||||
|  |                     is_delivered, | ||||||
|                     &key_store, |                     &key_store, | ||||||
|                 ) |                 ) | ||||||
|                 .await, |                 .await, | ||||||
| @ -143,12 +146,15 @@ pub async fn list_initial_delivery_attempts( | |||||||
|         .unwrap_or(events_list_begin_time); |         .unwrap_or(events_list_begin_time); | ||||||
|     let created_before = api_constraints.created_before.unwrap_or(now); |     let created_before = api_constraints.created_before.unwrap_or(now); | ||||||
|  |  | ||||||
|  |     let is_delivered = api_constraints.is_delivered; | ||||||
|  |  | ||||||
|     let total_count = store |     let total_count = store | ||||||
|         .count_initial_events_by_constraints( |         .count_initial_events_by_constraints( | ||||||
|             &merchant_id, |             &merchant_id, | ||||||
|             profile_id, |             profile_id, | ||||||
|             created_after, |             created_after, | ||||||
|             created_before, |             created_before, | ||||||
|  |             is_delivered, | ||||||
|         ) |         ) | ||||||
|         .await |         .await | ||||||
|         .change_context(errors::ApiErrorResponse::InternalServerError) |         .change_context(errors::ApiErrorResponse::InternalServerError) | ||||||
| @ -269,6 +275,7 @@ pub async fn retry_delivery_attempt( | |||||||
|         response: None, |         response: None, | ||||||
|         delivery_attempt: Some(delivery_attempt), |         delivery_attempt: Some(delivery_attempt), | ||||||
|         metadata: event_to_retry.metadata, |         metadata: event_to_retry.metadata, | ||||||
|  |         is_overall_delivery_successful: Some(false), | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let event = store |     let event = store | ||||||
|  | |||||||
| @ -53,6 +53,7 @@ where | |||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|         merchant_key_store: &domain::MerchantKeyStore, |         merchant_key_store: &domain::MerchantKeyStore, | ||||||
|     ) -> CustomResult<Vec<domain::Event>, errors::StorageError>; |     ) -> CustomResult<Vec<domain::Event>, errors::StorageError>; | ||||||
|  |  | ||||||
| @ -81,6 +82,7 @@ where | |||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|         merchant_key_store: &domain::MerchantKeyStore, |         merchant_key_store: &domain::MerchantKeyStore, | ||||||
|     ) -> CustomResult<Vec<domain::Event>, errors::StorageError>; |     ) -> CustomResult<Vec<domain::Event>, errors::StorageError>; | ||||||
|  |  | ||||||
| @ -99,6 +101,7 @@ where | |||||||
|         profile_id: Option<common_utils::id_type::ProfileId>, |         profile_id: Option<common_utils::id_type::ProfileId>, | ||||||
|         created_after: time::PrimitiveDateTime, |         created_after: time::PrimitiveDateTime, | ||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     ) -> CustomResult<i64, errors::StorageError>; |     ) -> CustomResult<i64, errors::StorageError>; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -193,6 +196,7 @@ impl EventInterface for Store { | |||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|         merchant_key_store: &domain::MerchantKeyStore, |         merchant_key_store: &domain::MerchantKeyStore, | ||||||
|     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { |     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { | ||||||
|         let conn = connection::pg_connection_read(self).await?; |         let conn = connection::pg_connection_read(self).await?; | ||||||
| @ -203,6 +207,7 @@ impl EventInterface for Store { | |||||||
|             created_before, |             created_before, | ||||||
|             limit, |             limit, | ||||||
|             offset, |             offset, | ||||||
|  |             is_delivered, | ||||||
|         ) |         ) | ||||||
|         .await |         .await | ||||||
|         .map_err(|error| report!(errors::StorageError::from(error))) |         .map_err(|error| report!(errors::StorageError::from(error))) | ||||||
| @ -304,6 +309,7 @@ impl EventInterface for Store { | |||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|         merchant_key_store: &domain::MerchantKeyStore, |         merchant_key_store: &domain::MerchantKeyStore, | ||||||
|     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { |     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { | ||||||
|         let conn = connection::pg_connection_read(self).await?; |         let conn = connection::pg_connection_read(self).await?; | ||||||
| @ -314,6 +320,7 @@ impl EventInterface for Store { | |||||||
|             created_before, |             created_before, | ||||||
|             limit, |             limit, | ||||||
|             offset, |             offset, | ||||||
|  |             is_delivered, | ||||||
|         ) |         ) | ||||||
|         .await |         .await | ||||||
|         .map_err(|error| report!(errors::StorageError::from(error))) |         .map_err(|error| report!(errors::StorageError::from(error))) | ||||||
| @ -366,6 +373,7 @@ impl EventInterface for Store { | |||||||
|         profile_id: Option<common_utils::id_type::ProfileId>, |         profile_id: Option<common_utils::id_type::ProfileId>, | ||||||
|         created_after: time::PrimitiveDateTime, |         created_after: time::PrimitiveDateTime, | ||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     ) -> CustomResult<i64, errors::StorageError> { |     ) -> CustomResult<i64, errors::StorageError> { | ||||||
|         let conn = connection::pg_connection_read(self).await?; |         let conn = connection::pg_connection_read(self).await?; | ||||||
|         storage::Event::count_initial_attempts_by_constraints( |         storage::Event::count_initial_attempts_by_constraints( | ||||||
| @ -374,6 +382,7 @@ impl EventInterface for Store { | |||||||
|             profile_id, |             profile_id, | ||||||
|             created_after, |             created_after, | ||||||
|             created_before, |             created_before, | ||||||
|  |             is_delivered, | ||||||
|         ) |         ) | ||||||
|         .await |         .await | ||||||
|         .map_err(|error| report!(errors::StorageError::from(error))) |         .map_err(|error| report!(errors::StorageError::from(error))) | ||||||
| @ -483,6 +492,7 @@ impl EventInterface for MockDb { | |||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|         merchant_key_store: &domain::MerchantKeyStore, |         merchant_key_store: &domain::MerchantKeyStore, | ||||||
|     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { |     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { | ||||||
|         let locked_events = self.events.lock().await; |         let locked_events = self.events.lock().await; | ||||||
| @ -490,10 +500,12 @@ impl EventInterface for MockDb { | |||||||
|             let check = event.merchant_id == Some(merchant_id.to_owned()) |             let check = event.merchant_id == Some(merchant_id.to_owned()) | ||||||
|                 && event.initial_attempt_id.as_ref() == Some(&event.event_id) |                 && event.initial_attempt_id.as_ref() == Some(&event.event_id) | ||||||
|                 && (event.created_at >= created_after) |                 && (event.created_at >= created_after) | ||||||
|                 && (event.created_at <= created_before); |                 && (event.created_at <= created_before) | ||||||
|  |                 && (event.is_overall_delivery_successful == is_delivered); | ||||||
|  |  | ||||||
|             check |             check | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         let offset: usize = if let Some(offset) = offset { |         let offset: usize = if let Some(offset) = offset { | ||||||
|             if offset < 0 { |             if offset < 0 { | ||||||
|                 Err(errors::StorageError::MockDbError)?; |                 Err(errors::StorageError::MockDbError)?; | ||||||
| @ -614,6 +626,7 @@ impl EventInterface for MockDb { | |||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|         merchant_key_store: &domain::MerchantKeyStore, |         merchant_key_store: &domain::MerchantKeyStore, | ||||||
|     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { |     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { | ||||||
|         let locked_events = self.events.lock().await; |         let locked_events = self.events.lock().await; | ||||||
| @ -621,7 +634,8 @@ impl EventInterface for MockDb { | |||||||
|             let check = event.business_profile_id == Some(profile_id.to_owned()) |             let check = event.business_profile_id == Some(profile_id.to_owned()) | ||||||
|                 && event.initial_attempt_id.as_ref() == Some(&event.event_id) |                 && event.initial_attempt_id.as_ref() == Some(&event.event_id) | ||||||
|                 && (event.created_at >= created_after) |                 && (event.created_at >= created_after) | ||||||
|                 && (event.created_at <= created_before); |                 && (event.created_at <= created_before) | ||||||
|  |                 && (event.is_overall_delivery_successful == is_delivered); | ||||||
|  |  | ||||||
|             check |             check | ||||||
|         }); |         }); | ||||||
| @ -694,6 +708,12 @@ impl EventInterface for MockDb { | |||||||
|                 event_to_update.is_webhook_notified = is_webhook_notified; |                 event_to_update.is_webhook_notified = is_webhook_notified; | ||||||
|                 event_to_update.response = response.map(Into::into); |                 event_to_update.response = response.map(Into::into); | ||||||
|             } |             } | ||||||
|  |             domain::EventUpdate::OverallDeliveryStatusUpdate { | ||||||
|  |                 is_overall_delivery_successful, | ||||||
|  |             } => { | ||||||
|  |                 event_to_update.is_overall_delivery_successful = | ||||||
|  |                     Some(is_overall_delivery_successful) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         event_to_update |         event_to_update | ||||||
| @ -713,6 +733,7 @@ impl EventInterface for MockDb { | |||||||
|         profile_id: Option<common_utils::id_type::ProfileId>, |         profile_id: Option<common_utils::id_type::ProfileId>, | ||||||
|         created_after: time::PrimitiveDateTime, |         created_after: time::PrimitiveDateTime, | ||||||
|         created_before: time::PrimitiveDateTime, |         created_before: time::PrimitiveDateTime, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     ) -> CustomResult<i64, errors::StorageError> { |     ) -> CustomResult<i64, errors::StorageError> { | ||||||
|         let locked_events = self.events.lock().await; |         let locked_events = self.events.lock().await; | ||||||
|  |  | ||||||
| @ -721,7 +742,8 @@ impl EventInterface for MockDb { | |||||||
|                 && (event.merchant_id == Some(merchant_id.to_owned())) |                 && (event.merchant_id == Some(merchant_id.to_owned())) | ||||||
|                 && (event.business_profile_id == profile_id) |                 && (event.business_profile_id == profile_id) | ||||||
|                 && (event.created_at >= created_after) |                 && (event.created_at >= created_after) | ||||||
|                 && (event.created_at <= created_before); |                 && (event.created_at <= created_before) | ||||||
|  |                 && (event.is_overall_delivery_successful == is_delivered); | ||||||
|  |  | ||||||
|             check |             check | ||||||
|         }); |         }); | ||||||
| @ -843,6 +865,7 @@ mod tests { | |||||||
|                         ) |                         ) | ||||||
|                         .unwrap(), |                         .unwrap(), | ||||||
|                     }), |                     }), | ||||||
|  |                     is_overall_delivery_successful: Some(false), | ||||||
|                 }, |                 }, | ||||||
|                 &merchant_key_store, |                 &merchant_key_store, | ||||||
|             ) |             ) | ||||||
|  | |||||||
| @ -772,6 +772,7 @@ impl EventInterface for KafkaStore { | |||||||
|         created_before: PrimitiveDateTime, |         created_before: PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|         merchant_key_store: &domain::MerchantKeyStore, |         merchant_key_store: &domain::MerchantKeyStore, | ||||||
|     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { |     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { | ||||||
|         self.diesel_store |         self.diesel_store | ||||||
| @ -782,6 +783,7 @@ impl EventInterface for KafkaStore { | |||||||
|                 created_before, |                 created_before, | ||||||
|                 limit, |                 limit, | ||||||
|                 offset, |                 offset, | ||||||
|  |                 is_delivered, | ||||||
|                 merchant_key_store, |                 merchant_key_store, | ||||||
|             ) |             ) | ||||||
|             .await |             .await | ||||||
| @ -829,6 +831,7 @@ impl EventInterface for KafkaStore { | |||||||
|         created_before: PrimitiveDateTime, |         created_before: PrimitiveDateTime, | ||||||
|         limit: Option<i64>, |         limit: Option<i64>, | ||||||
|         offset: Option<i64>, |         offset: Option<i64>, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|         merchant_key_store: &domain::MerchantKeyStore, |         merchant_key_store: &domain::MerchantKeyStore, | ||||||
|     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { |     ) -> CustomResult<Vec<domain::Event>, errors::StorageError> { | ||||||
|         self.diesel_store |         self.diesel_store | ||||||
| @ -839,6 +842,7 @@ impl EventInterface for KafkaStore { | |||||||
|                 created_before, |                 created_before, | ||||||
|                 limit, |                 limit, | ||||||
|                 offset, |                 offset, | ||||||
|  |                 is_delivered, | ||||||
|                 merchant_key_store, |                 merchant_key_store, | ||||||
|             ) |             ) | ||||||
|             .await |             .await | ||||||
| @ -869,6 +873,7 @@ impl EventInterface for KafkaStore { | |||||||
|         profile_id: Option<id_type::ProfileId>, |         profile_id: Option<id_type::ProfileId>, | ||||||
|         created_after: PrimitiveDateTime, |         created_after: PrimitiveDateTime, | ||||||
|         created_before: PrimitiveDateTime, |         created_before: PrimitiveDateTime, | ||||||
|  |         is_delivered: Option<bool>, | ||||||
|     ) -> CustomResult<i64, errors::StorageError> { |     ) -> CustomResult<i64, errors::StorageError> { | ||||||
|         self.diesel_store |         self.diesel_store | ||||||
|             .count_initial_events_by_constraints( |             .count_initial_events_by_constraints( | ||||||
| @ -876,6 +881,7 @@ impl EventInterface for KafkaStore { | |||||||
|                 profile_id, |                 profile_id, | ||||||
|                 created_after, |                 created_after, | ||||||
|                 created_before, |                 created_before, | ||||||
|  |                 is_delivered, | ||||||
|             ) |             ) | ||||||
|             .await |             .await | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -19,24 +19,58 @@ use crate::{ | |||||||
|  |  | ||||||
| #[derive(Clone, Debug, router_derive::ToEncryption)] | #[derive(Clone, Debug, router_derive::ToEncryption)] | ||||||
| pub struct Event { | pub struct Event { | ||||||
|  |     /// A string that uniquely identifies the event. | ||||||
|     pub event_id: String, |     pub event_id: String, | ||||||
|  |  | ||||||
|  |     /// Represents the type of event for the webhook. | ||||||
|     pub event_type: EventType, |     pub event_type: EventType, | ||||||
|  |  | ||||||
|  |     /// Represents the class of event for the webhook. | ||||||
|     pub event_class: EventClass, |     pub event_class: EventClass, | ||||||
|  |  | ||||||
|  |     /// Indicates whether the current webhook delivery was successful. | ||||||
|     pub is_webhook_notified: bool, |     pub is_webhook_notified: bool, | ||||||
|  |  | ||||||
|  |     /// Reference to the object for which the webhook was created. | ||||||
|     pub primary_object_id: String, |     pub primary_object_id: String, | ||||||
|  |  | ||||||
|  |     /// Type of the object type for which the webhook was created. | ||||||
|     pub primary_object_type: EventObjectType, |     pub primary_object_type: EventObjectType, | ||||||
|  |  | ||||||
|  |     /// The timestamp when the webhook was created. | ||||||
|     pub created_at: time::PrimitiveDateTime, |     pub created_at: time::PrimitiveDateTime, | ||||||
|  |  | ||||||
|  |     /// Merchant Account identifier to which the object is associated with. | ||||||
|     pub merchant_id: Option<common_utils::id_type::MerchantId>, |     pub merchant_id: Option<common_utils::id_type::MerchantId>, | ||||||
|  |  | ||||||
|  |     /// Business Profile identifier to which the object is associated with. | ||||||
|     pub business_profile_id: Option<common_utils::id_type::ProfileId>, |     pub business_profile_id: Option<common_utils::id_type::ProfileId>, | ||||||
|  |  | ||||||
|  |     /// The timestamp when the primary object was created. | ||||||
|     pub primary_object_created_at: Option<time::PrimitiveDateTime>, |     pub primary_object_created_at: Option<time::PrimitiveDateTime>, | ||||||
|  |  | ||||||
|  |     /// This allows the event to be uniquely identified to prevent multiple processing. | ||||||
|     pub idempotent_event_id: Option<String>, |     pub idempotent_event_id: Option<String>, | ||||||
|  |  | ||||||
|  |     /// Links to the initial attempt of the event. | ||||||
|     pub initial_attempt_id: Option<String>, |     pub initial_attempt_id: Option<String>, | ||||||
|  |  | ||||||
|  |     /// This field contains the encrypted request data sent as part of the event. | ||||||
|     #[encrypt] |     #[encrypt] | ||||||
|     pub request: Option<Encryptable<Secret<String>>>, |     pub request: Option<Encryptable<Secret<String>>>, | ||||||
|  |  | ||||||
|  |     /// This field contains the encrypted response data received as part of the event. | ||||||
|     #[encrypt] |     #[encrypt] | ||||||
|     pub response: Option<Encryptable<Secret<String>>>, |     pub response: Option<Encryptable<Secret<String>>>, | ||||||
|  |  | ||||||
|  |     /// Represents the event delivery type. | ||||||
|     pub delivery_attempt: Option<WebhookDeliveryAttempt>, |     pub delivery_attempt: Option<WebhookDeliveryAttempt>, | ||||||
|  |  | ||||||
|  |     /// Holds any additional data related to the event. | ||||||
|     pub metadata: Option<EventMetadata>, |     pub metadata: Option<EventMetadata>, | ||||||
|  |  | ||||||
|  |     /// Indicates whether the event was ultimately delivered. | ||||||
|  |     pub is_overall_delivery_successful: Option<bool>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| @ -45,6 +79,9 @@ pub enum EventUpdate { | |||||||
|         is_webhook_notified: bool, |         is_webhook_notified: bool, | ||||||
|         response: OptionalEncryptableSecretString, |         response: OptionalEncryptableSecretString, | ||||||
|     }, |     }, | ||||||
|  |     OverallDeliveryStatusUpdate { | ||||||
|  |         is_overall_delivery_successful: bool, | ||||||
|  |     }, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<EventUpdate> for EventUpdateInternal { | impl From<EventUpdate> for EventUpdateInternal { | ||||||
| @ -56,6 +93,14 @@ impl From<EventUpdate> for EventUpdateInternal { | |||||||
|             } => Self { |             } => Self { | ||||||
|                 is_webhook_notified: Some(is_webhook_notified), |                 is_webhook_notified: Some(is_webhook_notified), | ||||||
|                 response: response.map(Into::into), |                 response: response.map(Into::into), | ||||||
|  |                 is_overall_delivery_successful: None, | ||||||
|  |             }, | ||||||
|  |             EventUpdate::OverallDeliveryStatusUpdate { | ||||||
|  |                 is_overall_delivery_successful, | ||||||
|  |             } => Self { | ||||||
|  |                 is_webhook_notified: None, | ||||||
|  |                 response: None, | ||||||
|  |                 is_overall_delivery_successful: Some(is_overall_delivery_successful), | ||||||
|             }, |             }, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -84,6 +129,7 @@ impl super::behaviour::Conversion for Event { | |||||||
|             response: self.response.map(Into::into), |             response: self.response.map(Into::into), | ||||||
|             delivery_attempt: self.delivery_attempt, |             delivery_attempt: self.delivery_attempt, | ||||||
|             metadata: self.metadata, |             metadata: self.metadata, | ||||||
|  |             is_overall_delivery_successful: self.is_overall_delivery_successful, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -133,6 +179,7 @@ impl super::behaviour::Conversion for Event { | |||||||
|             response: encryptable_event.response, |             response: encryptable_event.response, | ||||||
|             delivery_attempt: item.delivery_attempt, |             delivery_attempt: item.delivery_attempt, | ||||||
|             metadata: item.metadata, |             metadata: item.metadata, | ||||||
|  |             is_overall_delivery_successful: item.is_overall_delivery_successful, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -154,6 +201,7 @@ impl super::behaviour::Conversion for Event { | |||||||
|             response: self.response.map(Into::into), |             response: self.response.map(Into::into), | ||||||
|             delivery_attempt: self.delivery_attempt, |             delivery_attempt: self.delivery_attempt, | ||||||
|             metadata: self.metadata, |             metadata: self.metadata, | ||||||
|  |             is_overall_delivery_successful: self.is_overall_delivery_successful, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1936,6 +1936,7 @@ impl ForeignTryFrom<api_types::webhook_events::EventListConstraints> | |||||||
|                 created_before: item.created_before, |                 created_before: item.created_before, | ||||||
|                 limit: item.limit.map(i64::from), |                 limit: item.limit.map(i64::from), | ||||||
|                 offset: item.offset.map(i64::from), |                 offset: item.offset.map(i64::from), | ||||||
|  |                 is_delivered: item.is_delivered, | ||||||
|             }), |             }), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -1971,7 +1972,7 @@ impl TryFrom<domain::Event> for api_models::webhook_events::EventListItemRespons | |||||||
|             object_id: item.primary_object_id, |             object_id: item.primary_object_id, | ||||||
|             event_type: item.event_type, |             event_type: item.event_type, | ||||||
|             event_class: item.event_class, |             event_class: item.event_class, | ||||||
|             is_delivery_successful: item.is_webhook_notified, |             is_delivery_successful: item.is_overall_delivery_successful, | ||||||
|             initial_attempt_id, |             initial_attempt_id, | ||||||
|             created: item.created_at, |             created: item.created_at, | ||||||
|         }) |         }) | ||||||
|  | |||||||
| @ -118,6 +118,7 @@ impl ProcessTrackerWorkflow<SessionState> for OutgoingWebhookRetryWorkflow { | |||||||
|             response: None, |             response: None, | ||||||
|             delivery_attempt: Some(delivery_attempt), |             delivery_attempt: Some(delivery_attempt), | ||||||
|             metadata: initial_event.metadata, |             metadata: initial_event.metadata, | ||||||
|  |             is_overall_delivery_successful: Some(false), | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let event = db |         let event = db | ||||||
|  | |||||||
| @ -0,0 +1 @@ | |||||||
|  | ALTER TABLE events DROP COLUMN IF EXISTS is_overall_delivery_successful; | ||||||
| @ -0,0 +1 @@ | |||||||
|  | ALTER TABLE events ADD COLUMN IF NOT EXISTS is_overall_delivery_successful BOOLEAN; | ||||||
		Reference in New Issue
	
	Block a user
	 Amey Wale
					Amey Wale