diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 5aac1a07457b..a0346e762a41 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1728,6 +1728,51 @@ pub enum RefundStatus { TransactionFailure, } +#[derive( + Clone, + Copy, + Debug, + Default, + Eq, + Hash, + PartialEq, + strum::Display, + strum::EnumString, + strum::EnumIter, + serde::Serialize, + serde::Deserialize, +)] +#[router_derive::diesel_enum(storage_type = "db_enum")] +#[strum(serialize_all = "snake_case")] +#[serde(rename_all = "snake_case")] +pub enum RelayStatus { + Created, + #[default] + Pending, + Success, + Failure, +} + +#[derive( + Clone, + Copy, + Debug, + Eq, + Hash, + PartialEq, + strum::Display, + strum::EnumString, + strum::EnumIter, + serde::Serialize, + serde::Deserialize, +)] +#[router_derive::diesel_enum(storage_type = "db_enum")] +#[strum(serialize_all = "snake_case")] +#[serde(rename_all = "snake_case")] +pub enum RelayType { + Refund, +} + #[derive( Clone, Copy, diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index 19f3bf0a3823..ec6e91a2ecb0 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -17,7 +17,8 @@ pub mod diesel_exports { DbPaymentMethodIssuerCode as PaymentMethodIssuerCode, DbPaymentSource as PaymentSource, DbPaymentType as PaymentType, DbPayoutStatus as PayoutStatus, DbPayoutType as PayoutType, DbProcessTrackerStatus as ProcessTrackerStatus, DbReconStatus as ReconStatus, - DbRefundStatus as RefundStatus, DbRefundType as RefundType, + DbRefundStatus as RefundStatus, DbRefundType as RefundType, DbRelayStatus as RelayStatus, + DbRelayType as RelayType, DbRequestIncrementalAuthorization as RequestIncrementalAuthorization, DbRoleScope as RoleScope, DbRoutingAlgorithmKind as RoutingAlgorithmKind, DbScaExemptionType as ScaExemptionType, diff --git a/crates/diesel_models/src/lib.rs b/crates/diesel_models/src/lib.rs index cc3dc1361545..ace65aeedce5 100644 --- a/crates/diesel_models/src/lib.rs +++ b/crates/diesel_models/src/lib.rs @@ -13,6 +13,7 @@ pub mod blocklist_fingerprint; pub mod customers; pub mod dispute; pub mod dynamic_routing_stats; +pub mod relay; pub mod enums; pub mod ephemeral_key; pub mod errors; diff --git a/crates/diesel_models/src/query.rs b/crates/diesel_models/src/query.rs index ab044b5c6e69..e676c94d7f67 100644 --- a/crates/diesel_models/src/query.rs +++ b/crates/diesel_models/src/query.rs @@ -42,3 +42,4 @@ pub mod user; pub mod user_authentication_method; pub mod user_key_store; pub mod user_role; +pub mod relay; diff --git a/crates/diesel_models/src/query/relay.rs b/crates/diesel_models/src/query/relay.rs new file mode 100644 index 000000000000..f2b983d80e20 --- /dev/null +++ b/crates/diesel_models/src/query/relay.rs @@ -0,0 +1,50 @@ +use super::generics; +use crate::{ + errors, + relay::{Relay, RelayNew, RelayUpdate, RelayUpdateInternal}, + schema::relay::dsl, + PgPooledConn, StorageResult, +}; +use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; + +impl RelayNew { + pub async fn insert(self, conn: &PgPooledConn) -> StorageResult { + generics::generic_insert(conn, self).await + } +} + +impl Relay { + pub async fn update(self, conn: &PgPooledConn, relay: RelayUpdate) -> StorageResult { + match generics::generic_update_with_unique_predicate_get_result::< + ::Table, + _, + _, + _, + >( + conn, + dsl::id + .eq(self.id.to_owned()) + .and(dsl::merchant_id.eq(self.merchant_id.to_owned())), + RelayUpdateInternal::from(relay), + ) + .await + { + Err(error) => match error.current_context() { + errors::DatabaseError::NoFieldsToUpdate => Ok(self), + _ => Err(error), + }, + result => result, + } + } + + pub async fn find_by_id( + conn: &PgPooledConn, + id: &str, + ) -> StorageResult { + generics::generic_find_one::<::Table, _, _>( + conn, + dsl::id.eq(id.to_owned()), + ) + .await + } +} diff --git a/crates/diesel_models/src/relay.rs b/crates/diesel_models/src/relay.rs new file mode 100644 index 000000000000..c396d88d18ec --- /dev/null +++ b/crates/diesel_models/src/relay.rs @@ -0,0 +1,128 @@ +use crate::{enums as storage_enums, schema::relay}; +use common_utils::pii; +use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; +use time::PrimitiveDateTime; + +#[derive( + Clone, + Debug, + Eq, + Identifiable, + Queryable, + Selectable, + PartialEq, + serde::Serialize, + serde::Deserialize, +)] +#[diesel(table_name = relay)] +pub struct Relay { + pub id: String, + pub connector_resource_id: String, + pub connector_id: common_utils::id_type::MerchantConnectorAccountId, + pub profile_id: common_utils::id_type::ProfileId, + pub merchant_id: common_utils::id_type::MerchantId, + pub relay_type: storage_enums::RelayType, + pub request_data: Option, + pub status: storage_enums::RelayStatus, + pub connector_reference_id: Option, + pub error_code: Option, + pub error_reason: Option, + #[serde(with = "common_utils::custom_serde::iso8601")] + pub created_at: PrimitiveDateTime, + #[serde(with = "common_utils::custom_serde::iso8601")] + pub modified_at: PrimitiveDateTime, + pub response_data: Option, +} + +#[derive( + Clone, + Debug, + Eq, + PartialEq, + Insertable, + router_derive::DebugAsDisplay, + serde::Serialize, + serde::Deserialize, + router_derive::Setter, +)] +#[diesel(table_name = relay)] +pub struct RelayNew { + pub id: String, + pub connector_resource_id: String, + pub connector_id: common_utils::id_type::MerchantConnectorAccountId, + pub profile_id: common_utils::id_type::ProfileId, + pub merchant_id: common_utils::id_type::MerchantId, + pub relay_type: storage_enums::RelayType, + pub request_data: Option, + pub status: storage_enums::RelayStatus, + pub connector_reference_id: Option, + pub error_code: Option, + pub error_reason: Option, + #[serde(with = "common_utils::custom_serde::iso8601")] + pub created_at: PrimitiveDateTime, + #[serde(with = "common_utils::custom_serde::iso8601")] + pub modified_at: PrimitiveDateTime, + pub response_data: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub enum RelayUpdate { + ErrorUpdate { + error_code: String, + error_reason: String, + }, + StatusUpdate { + connector_reference_id: Option, + status: storage_enums::RelayStatus, + }, +} + +#[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay)] +#[table_name = "relay"] +pub struct RelayUpdateInternal { + pub connector_reference_id: Option, + pub status: Option, + pub error_code: Option, + pub error_reason: Option, + pub modified_at: PrimitiveDateTime, +} + +impl RelayUpdateInternal { + pub fn create_relay(self, source: Relay) -> Relay { + Relay { + status: self.status.unwrap_or_default(), + modified_at: self.modified_at, + connector_reference_id: self.connector_reference_id, + error_code: self.error_code, + error_reason: self.error_reason, + ..source + } + } +} + +impl From for RelayUpdateInternal { + fn from(value: RelayUpdate) -> Self { + match value { + RelayUpdate::ErrorUpdate { + error_code, + error_reason, + } => Self { + error_code: Some(error_code), + error_reason: Some(error_reason), + connector_reference_id: None, + status: None, + modified_at: common_utils::date_time::now(), + }, + RelayUpdate::StatusUpdate { + connector_reference_id, + status, + } => Self { + connector_reference_id, + status: Some(status), + error_code: None, + error_reason: None, + modified_at: common_utils::date_time::now(), + }, + } + } +} diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 178f5600542a..39ad4d26dde1 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1233,6 +1233,35 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use crate::enums::diesel_exports::*; + + relay (id) { + #[max_length = 64] + id -> Varchar, + #[max_length = 128] + connector_resource_id -> Varchar, + #[max_length = 64] + connector_id -> Varchar, + #[max_length = 64] + profile_id -> Varchar, + #[max_length = 64] + merchant_id -> Varchar, + relay_type -> RelayType, + request_data -> Nullable, + status -> RelayStatus, + #[max_length = 128] + connector_reference_id -> Nullable, + #[max_length = 64] + error_code -> Nullable, + error_reason -> Nullable, + created_at -> Timestamp, + modified_at -> Timestamp, + response_data -> Nullable, + } +} + diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; @@ -1477,6 +1506,7 @@ diesel::allow_tables_to_appear_in_same_query!( payouts, process_tracker, refund, + relay, reverse_lookup, roles, routing_algorithm, diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs index cc9a1e2f436d..2b083e79287d 100644 --- a/crates/router/src/db.rs +++ b/crates/router/src/db.rs @@ -33,6 +33,7 @@ pub mod refund; pub mod reverse_lookup; pub mod role; pub mod routing_algorithm; +pub mod relay; pub mod unified_translations; pub mod user; pub mod user_authentication_method; diff --git a/crates/router/src/db/relay.rs b/crates/router/src/db/relay.rs new file mode 100644 index 000000000000..df0e88f93302 --- /dev/null +++ b/crates/router/src/db/relay.rs @@ -0,0 +1,67 @@ +use diesel_models; + +use crate::core::errors::{self, CustomResult}; + +#[async_trait::async_trait] +pub trait RelayInterface { + async fn insert_relay( + &self, + new: diesel_models::relay::RelayNew, + ) -> CustomResult; + + async fn update_relay( + &self, + this: diesel_models::relay::Relay, + relay: diesel_models::relay::RelayUpdate, + ) -> CustomResult; + + async fn find_relay_by_id( + &self, + id: &str, + ) -> CustomResult; +} + +mod storage { + use error_stack::report; + + use super::RelayInterface; + use crate::{ + connection, + core::errors::{self, CustomResult}, + services::Store, + }; + + #[async_trait::async_trait] + impl RelayInterface for Store { + async fn insert_relay( + &self, + new: diesel_models::relay::RelayNew, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + new.insert(&conn) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } + + async fn update_relay( + &self, + this: diesel_models::relay::Relay, + relay: diesel_models::relay::RelayUpdate, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + this.update(&conn, relay) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } + + async fn find_relay_by_id( + &self, + id: &str, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + diesel_models::relay::Relay::find_by_id(&conn, id) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } + } +} diff --git a/crates/router/src/types/storage.rs b/crates/router/src/types/storage.rs index 24573548d797..92b40dff0bef 100644 --- a/crates/router/src/types/storage.rs +++ b/crates/router/src/types/storage.rs @@ -67,8 +67,7 @@ pub use self::{ configs::*, customers::*, dashboard_metadata::*, dispute::*, dynamic_routing_stats::*, ephemeral_key::*, events::*, file::*, fraud_check::*, generic_link::*, gsm::*, locker_mock_up::*, mandate::*, merchant_account::*, merchant_connector_account::*, - merchant_key_store::*, payment_link::*, payment_method::*, process_tracker::*, refund::*, - reverse_lookup::*, role::*, routing_algorithm::*, unified_translations::*, user::*, + merchant_key_store::*, payment_link::*, payment_method::*, process_tracker::*, refund::*, reverse_lookup::*, role::*, routing_algorithm::*, unified_translations::*, user::*, user_authentication_method::*, user_role::*, }; use crate::types::api::routing; diff --git a/migrations/2024-12-17-141811_add_relay_table/down.sql b/migrations/2024-12-17-141811_add_relay_table/down.sql new file mode 100644 index 000000000000..4ca7813bdfa9 --- /dev/null +++ b/migrations/2024-12-17-141811_add_relay_table/down.sql @@ -0,0 +1,6 @@ +-- This file should undo anything in `up.sql` +DROP TYPE IF EXISTS "RelayStatus"; + +DROP TYPE IF EXISTS "RelayType"; + +DROP TABLE relay; \ No newline at end of file diff --git a/migrations/2024-12-17-141811_add_relay_table/up.sql b/migrations/2024-12-17-141811_add_relay_table/up.sql new file mode 100644 index 000000000000..74144c006c3a --- /dev/null +++ b/migrations/2024-12-17-141811_add_relay_table/up.sql @@ -0,0 +1,22 @@ +-- Your SQL goes here +CREATE TYPE "RelayStatus" AS ENUM ('created', 'pending', 'failed', 'succeeded'); + +CREATE TYPE "RelayType" AS ENUM ('refund'); + +CREATE TABLE relay ( + id VARCHAR(64) PRIMARY KEY, + connector_resource_id VARCHAR(128) NOT NULL, + connector_id VARCHAR(64) NOT NULL, + profile_id VARCHAR(64) NOT NULL, + merchant_id VARCHAR(64) NOT NULL, + relay_type "RelayType" NOT NULL, + request_data JSONB DEFAULT NULL, + status "RelayStatus" NOT NULL, + connector_reference_id VARCHAR(128), + error_code VARCHAR(64), + error_reason TEXT, + created_at TIMESTAMP NOT NULL DEFAULT now()::TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT now()::TIMESTAMP, + response_data JSONB DEFAULT NULL +); +