diff --git a/crates/diesel_models/src/gsm.rs b/crates/diesel_models/src/gsm.rs new file mode 100644 index 000000000000..d5b3122c7806 --- /dev/null +++ b/crates/diesel_models/src/gsm.rs @@ -0,0 +1,97 @@ +//! Gateway status mapping + +use common_utils::custom_serde; +use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; +use time::PrimitiveDateTime; + +use crate::schema::gateway_status_map; + +#[derive( + Clone, + Debug, + Eq, + PartialEq, + router_derive::DebugAsDisplay, + Identifiable, + Queryable, + serde::Serialize, +)] +#[diesel(table_name = gateway_status_map, primary_key(connector, flow, sub_flow, code, message))] +pub struct GatewayStatusMap { + pub connector: String, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, + pub status: String, + pub router_error: Option, + pub decision: String, + #[serde(with = "custom_serde::iso8601")] + pub created_at: PrimitiveDateTime, + #[serde(with = "custom_serde::iso8601")] + pub last_modified: PrimitiveDateTime, + pub step_up_possible: bool, +} + +#[derive(Clone, Debug, Eq, PartialEq, Insertable)] +#[diesel(table_name = gateway_status_map)] +pub struct GatewayStatusMappingNew { + pub connector: String, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, + pub status: String, + pub router_error: Option, + pub decision: String, + pub step_up_possible: bool, +} + +#[derive( + Clone, + Debug, + PartialEq, + Eq, + AsChangeset, + router_derive::DebugAsDisplay, + Default, + serde::Deserialize, +)] +#[diesel(table_name = gateway_status_map)] +pub struct GatewayStatusMapperUpdateInternal { + pub connector: Option, + pub flow: Option, + pub sub_flow: Option, + pub code: Option, + pub message: Option, + pub status: Option, + pub router_error: Option>, + pub decision: Option, + pub step_up_possible: Option, +} + +#[derive(Debug)] +pub struct GatewayStatusMappingUpdate { + pub status: Option, + pub router_error: Option>, + pub decision: Option, + pub step_up_possible: Option, +} + +impl From for GatewayStatusMapperUpdateInternal { + fn from(value: GatewayStatusMappingUpdate) -> Self { + let GatewayStatusMappingUpdate { + decision, + status, + router_error, + step_up_possible, + } = value; + Self { + status, + router_error, + decision, + step_up_possible, + ..Default::default() + } + } +} diff --git a/crates/diesel_models/src/lib.rs b/crates/diesel_models/src/lib.rs index 2d459499a1bd..08d74fb8fd37 100644 --- a/crates/diesel_models/src/lib.rs +++ b/crates/diesel_models/src/lib.rs @@ -15,6 +15,7 @@ pub mod events; pub mod file; #[allow(unused)] pub mod fraud_check; +pub mod gsm; #[cfg(feature = "kv_store")] pub mod kv; pub mod locker_mock_up; diff --git a/crates/diesel_models/src/query.rs b/crates/diesel_models/src/query.rs index aeb09b969f13..ac3eeba44359 100644 --- a/crates/diesel_models/src/query.rs +++ b/crates/diesel_models/src/query.rs @@ -11,6 +11,7 @@ pub mod events; pub mod file; pub mod fraud_check; pub mod generics; +pub mod gsm; pub mod locker_mock_up; pub mod mandate; pub mod merchant_account; diff --git a/crates/diesel_models/src/query/gsm.rs b/crates/diesel_models/src/query/gsm.rs new file mode 100644 index 000000000000..bd44ce4dc378 --- /dev/null +++ b/crates/diesel_models/src/query/gsm.rs @@ -0,0 +1,100 @@ +use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; +use error_stack::report; + +use crate::{ + errors, gsm::*, query::generics, schema::gateway_status_map::dsl, PgPooledConn, StorageResult, +}; + +impl GatewayStatusMappingNew { + pub async fn insert(self, conn: &PgPooledConn) -> StorageResult { + generics::generic_insert(conn, self).await + } +} + +impl GatewayStatusMap { + pub async fn find( + conn: &PgPooledConn, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> StorageResult { + generics::generic_find_one::<::Table, _, _>( + conn, + dsl::connector + .eq(connector) + .and(dsl::flow.eq(flow)) + .and(dsl::sub_flow.eq(sub_flow)) + .and(dsl::code.eq(code)) + .and(dsl::message.eq(message)), + ) + .await + } + + pub async fn retrieve_decision( + conn: &PgPooledConn, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> StorageResult { + Self::find(conn, connector, flow, sub_flow, code, message) + .await + .map(|item| item.decision) + } + + pub async fn update( + conn: &PgPooledConn, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + gsm: GatewayStatusMappingUpdate, + ) -> StorageResult { + generics::generic_update_with_results::< + ::Table, + GatewayStatusMapperUpdateInternal, + _, + _, + >( + conn, + dsl::connector + .eq(connector) + .and(dsl::flow.eq(flow)) + .and(dsl::sub_flow.eq(sub_flow)) + .and(dsl::code.eq(code)) + .and(dsl::message.eq(message)), + gsm.into(), + ) + .await? + .first() + .cloned() + .ok_or_else(|| { + report!(errors::DatabaseError::NotFound) + .attach_printable("Error while updating gsm entry") + }) + } + + pub async fn delete( + conn: &PgPooledConn, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> StorageResult { + generics::generic_delete::<::Table, _>( + conn, + dsl::connector + .eq(connector) + .and(dsl::flow.eq(flow)) + .and(dsl::sub_flow.eq(sub_flow)) + .and(dsl::code.eq(code)) + .and(dsl::message.eq(message)), + ) + .await + } +} diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 2923c719c8f7..50531e432adc 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -332,6 +332,33 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use crate::enums::diesel_exports::*; + + gateway_status_map (connector, flow, sub_flow, code, message) { + #[max_length = 64] + connector -> Varchar, + #[max_length = 64] + flow -> Varchar, + #[max_length = 64] + sub_flow -> Varchar, + #[max_length = 255] + code -> Varchar, + #[max_length = 1024] + message -> Varchar, + #[max_length = 64] + status -> Varchar, + #[max_length = 64] + router_error -> Nullable, + #[max_length = 64] + decision -> Varchar, + created_at -> Timestamp, + last_modified -> Timestamp, + step_up_possible -> Bool, + } +} + diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; @@ -909,6 +936,7 @@ diesel::allow_tables_to_appear_in_same_query!( events, file_metadata, fraud_check, + gateway_status_map, locker_mock_up, mandate, merchant_account, diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index b62ffd2c530f..3efef2c40f29 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -12,6 +12,7 @@ pub mod ephemeral_key; pub mod events; pub mod file; pub mod fraud_check; +pub mod gsm; pub mod locker_mock_up; pub mod mandate; pub mod merchant_account; @@ -80,6 +81,7 @@ pub trait StorageInterface: + business_profile::BusinessProfileInterface + organization::OrganizationInterface + routing_algorithm::RoutingAlgorithmInterface + + gsm::GsmInterface + 'static { fn get_scheduler_db(&self) -> Box; diff --git a/crates/router/src/db/gsm.rs b/crates/router/src/db/gsm.rs new file mode 100644 index 000000000000..b623bdc2bcf5 --- /dev/null +++ b/crates/router/src/db/gsm.rs @@ -0,0 +1,180 @@ +use diesel_models::gsm as storage; +use error_stack::IntoReport; + +use super::MockDb; +use crate::{ + connection, + core::errors::{self, CustomResult}, + services::Store, +}; + +#[async_trait::async_trait] +pub trait GsmInterface { + async fn add_gsm_rule( + &self, + rule: storage::GatewayStatusMappingNew, + ) -> CustomResult; + async fn find_gsm_decision( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult; + async fn find_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult; + async fn update_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + data: storage::GatewayStatusMappingUpdate, + ) -> CustomResult; + + async fn delete_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult; +} + +#[async_trait::async_trait] +impl GsmInterface for Store { + async fn add_gsm_rule( + &self, + rule: storage::GatewayStatusMappingNew, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + rule.insert(&conn).await.map_err(Into::into).into_report() + } + + async fn find_gsm_decision( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + storage::GatewayStatusMap::retrieve_decision( + &conn, connector, flow, sub_flow, code, message, + ) + .await + .map_err(Into::into) + .into_report() + } + + async fn find_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + storage::GatewayStatusMap::find(&conn, connector, flow, sub_flow, code, message) + .await + .map_err(Into::into) + .into_report() + } + + async fn update_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + data: storage::GatewayStatusMappingUpdate, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + storage::GatewayStatusMap::update(&conn, connector, flow, sub_flow, code, message, data) + .await + .map_err(Into::into) + .into_report() + } + + async fn delete_gsm_rule( + &self, + connector: String, + flow: String, + sub_flow: String, + code: String, + message: String, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + storage::GatewayStatusMap::delete(&conn, connector, flow, sub_flow, code, message) + .await + .map_err(Into::into) + .into_report() + } +} + +#[async_trait::async_trait] +impl GsmInterface for MockDb { + async fn add_gsm_rule( + &self, + _rule: storage::GatewayStatusMappingNew, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } + + async fn find_gsm_decision( + &self, + _connector: String, + _flow: String, + _sub_flow: String, + _code: String, + _message: String, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } + + async fn find_gsm_rule( + &self, + _connector: String, + _flow: String, + _sub_flow: String, + _code: String, + _message: String, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } + + async fn update_gsm_rule( + &self, + _connector: String, + _flow: String, + _sub_flow: String, + _code: String, + _message: String, + _data: storage::GatewayStatusMappingUpdate, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } + + async fn delete_gsm_rule( + &self, + _connector: String, + _flow: String, + _sub_flow: String, + _code: String, + _message: String, + ) -> CustomResult { + Err(errors::StorageError::MockDbError)? + } +} diff --git a/crates/router/src/types/storage.rs b/crates/router/src/types/storage.rs index 00a5e07a30e8..1e7c34a420b1 100644 --- a/crates/router/src/types/storage.rs +++ b/crates/router/src/types/storage.rs @@ -11,6 +11,7 @@ pub mod enums; pub mod ephemeral_key; pub mod events; pub mod file; +pub mod gsm; #[cfg(feature = "kv_store")] pub mod kv; pub mod locker_mock_up; @@ -41,10 +42,10 @@ pub use data_models::payments::{ pub use self::{ address::*, api_keys::*, capture::*, cards_info::*, configs::*, connector_response::*, - customers::*, dispute::*, ephemeral_key::*, events::*, file::*, locker_mock_up::*, mandate::*, - merchant_account::*, merchant_connector_account::*, merchant_key_store::*, payment_link::*, - payment_method::*, payout_attempt::*, payouts::*, process_tracker::*, refund::*, - reverse_lookup::*, routing_algorithm::*, + customers::*, dispute::*, ephemeral_key::*, events::*, file::*, gsm::*, locker_mock_up::*, + mandate::*, merchant_account::*, merchant_connector_account::*, merchant_key_store::*, + payment_link::*, payment_method::*, payout_attempt::*, payouts::*, process_tracker::*, + refund::*, reverse_lookup::*, routing_algorithm::*, }; use crate::types::api::routing; diff --git a/crates/router/src/types/storage/gsm.rs b/crates/router/src/types/storage/gsm.rs new file mode 100644 index 000000000000..bcea00e90910 --- /dev/null +++ b/crates/router/src/types/storage/gsm.rs @@ -0,0 +1,4 @@ +pub use diesel_models::gsm::{ + GatewayStatusMap, GatewayStatusMapperUpdateInternal, GatewayStatusMappingNew, + GatewayStatusMappingUpdate, +}; diff --git a/migrations/2023-11-07-110139_add_gsm_table/down.sql b/migrations/2023-11-07-110139_add_gsm_table/down.sql new file mode 100644 index 000000000000..e1cdd5d4133d --- /dev/null +++ b/migrations/2023-11-07-110139_add_gsm_table/down.sql @@ -0,0 +1,2 @@ +-- Tables +DROP TABLE gateway_status_map; diff --git a/migrations/2023-11-07-110139_add_gsm_table/up.sql b/migrations/2023-11-07-110139_add_gsm_table/up.sql new file mode 100644 index 000000000000..9dfa68b01af9 --- /dev/null +++ b/migrations/2023-11-07-110139_add_gsm_table/up.sql @@ -0,0 +1,16 @@ +-- Your SQL goes here +-- Tables +CREATE TABLE IF NOT EXISTS gateway_status_map ( + connector VARCHAR(64) NOT NULL, + flow VARCHAR(64) NOT NULL, + sub_flow VARCHAR(64) NOT NULL, + code VARCHAR(255) NOT NULL, + message VARCHAR(1024), + status VARCHAR(64) NOT NULL, + router_error VARCHAR(64), + decision VARCHAR(64) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT now()::TIMESTAMP, + last_modified TIMESTAMP NOT NULL DEFAULT now()::TIMESTAMP, + step_up_possible BOOLEAN NOT NULL DEFAULT FALSE, + PRIMARY KEY (connector, flow, sub_flow, code, message) +);