feat(core): add localization support for unified error messages (#5624)

Co-authored-by: Chikke Srujan <chikke.srujan@Chikke-Srujan-N7WRTY72X7.local>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
chikke srujan
2024-08-21 18:05:44 +05:30
committed by GitHub
parent 1d08c7b932
commit 1f0ee3cae0
23 changed files with 525 additions and 9 deletions

View File

@ -38,6 +38,7 @@ ws2ipdef = "ws2ipdef" # WinSock Extension
ws2tcpip = "ws2tcpip" # WinSock Extension
ZAR = "ZAR" # South African Rand currency code
JOD = "JOD" # Jordan currency code
UE_000 = "UE_000" #default unified error code
[default.extend-words]

View File

@ -5435,6 +5435,8 @@ pub struct PaymentLinkStatusDetails {
pub return_url: String,
pub locale: Option<String>,
pub transaction_details: Option<String>,
pub unified_code: Option<String>,
pub unified_message: Option<String>,
}
#[derive(Clone, Debug, serde::Deserialize, ToSchema, serde::Serialize)]

View File

@ -41,6 +41,7 @@ pub mod refund;
pub mod reverse_lookup;
pub mod role;
pub mod routing_algorithm;
pub mod unified_translations;
#[allow(unused_qualifications)]
pub mod schema;

View File

@ -36,6 +36,7 @@ pub mod refund;
pub mod reverse_lookup;
pub mod role;
pub mod routing_algorithm;
pub mod unified_translations;
pub mod user;
pub mod user_authentication_method;
pub mod user_key_store;

View File

@ -0,0 +1,79 @@
use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};
use error_stack::report;
use crate::{
errors,
query::generics,
schema::unified_translations::dsl,
unified_translations::{UnifiedTranslationsUpdateInternal, *},
PgPooledConn, StorageResult,
};
impl UnifiedTranslationsNew {
pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<UnifiedTranslations> {
generics::generic_insert(conn, self).await
}
}
impl UnifiedTranslations {
pub async fn find_by_unified_code_unified_message_locale(
conn: &PgPooledConn,
unified_code: String,
unified_message: String,
locale: String,
) -> StorageResult<Self> {
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
conn,
dsl::unified_code
.eq(unified_code)
.and(dsl::unified_message.eq(unified_message))
.and(dsl::locale.eq(locale)),
)
.await
}
pub async fn update_by_unified_code_unified_message_locale(
conn: &PgPooledConn,
unified_code: String,
unified_message: String,
locale: String,
data: UnifiedTranslationsUpdate,
) -> StorageResult<Self> {
generics::generic_update_with_results::<
<Self as HasTable>::Table,
UnifiedTranslationsUpdateInternal,
_,
_,
>(
conn,
dsl::unified_code
.eq(unified_code)
.and(dsl::unified_message.eq(unified_message))
.and(dsl::locale.eq(locale)),
data.into(),
)
.await?
.first()
.cloned()
.ok_or_else(|| {
report!(errors::DatabaseError::NotFound)
.attach_printable("Error while updating unified_translations entry")
})
}
pub async fn delete_by_unified_code_unified_message_locale(
conn: &PgPooledConn,
unified_code: String,
unified_message: String,
locale: String,
) -> StorageResult<bool> {
generics::generic_delete::<<Self as HasTable>::Table, _>(
conn,
dsl::unified_code
.eq(unified_code)
.and(dsl::unified_message.eq(unified_message))
.and(dsl::locale.eq(locale)),
)
.await
}
}

View File

@ -1215,6 +1215,24 @@ diesel::table! {
}
}
diesel::table! {
use diesel::sql_types::*;
use crate::enums::diesel_exports::*;
unified_translations (unified_code, unified_message, locale) {
#[max_length = 255]
unified_code -> Varchar,
#[max_length = 1024]
unified_message -> Varchar,
#[max_length = 255]
locale -> Varchar,
#[max_length = 1024]
translation -> Varchar,
created_at -> Timestamp,
last_modified_at -> Timestamp,
}
}
diesel::table! {
use diesel::sql_types::*;
use crate::enums::diesel_exports::*;
@ -1343,6 +1361,7 @@ diesel::allow_tables_to_appear_in_same_query!(
reverse_lookup,
roles,
routing_algorithm,
unified_translations,
user_authentication_methods,
user_key_store,
user_roles,

View File

@ -1197,6 +1197,24 @@ diesel::table! {
}
}
diesel::table! {
use diesel::sql_types::*;
use crate::enums::diesel_exports::*;
unified_translations (unified_code, unified_message, locale) {
#[max_length = 255]
unified_code -> Varchar,
#[max_length = 1024]
unified_message -> Varchar,
#[max_length = 255]
locale -> Varchar,
#[max_length = 1024]
translation -> Varchar,
created_at -> Timestamp,
last_modified_at -> Timestamp,
}
}
diesel::table! {
use diesel::sql_types::*;
use crate::enums::diesel_exports::*;
@ -1326,6 +1344,7 @@ diesel::allow_tables_to_appear_in_same_query!(
reverse_lookup,
roles,
routing_algorithm,
unified_translations,
user_authentication_methods,
user_key_store,
user_roles,

View File

@ -0,0 +1,50 @@
//! Translations
use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable};
use time::PrimitiveDateTime;
use crate::schema::unified_translations;
#[derive(Clone, Debug, Queryable, Selectable, Identifiable)]
#[diesel(table_name = unified_translations, primary_key(unified_code, unified_message, locale), check_for_backend(diesel::pg::Pg))]
pub struct UnifiedTranslations {
pub unified_code: String,
pub unified_message: String,
pub locale: String,
pub translation: String,
pub created_at: PrimitiveDateTime,
pub last_modified_at: PrimitiveDateTime,
}
#[derive(Clone, Debug, Insertable)]
#[diesel(table_name = unified_translations)]
pub struct UnifiedTranslationsNew {
pub unified_code: String,
pub unified_message: String,
pub locale: String,
pub translation: String,
pub created_at: PrimitiveDateTime,
pub last_modified_at: PrimitiveDateTime,
}
#[derive(Clone, Debug, AsChangeset)]
#[diesel(table_name = unified_translations)]
pub struct UnifiedTranslationsUpdateInternal {
pub translation: Option<String>,
pub last_modified_at: PrimitiveDateTime,
}
#[derive(Debug)]
pub struct UnifiedTranslationsUpdate {
pub translation: Option<String>,
}
impl From<UnifiedTranslationsUpdate> for UnifiedTranslationsUpdateInternal {
fn from(value: UnifiedTranslationsUpdate) -> Self {
let now = common_utils::date_time::now();
let UnifiedTranslationsUpdate { translation } = value;
Self {
translation,
last_modified_at: now,
}
}
}

View File

@ -130,3 +130,7 @@ pub const CONNECTOR_CREDS_TOKEN_TTL: i64 = 900;
//max_amount allowed is 999999999 in minor units
pub const MAX_ALLOWED_AMOUNT: i64 = 999999999;
//payment attempt default unified error code and unified error message
pub const DEFAULT_UNIFIED_ERROR_CODE: &str = "UE_000";
pub const DEFAULT_UNIFIED_ERROR_MESSAGE: &str = "Something went wrong";

View File

@ -11,7 +11,7 @@ use common_utils::{
DEFAULT_PRODUCT_IMG, DEFAULT_SDK_LAYOUT, DEFAULT_SESSION_EXPIRY,
DEFAULT_TRANSACTION_DETAILS,
},
ext_traits::{OptionExt, ValueExt},
ext_traits::{AsyncExt, OptionExt, ValueExt},
types::{AmountConvertor, MinorUnit, StringMajorUnitForCore},
};
use error_stack::{report, ResultExt};
@ -21,8 +21,12 @@ use masking::{PeekInterface, Secret};
use router_env::logger;
use time::PrimitiveDateTime;
use super::errors::{self, RouterResult, StorageErrorExt};
use super::{
errors::{self, RouterResult, StorageErrorExt},
payments::helpers,
};
use crate::{
consts,
errors::RouterResponse,
get_payment_link_config_value, get_payment_link_config_value_based_on_priority,
headers::ACCEPT_LANGUAGE,
@ -222,6 +226,8 @@ pub async fn form_payment_link_data(
return_url: return_url.clone(),
locale: locale.clone(),
transaction_details: payment_link_config.transaction_details.clone(),
unified_code: payment_attempt.unified_code,
unified_message: payment_attempt.unified_message,
};
return Ok((
@ -760,6 +766,31 @@ pub async fn get_payment_link_status(
field_name: "return_url",
})?
};
let (unified_code, unified_message) = if let Some((code, message)) = payment_attempt
.unified_code
.as_ref()
.zip(payment_attempt.unified_message.as_ref())
{
(code.to_owned(), message.to_owned())
} else {
(
consts::DEFAULT_UNIFIED_ERROR_CODE.to_owned(),
consts::DEFAULT_UNIFIED_ERROR_MESSAGE.to_owned(),
)
};
let unified_translated_message = locale
.as_ref()
.async_and_then(|locale_str| async {
helpers::get_unified_translation(
&state,
unified_code.to_owned(),
unified_message.to_owned(),
locale_str.to_owned(),
)
.await
})
.await
.or(Some(unified_message));
let payment_details = api_models::payments::PaymentLinkStatusDetails {
amount,
@ -776,6 +807,8 @@ pub async fn get_payment_link_status(
return_url,
locale,
transaction_details: payment_link_config.transaction_details,
unified_code: Some(unified_code),
unified_message: unified_translated_message,
};
let js_script = get_js_script(&PaymentLinkData::PaymentLinkStatusDetails(Box::new(
payment_details,

View File

@ -167,10 +167,12 @@ function renderStatusDetails(paymentDetails) {
case "failed":
statusDetails.imageSource = "https://live.hyperswitch.io/payment-link-assets/failed.png";
statusDetails.status = translations.paymentFailed;
var errorCodeNode = createItem(translations.errorCode, paymentDetails.error_code);
var unifiedErrorCode = paymentDetails.unified_code || paymentDetails.error_code;
var unifiedErrorMessage = paymentDetails.unified_message || paymentDetails.error_message;
var errorCodeNode = createItem(translations.errorCode, unifiedErrorCode);
var errorMessageNode = createItem(
translations.errorMessage,
paymentDetails.error_message
unifiedErrorMessage
);
// @ts-ignore
statusDetails.items.push(errorMessageNode, errorCodeNode);

View File

@ -207,6 +207,8 @@ where
let should_add_task_to_process_tracker = should_add_task_to_process_tracker(&payment_data);
let locale = header_payload.locale.clone();
payment_data = tokenize_in_router_when_confirm_false_or_external_authentication(
state,
&operation,
@ -352,6 +354,7 @@ where
router_data,
&key_store,
merchant_account.storage_scheme,
&locale,
)
.await?;
@ -475,6 +478,7 @@ where
router_data,
&key_store,
merchant_account.storage_scheme,
&locale,
)
.await?;

View File

@ -4701,6 +4701,40 @@ pub async fn get_gsm_record(
.ok()
}
pub async fn get_unified_translation(
state: &SessionState,
unified_code: String,
unified_message: String,
locale: String,
) -> Option<String> {
let get_unified_translation = || async {
state.store.find_translation(
unified_code.clone(),
unified_message.clone(),
locale.clone(),
)
.await
.map_err(|err| {
if err.current_context().is_db_not_found() {
logger::warn!(
"Translation missing for unified_code - {:?}, unified_message - {:?}, locale - {:?}",
unified_code,
unified_message,
locale
);
}
err.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("failed to fetch translation from unified_translations")
})
};
get_unified_translation()
.await
.inspect_err(|err| {
// warn log should suffice here because we are not propagating this error
logger::warn!(get_translation_error=?err, "error fetching unified translations");
})
.ok()
}
pub fn validate_order_details_amount(
order_details: Vec<api_models::payments::OrderDetailsWithAmount>,
amount: i64,

View File

@ -220,6 +220,7 @@ pub trait UpdateTracker<F, D, Req>: Send {
}
#[async_trait]
#[allow(clippy::too_many_arguments)]
pub trait PostUpdateTracker<F, D, R: Send>: Send {
async fn update_tracker<'b>(
&'b self,
@ -229,6 +230,7 @@ pub trait PostUpdateTracker<F, D, R: Send>: Send {
response: types::RouterData<F, R, PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<D>
where
F: 'b + Send + Sync;

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use async_trait::async_trait;
use common_enums::AuthorizationStatus;
use common_utils::{
ext_traits::Encode,
ext_traits::{AsyncExt, Encode},
types::{keymanager::KeyManagerState, MinorUnit},
};
use error_stack::{report, ResultExt};
@ -17,6 +17,7 @@ use tracing_futures::Instrument;
use super::{Operation, PostUpdateTracker};
use crate::{
connector::utils::PaymentResponseRouterData,
consts,
core::{
errors::{self, CustomResult, RouterResult, StorageErrorExt},
mandate, payment_methods,
@ -64,6 +65,7 @@ impl<F: Send + Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthor
>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b,
@ -79,6 +81,7 @@ impl<F: Send + Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthor
router_data,
key_store,
storage_scheme,
locale,
))
.await?;
@ -278,6 +281,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsIncrementalAu
>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
_locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -409,6 +413,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsSyncData> for
router_data: types::RouterData<F, types::PaymentsSyncData, types::PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -420,6 +425,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsSyncData> for
router_data,
key_store,
storage_scheme,
locale,
))
.await
}
@ -461,6 +467,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsSessionData>
router_data: types::RouterData<F, types::PaymentsSessionData, types::PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -472,6 +479,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsSessionData>
router_data,
key_store,
storage_scheme,
locale,
))
.await?;
@ -491,6 +499,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsCaptureData>
router_data: types::RouterData<F, types::PaymentsCaptureData, types::PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -502,6 +511,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsCaptureData>
router_data,
key_store,
storage_scheme,
locale,
))
.await?;
@ -519,6 +529,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsCancelData> f
router_data: types::RouterData<F, types::PaymentsCancelData, types::PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -530,6 +541,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsCancelData> f
router_data,
key_store,
storage_scheme,
locale,
))
.await?;
@ -549,6 +561,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsApproveData>
router_data: types::RouterData<F, types::PaymentsApproveData, types::PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -560,6 +573,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsApproveData>
router_data,
key_store,
storage_scheme,
locale,
))
.await?;
@ -577,6 +591,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsRejectData> f
router_data: types::RouterData<F, types::PaymentsRejectData, types::PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -588,6 +603,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsRejectData> f
router_data,
key_store,
storage_scheme,
locale,
))
.await?;
@ -611,6 +627,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::SetupMandateRequestDa
>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -627,6 +644,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::SetupMandateRequestDa
router_data,
key_store,
storage_scheme,
locale,
))
.await?;
@ -709,6 +727,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::CompleteAuthorizeData
response: types::RouterData<F, types::CompleteAuthorizeData, types::PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>>
where
F: 'b + Send,
@ -720,6 +739,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::CompleteAuthorizeData
response,
key_store,
storage_scheme,
locale,
))
.await
}
@ -757,6 +777,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
router_data: types::RouterData<F, T, types::PaymentsResponseData>,
key_store: &domain::MerchantKeyStore,
storage_scheme: enums::MerchantStorageScheme,
locale: &Option<String>,
) -> RouterResult<PaymentData<F>> {
// Update additional payment data with the payment method response that we received from connector
let additional_payment_method_data =
@ -822,6 +843,34 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
)
.await;
let gsm_unified_code =
option_gsm.as_ref().and_then(|gsm| gsm.unified_code.clone());
let gsm_unified_message = option_gsm.and_then(|gsm| gsm.unified_message);
let (unified_code, unified_message) = if let Some((code, message)) =
gsm_unified_code.as_ref().zip(gsm_unified_message.as_ref())
{
(code.to_owned(), message.to_owned())
} else {
(
consts::DEFAULT_UNIFIED_ERROR_CODE.to_owned(),
consts::DEFAULT_UNIFIED_ERROR_MESSAGE.to_owned(),
)
};
let unified_translated_message = locale
.as_ref()
.async_and_then(|locale_str| async {
payments_helpers::get_unified_translation(
state,
unified_code.to_owned(),
unified_message.to_owned(),
locale_str.to_owned(),
)
.await
})
.await
.or(Some(unified_message));
let status = match err.attempt_status {
// Use the status sent by connector in error_response if it's present
Some(status) => status,
@ -862,8 +911,8 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
.get_amount_capturable(&payment_data, status)
.map(MinorUnit::new),
updated_by: storage_scheme.to_string(),
unified_code: option_gsm.clone().map(|gsm| gsm.unified_code),
unified_message: option_gsm.map(|gsm| gsm.unified_message),
unified_code: Some(Some(unified_code)),
unified_message: Some(unified_translated_message),
connector_transaction_id: err.connector_transaction_id,
payment_method_data: additional_payment_method_data,
authentication_type: auth_update,

View File

@ -32,6 +32,7 @@ pub mod refund;
pub mod reverse_lookup;
pub mod role;
pub mod routing_algorithm;
pub mod unified_translations;
pub mod user;
pub mod user_authentication_method;
pub mod user_key_store;
@ -121,6 +122,7 @@ pub trait StorageInterface:
+ OrganizationInterface
+ routing_algorithm::RoutingAlgorithmInterface
+ gsm::GsmInterface
+ unified_translations::UnifiedTranslationsInterface
+ user_role::UserRoleInterface
+ authorization::AuthorizationInterface
+ user::sample_data::BatchSampleDataInterface

View File

@ -68,6 +68,7 @@ use crate::{
refund::RefundInterface,
reverse_lookup::ReverseLookupInterface,
routing_algorithm::RoutingAlgorithmInterface,
unified_translations::UnifiedTranslationsInterface,
CommonStorageInterface, GlobalStorageInterface, MasterKeyInterface, StorageInterface,
},
services::{authentication, kafka::KafkaProducer, Store},
@ -2613,6 +2614,50 @@ impl GsmInterface for KafkaStore {
}
}
#[async_trait::async_trait]
impl UnifiedTranslationsInterface for KafkaStore {
async fn add_unfied_translation(
&self,
translation: storage::UnifiedTranslationsNew,
) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
self.diesel_store.add_unfied_translation(translation).await
}
async fn find_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
) -> CustomResult<String, errors::StorageError> {
self.diesel_store
.find_translation(unified_code, unified_message, locale)
.await
}
async fn update_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
data: storage::UnifiedTranslationsUpdate,
) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
self.diesel_store
.update_translation(unified_code, unified_message, locale, data)
.await
}
async fn delete_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
) -> CustomResult<bool, errors::StorageError> {
self.diesel_store
.delete_translation(unified_code, unified_message, locale)
.await
}
}
#[async_trait::async_trait]
impl StorageInterface for KafkaStore {
fn get_scheduler_db(&self) -> Box<dyn SchedulerInterface> {

View File

@ -0,0 +1,146 @@
use diesel_models::unified_translations as storage;
use error_stack::report;
use super::MockDb;
use crate::{
connection,
core::errors::{self, CustomResult},
services::Store,
};
#[async_trait::async_trait]
pub trait UnifiedTranslationsInterface {
async fn add_unfied_translation(
&self,
translation: storage::UnifiedTranslationsNew,
) -> CustomResult<storage::UnifiedTranslations, errors::StorageError>;
async fn update_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
data: storage::UnifiedTranslationsUpdate,
) -> CustomResult<storage::UnifiedTranslations, errors::StorageError>;
async fn find_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
) -> CustomResult<String, errors::StorageError>;
async fn delete_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
) -> CustomResult<bool, errors::StorageError>;
}
#[async_trait::async_trait]
impl UnifiedTranslationsInterface for Store {
async fn add_unfied_translation(
&self,
translation: storage::UnifiedTranslationsNew,
) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
let conn = connection::pg_connection_write(self).await?;
translation
.insert(&conn)
.await
.map_err(|error| report!(errors::StorageError::from(error)))
}
async fn update_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
data: storage::UnifiedTranslationsUpdate,
) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
let conn = connection::pg_connection_write(self).await?;
storage::UnifiedTranslations::update_by_unified_code_unified_message_locale(
&conn,
unified_code,
unified_message,
locale,
data,
)
.await
.map_err(|error| report!(errors::StorageError::from(error)))
}
async fn find_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
) -> CustomResult<String, errors::StorageError> {
let conn = connection::pg_connection_read(self).await?;
let translations =
storage::UnifiedTranslations::find_by_unified_code_unified_message_locale(
&conn,
unified_code,
unified_message,
locale,
)
.await
.map_err(|error| report!(errors::StorageError::from(error)))?;
Ok(translations.translation)
}
async fn delete_translation(
&self,
unified_code: String,
unified_message: String,
locale: String,
) -> CustomResult<bool, errors::StorageError> {
let conn = connection::pg_connection_write(self).await?;
storage::UnifiedTranslations::delete_by_unified_code_unified_message_locale(
&conn,
unified_code,
unified_message,
locale,
)
.await
.map_err(|error| report!(errors::StorageError::from(error)))
}
}
#[async_trait::async_trait]
impl UnifiedTranslationsInterface for MockDb {
async fn add_unfied_translation(
&self,
_translation: storage::UnifiedTranslationsNew,
) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
Err(errors::StorageError::MockDbError)?
}
async fn find_translation(
&self,
_unified_code: String,
_unified_message: String,
_locale: String,
) -> CustomResult<String, errors::StorageError> {
Err(errors::StorageError::MockDbError)?
}
async fn update_translation(
&self,
_unified_code: String,
_unified_message: String,
_locale: String,
_data: storage::UnifiedTranslationsUpdate,
) -> CustomResult<storage::UnifiedTranslations, errors::StorageError> {
Err(errors::StorageError::MockDbError)?
}
async fn delete_translation(
&self,
_unified_code: String,
_unified_message: String,
_locale: String,
) -> CustomResult<bool, errors::StorageError> {
Err(errors::StorageError::MockDbError)?
}
}

View File

@ -262,6 +262,12 @@ pub async fn payments_retrieve(
expand_captures: json_payload.expand_captures,
..Default::default()
};
let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
Ok(headers) => headers,
Err(err) => {
return api::log_and_return_error_response(err);
}
};
tracing::Span::current().record("payment_id", path.to_string());
tracing::Span::current().record("flow", flow.to_string());
@ -291,7 +297,7 @@ pub async fn payments_retrieve(
auth_flow,
payments::CallConnectorAction::Trigger,
None,
HeaderPayload::default(),
header_payload.clone(),
)
},
auth::auth_type(

View File

@ -35,6 +35,7 @@ pub mod refund;
pub mod reverse_lookup;
pub mod role;
pub mod routing_algorithm;
pub mod unified_translations;
pub mod user;
pub mod user_authentication_method;
pub mod user_role;
@ -64,7 +65,8 @@ pub use self::{
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::*, user::*, user_authentication_method::*, user_role::*,
routing_algorithm::*, unified_translations::*, user::*, user_authentication_method::*,
user_role::*,
};
use crate::types::api::routing;

View File

@ -0,0 +1,4 @@
pub use diesel_models::unified_translations::{
UnifiedTranslations, UnifiedTranslationsNew, UnifiedTranslationsUpdate,
UnifiedTranslationsUpdateInternal,
};

View File

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE IF EXISTS unified_translations;

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS unified_translations (
unified_code VARCHAR(255) NOT NULL,
unified_message VARCHAR(1024) NOT NULL,
locale VARCHAR(255) NOT NULL ,
translation VARCHAR(1024) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now()::TIMESTAMP,
last_modified_at TIMESTAMP NOT NULL DEFAULT now()::TIMESTAMP,
PRIMARY KEY (unified_code,unified_message,locale)
);