diff --git a/crates/diesel_models/src/query/address.rs b/crates/diesel_models/src/query/address.rs index 77de21d2d952..9a4f20942ccd 100644 --- a/crates/diesel_models/src/query/address.rs +++ b/crates/diesel_models/src/query/address.rs @@ -17,6 +17,15 @@ impl AddressNew { } impl Address { + #[instrument(skip(conn))] + pub async fn find_by_address_id<'a>( + conn: &PgPooledConn, + address_id: &str, + ) -> StorageResult { + generics::generic_find_by_id::<::Table, _, _>(conn, address_id.to_owned()) + .await + } + #[instrument(skip(conn))] pub async fn update_by_address_id( conn: &PgPooledConn, diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index dce80f77e36c..f7262346e2ca 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -40,7 +40,7 @@ pub async fn create_customer( customer_data.merchant_id = merchant_id.to_owned(); let key = key_store.key.get_inner().peek(); - let address_id = if let Some(addr) = &customer_data.address { + let address = if let Some(addr) = &customer_data.address { let customer_address: api_models::payments::AddressDetails = addr.clone(); let address = customer_data @@ -53,8 +53,7 @@ pub async fn create_customer( db.insert_address_for_customers(address, &key_store) .await .switch() - .attach_printable("Failed while inserting new address")? - .address_id, + .attach_printable("Failed while inserting new address")?, ) } else { None @@ -81,7 +80,7 @@ pub async fn create_customer( metadata: customer_data.metadata, id: None, connector_customer: None, - address_id, + address_id: address.clone().map(|addr| addr.address_id), created_at: common_utils::date_time::now(), modified_at: common_utils::date_time::now(), }) @@ -107,10 +106,12 @@ pub async fn create_customer( } } }; - let mut customer_response: customers::CustomerResponse = customer.into(); - customer_response.address = customer_data.address; - Ok(services::ApplicationResponse::Json(customer_response)) + let address_details = address.map(api_models::payments::AddressDetails::from); + + Ok(services::ApplicationResponse::Json( + customers::CustomerResponse::from((customer, address_details)), + )) } #[instrument(skip(state))] @@ -129,8 +130,17 @@ pub async fn retrieve_customer( ) .await .switch()?; - - Ok(services::ApplicationResponse::Json(response.into())) + let address = match &response.address_id { + Some(address_id) => Some(api_models::payments::AddressDetails::from( + db.find_address_by_address_id(address_id, &key_store) + .await + .switch()?, + )), + None => None, + }; + Ok(services::ApplicationResponse::Json( + customers::CustomerResponse::from((response, address)), + )) } #[instrument(skip_all)] @@ -294,7 +304,7 @@ pub async fn update_customer( let key = key_store.key.get_inner().peek(); - let address_id = if let Some(addr) = &update_customer.address { + let address = if let Some(addr) = &update_customer.address { match customer.address_id { Some(address_id) => { let customer_address: api_models::payments::AddressDetails = addr.clone(); @@ -303,14 +313,15 @@ pub async fn update_customer( .await .switch() .attach_printable("Failed while encrypting Address while Update")?; - db.update_address(address_id.clone(), update_address, &key_store) - .await - .switch() - .attach_printable(format!( - "Failed while updating address: merchant_id: {}, customer_id: {}", - merchant_account.merchant_id, update_customer.customer_id - ))?; - Some(address_id) + Some( + db.update_address(address_id.clone(), update_address, &key_store) + .await + .switch() + .attach_printable(format!( + "Failed while updating address: merchant_id: {}, customer_id: {}", + merchant_account.merchant_id, update_customer.customer_id + ))?, + ) } None => { let customer_address: api_models::payments::AddressDetails = addr.clone(); @@ -329,13 +340,19 @@ pub async fn update_customer( db.insert_address_for_customers(address, &key_store) .await .switch() - .attach_printable("Failed while inserting new address")? - .address_id, + .attach_printable("Failed while inserting new address")?, ) } } } else { - None + match &customer.address_id { + Some(address_id) => Some( + db.find_address_by_address_id(address_id, &key_store) + .await + .switch()?, + ), + None => None, + } }; let response = db @@ -364,7 +381,7 @@ pub async fn update_customer( metadata: update_customer.metadata, description: update_customer.description, connector_customer: None, - address_id, + address_id: address.clone().map(|addr| addr.address_id), }) } .await @@ -375,9 +392,7 @@ pub async fn update_customer( .await .switch()?; - let mut customer_update_response: customers::CustomerResponse = response.into(); - customer_update_response.address = update_customer.address; Ok(services::ApplicationResponse::Json( - customer_update_response, + customers::CustomerResponse::from((response, update_customer.address)), )) } diff --git a/crates/router/src/db/address.rs b/crates/router/src/db/address.rs index 2205f4555d74..904f91613359 100644 --- a/crates/router/src/db/address.rs +++ b/crates/router/src/db/address.rs @@ -28,6 +28,12 @@ where key_store: &domain::MerchantKeyStore, ) -> CustomResult; + async fn find_address_by_address_id( + &self, + address_id: &str, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult; + async fn insert_address_for_payments( &self, payment_id: &str, @@ -82,6 +88,25 @@ mod storage { }; #[async_trait::async_trait] impl AddressInterface for Store { + async fn find_address_by_address_id( + &self, + address_id: &str, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + storage_types::Address::find_by_address_id(&conn, address_id) + .await + .map_err(Into::into) + .into_report() + .async_and_then(|address| async { + address + .convert(key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + } + async fn find_address_by_merchant_id_payment_id_address_id( &self, merchant_id: &str, @@ -237,6 +262,25 @@ mod storage { }; #[async_trait::async_trait] impl AddressInterface for Store { + async fn find_address_by_address_id( + &self, + address_id: &str, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + storage_types::Address::find_by_address_id(&conn, address_id) + .await + .map_err(Into::into) + .into_report() + .async_and_then(|address| async { + address + .convert(key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + } + async fn find_address_by_merchant_id_payment_id_address_id( &self, merchant_id: &str, @@ -447,6 +491,31 @@ mod storage { #[async_trait::async_trait] impl AddressInterface for MockDb { + async fn find_address_by_address_id( + &self, + address_id: &str, + key_store: &domain::MerchantKeyStore, + ) -> CustomResult { + match self + .addresses + .lock() + .await + .iter() + .find(|address| address.address_id == address_id) + { + Some(address) => address + .clone() + .convert(key_store.key.get_inner()) + .await + .change_context(errors::StorageError::DecryptionError), + None => { + return Err( + errors::StorageError::ValueNotFound("address not found".to_string()).into(), + ) + } + } + } + async fn find_address_by_merchant_id_payment_id_address_id( &self, _merchant_id: &str, diff --git a/crates/router/src/types/api/customers.rs b/crates/router/src/types/api/customers.rs index 6878ea8360f2..2050b4149ef8 100644 --- a/crates/router/src/types/api/customers.rs +++ b/crates/router/src/types/api/customers.rs @@ -2,6 +2,7 @@ use api_models::customers; pub use api_models::customers::{CustomerDeleteResponse, CustomerId, CustomerRequest}; use serde::Serialize; +use super::payments; use crate::{core::errors::RouterResult, newtype, types::domain}; newtype!( @@ -13,8 +14,8 @@ pub(crate) trait CustomerRequestExt: Sized { fn validate(self) -> RouterResult; } -impl From for CustomerResponse { - fn from(cust: domain::Customer) -> Self { +impl From<(domain::Customer, Option)> for CustomerResponse { + fn from((cust, address): (domain::Customer, Option)) -> Self { customers::CustomerResponse { customer_id: cust.customer_id, name: cust.name, @@ -24,7 +25,7 @@ impl From for CustomerResponse { description: cust.description, created_at: cust.created_at, metadata: cust.metadata, - address: None, + address, } .into() } diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index a835ed8ec445..5bdb63814e84 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1,6 +1,6 @@ // use actix_web::HttpMessage; use actix_web::http::header::HeaderMap; -use api_models::enums as api_enums; +use api_models::{enums as api_enums, payments}; use common_utils::{ crypto::Encryptable, ext_traits::{StringExt, ValueExt}, @@ -798,3 +798,19 @@ impl } } } + +impl From for payments::AddressDetails { + fn from(addr: domain::Address) -> Self { + Self { + city: addr.city, + country: addr.country, + line1: addr.line1.map(Encryptable::into_inner), + line2: addr.line2.map(Encryptable::into_inner), + line3: addr.line3.map(Encryptable::into_inner), + zip: addr.zip.map(Encryptable::into_inner), + state: addr.state.map(Encryptable::into_inner), + first_name: addr.first_name.map(Encryptable::into_inner), + last_name: addr.last_name.map(Encryptable::into_inner), + } + } +}