2022-05-19 16:13:27 +02:00
|
|
|
use crate::{db_async_handler, Result};
|
|
|
|
|
2022-05-22 14:19:11 +02:00
|
|
|
#[derive(Debug, Copy, Clone, serde::Serialize, thiserror::Error)]
|
2022-05-19 16:13:27 +02:00
|
|
|
pub enum Error {
|
|
|
|
#[error("Can't load account addresses")]
|
|
|
|
AccountAddresses,
|
|
|
|
#[error("Failed to save account address")]
|
|
|
|
CreateAccountAddress,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(actix::Message)]
|
2022-05-23 14:11:56 +02:00
|
|
|
#[rtype(result = "Result<Vec<model::AccountAddress>>")]
|
2022-05-19 16:13:27 +02:00
|
|
|
pub struct AccountAddresses {
|
|
|
|
pub account_id: model::AccountId,
|
|
|
|
}
|
|
|
|
|
|
|
|
db_async_handler!(
|
|
|
|
AccountAddresses,
|
|
|
|
account_addresses,
|
2022-05-23 14:11:56 +02:00
|
|
|
Vec<model::AccountAddress>,
|
2022-05-19 16:13:27 +02:00
|
|
|
inner_account_addresses
|
|
|
|
);
|
|
|
|
|
|
|
|
pub(crate) async fn account_addresses(
|
|
|
|
msg: AccountAddresses,
|
|
|
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
2022-05-23 14:11:56 +02:00
|
|
|
) -> Result<Vec<model::AccountAddress>> {
|
2022-05-19 16:13:27 +02:00
|
|
|
sqlx::query_as(
|
|
|
|
r#"
|
2022-05-29 13:36:25 +02:00
|
|
|
SELECT id, name, email, phone, street, city, country, zip, account_id, is_default
|
2022-05-19 16:13:27 +02:00
|
|
|
FROM account_addresses
|
|
|
|
WHERE account_id = $1
|
|
|
|
"#,
|
|
|
|
)
|
|
|
|
.bind(msg.account_id)
|
|
|
|
.fetch_all(pool)
|
|
|
|
.await
|
|
|
|
.map_err(|_| Error::AccountAddresses.into())
|
|
|
|
}
|
2022-05-28 14:03:14 +02:00
|
|
|
|
|
|
|
#[derive(actix::Message)]
|
|
|
|
#[rtype(result = "Result<model::AccountAddress>")]
|
|
|
|
pub struct FindAccountAddress {
|
|
|
|
pub account_id: model::AccountId,
|
|
|
|
pub address_id: model::AddressId,
|
|
|
|
}
|
|
|
|
|
|
|
|
db_async_handler!(
|
|
|
|
FindAccountAddress,
|
|
|
|
find_account_address,
|
|
|
|
model::AccountAddress,
|
|
|
|
inner_find_account_address
|
|
|
|
);
|
|
|
|
|
|
|
|
pub(crate) async fn find_account_address(
|
|
|
|
msg: FindAccountAddress,
|
|
|
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
|
|
) -> Result<model::AccountAddress> {
|
|
|
|
sqlx::query_as(
|
|
|
|
r#"
|
2022-05-29 13:36:25 +02:00
|
|
|
SELECT id, name, email, phone, street, city, country, zip, account_id, is_default
|
2022-05-28 14:03:14 +02:00
|
|
|
FROM account_addresses
|
|
|
|
WHERE account_id = $1 AND id = $2
|
|
|
|
"#,
|
|
|
|
)
|
|
|
|
.bind(msg.account_id)
|
|
|
|
.bind(msg.address_id)
|
|
|
|
.fetch_one(pool)
|
|
|
|
.await
|
|
|
|
.map_err(|_| Error::AccountAddresses.into())
|
|
|
|
}
|
|
|
|
|
|
|
|
/////
|
2022-05-19 16:13:27 +02:00
|
|
|
|
|
|
|
#[derive(actix::Message)]
|
2022-05-23 14:11:56 +02:00
|
|
|
#[rtype(result = "Result<model::AccountAddress>")]
|
|
|
|
pub struct DefaultAccountAddress {
|
|
|
|
pub account_id: model::AccountId,
|
|
|
|
}
|
|
|
|
|
|
|
|
db_async_handler!(
|
|
|
|
DefaultAccountAddress,
|
|
|
|
default_account_address,
|
|
|
|
model::AccountAddress,
|
|
|
|
inner_default_account_address
|
|
|
|
);
|
|
|
|
|
|
|
|
pub(crate) async fn default_account_address(
|
|
|
|
msg: DefaultAccountAddress,
|
|
|
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
|
|
) -> Result<model::AccountAddress> {
|
|
|
|
sqlx::query_as(
|
|
|
|
r#"
|
2022-05-29 13:36:25 +02:00
|
|
|
SELECT id, name, email, phone, street, city, country, zip, account_id, is_default
|
2022-05-23 14:11:56 +02:00
|
|
|
FROM account_addresses
|
|
|
|
WHERE account_id = $1 AND is_default
|
|
|
|
"#,
|
|
|
|
)
|
|
|
|
.bind(msg.account_id)
|
|
|
|
.fetch_one(pool)
|
|
|
|
.await
|
|
|
|
.map_err(|_| Error::AccountAddresses.into())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(actix::Message)]
|
|
|
|
#[rtype(result = "Result<model::AccountAddress>")]
|
2022-05-19 16:13:27 +02:00
|
|
|
pub struct CreateAccountAddress {
|
|
|
|
pub name: model::Name,
|
|
|
|
pub email: model::Email,
|
2022-05-29 13:36:25 +02:00
|
|
|
pub phone: model::Phone,
|
2022-05-19 16:13:27 +02:00
|
|
|
pub street: model::Street,
|
|
|
|
pub city: model::City,
|
|
|
|
pub country: model::Country,
|
|
|
|
pub zip: model::Zip,
|
2022-05-23 14:11:56 +02:00
|
|
|
pub account_id: Option<model::AccountId>,
|
|
|
|
pub is_default: bool,
|
2022-05-19 16:13:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
db_async_handler!(
|
|
|
|
CreateAccountAddress,
|
|
|
|
create_address,
|
2022-05-23 14:11:56 +02:00
|
|
|
model::AccountAddress,
|
2022-05-19 16:13:27 +02:00
|
|
|
inner_create_address
|
|
|
|
);
|
|
|
|
|
|
|
|
pub(crate) async fn create_address(
|
|
|
|
msg: CreateAccountAddress,
|
|
|
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
2022-05-23 14:11:56 +02:00
|
|
|
) -> Result<model::AccountAddress> {
|
2022-05-29 13:36:25 +02:00
|
|
|
if msg.is_default && msg.account_id.is_some() {
|
2022-05-23 14:11:56 +02:00
|
|
|
if let Err(e) = sqlx::query(
|
|
|
|
r#"
|
|
|
|
UPDATE account_addresses
|
|
|
|
SET is_default = FALSE
|
|
|
|
WHERE account_id = $1
|
|
|
|
"#,
|
|
|
|
)
|
|
|
|
.bind(msg.account_id)
|
|
|
|
.fetch_all(&mut *pool)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
log::error!("{}", e);
|
|
|
|
}
|
|
|
|
}
|
2022-05-29 13:36:25 +02:00
|
|
|
|
2022-05-19 16:13:27 +02:00
|
|
|
sqlx::query_as(
|
|
|
|
r#"
|
2022-05-29 13:36:25 +02:00
|
|
|
INSERT INTO account_addresses ( name, email, phone, street, city, country, zip, account_id )
|
|
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
|
|
RETURNING id, name, email, phone, street, city, country, zip, account_id, is_default
|
2022-05-19 16:13:27 +02:00
|
|
|
"#,
|
|
|
|
)
|
|
|
|
.bind(msg.name)
|
|
|
|
.bind(msg.email)
|
2022-05-29 13:36:25 +02:00
|
|
|
.bind(msg.phone)
|
2022-05-19 16:13:27 +02:00
|
|
|
.bind(msg.street)
|
|
|
|
.bind(msg.city)
|
|
|
|
.bind(msg.country)
|
|
|
|
.bind(msg.zip)
|
|
|
|
.bind(msg.account_id)
|
2022-05-29 13:36:25 +02:00
|
|
|
.bind(msg.is_default)
|
2022-05-19 16:13:27 +02:00
|
|
|
.fetch_one(pool)
|
|
|
|
.await
|
|
|
|
.map_err(|_| Error::CreateAccountAddress.into())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(actix::Message)]
|
2022-05-23 14:11:56 +02:00
|
|
|
#[rtype(result = "Result<model::AccountAddress>")]
|
2022-05-19 16:13:27 +02:00
|
|
|
pub struct UpdateAccountAddress {
|
|
|
|
pub id: model::AddressId,
|
|
|
|
pub name: model::Name,
|
|
|
|
pub email: model::Email,
|
2022-05-29 13:36:25 +02:00
|
|
|
pub phone: model::Phone,
|
2022-05-19 16:13:27 +02:00
|
|
|
pub street: model::Street,
|
|
|
|
pub city: model::City,
|
|
|
|
pub country: model::Country,
|
|
|
|
pub zip: model::Zip,
|
|
|
|
pub account_id: model::AccountId,
|
2022-05-23 14:11:56 +02:00
|
|
|
pub is_default: bool,
|
2022-05-19 16:13:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
db_async_handler!(
|
|
|
|
UpdateAccountAddress,
|
|
|
|
update_account_address,
|
2022-05-23 14:11:56 +02:00
|
|
|
model::AccountAddress,
|
2022-05-19 16:13:27 +02:00
|
|
|
inner_update_account_address
|
|
|
|
);
|
|
|
|
|
|
|
|
pub(crate) async fn update_account_address(
|
|
|
|
msg: UpdateAccountAddress,
|
|
|
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
2022-05-23 14:11:56 +02:00
|
|
|
) -> Result<model::AccountAddress> {
|
2022-05-19 16:13:27 +02:00
|
|
|
sqlx::query_as(
|
|
|
|
r#"
|
|
|
|
UPDATE account_addresses
|
2022-05-29 13:36:25 +02:00
|
|
|
SET name = $2, email = $3, street = $4, city = $5, country = $6, zip = $7, account_id = $8, is_default = $9, phone = $10
|
2022-05-19 16:13:27 +02:00
|
|
|
WHERE id = $1
|
2022-05-29 13:36:25 +02:00
|
|
|
RETURNING id, name, email, phone, street, city, country, zip, account_id, is_default
|
2022-05-19 16:13:27 +02:00
|
|
|
"#,
|
|
|
|
)
|
|
|
|
.bind(msg.id)
|
|
|
|
.bind(msg.name)
|
|
|
|
.bind(msg.email)
|
|
|
|
.bind(msg.street)
|
|
|
|
.bind(msg.city)
|
|
|
|
.bind(msg.country)
|
|
|
|
.bind(msg.zip)
|
|
|
|
.bind(msg.account_id)
|
2022-05-23 14:11:56 +02:00
|
|
|
.bind(msg.is_default)
|
2022-05-29 13:36:25 +02:00
|
|
|
.bind(msg.phone)
|
2022-05-19 16:13:27 +02:00
|
|
|
.fetch_one(pool)
|
|
|
|
.await
|
|
|
|
.map_err(|_| Error::CreateAccountAddress.into())
|
|
|
|
}
|
2022-05-29 13:36:25 +02:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use actix::{Actor, Addr};
|
|
|
|
use config::*;
|
|
|
|
use fake::Fake;
|
|
|
|
use model::*;
|
|
|
|
|
|
|
|
use crate::*;
|
|
|
|
|
|
|
|
pub struct NoOpts;
|
|
|
|
|
|
|
|
impl UpdateConfig for NoOpts {}
|
|
|
|
|
|
|
|
async fn test_create_account(db: Addr<Database>) -> FullAccount {
|
|
|
|
let login: String = fake::faker::internet::en::Username().fake();
|
|
|
|
let email: String = fake::faker::internet::en::FreeEmail().fake();
|
|
|
|
let hash: String = fake::faker::internet::en::Password(10..20).fake();
|
|
|
|
|
|
|
|
db.send(CreateAccount {
|
2022-06-04 16:05:18 +02:00
|
|
|
email: Email::new(email),
|
|
|
|
login: Login::new(login),
|
|
|
|
pass_hash: PassHash::new(hash),
|
2022-05-29 13:36:25 +02:00
|
|
|
role: Role::Admin,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[actix::test]
|
|
|
|
async fn full_check() {
|
|
|
|
let config = config::default_load(&mut NoOpts);
|
|
|
|
config
|
|
|
|
.lock()
|
|
|
|
.database_mut()
|
|
|
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
|
|
|
|
2022-06-04 16:05:18 +02:00
|
|
|
let db = Database::build(config).await.start();
|
2022-05-29 13:36:25 +02:00
|
|
|
|
|
|
|
// account
|
|
|
|
let account = test_create_account(db.clone()).await;
|
|
|
|
|
|
|
|
// address
|
|
|
|
let mut address: AccountAddress = {
|
|
|
|
let name: String = fake::faker::name::en::Name().fake();
|
|
|
|
let email: String = fake::faker::internet::en::FreeEmail().fake();
|
|
|
|
let phone: String = fake::faker::phone_number::en::PhoneNumber().fake();
|
|
|
|
let street: String = fake::faker::address::en::StreetName().fake();
|
|
|
|
let city: String = fake::faker::address::en::CityName().fake();
|
|
|
|
let country: String = fake::faker::address::en::CountryName().fake();
|
|
|
|
let zip: String = fake::faker::address::en::ZipCode().fake();
|
|
|
|
let account_id = Some(account.id);
|
|
|
|
let is_default: bool = fake::faker::boolean::en::Boolean(6).fake();
|
|
|
|
|
|
|
|
let address = db
|
|
|
|
.send(CreateAccountAddress {
|
|
|
|
name: model::Name::new(name.clone()),
|
|
|
|
email: model::Email::new(email.clone()),
|
|
|
|
phone: model::Phone::new(phone.clone()),
|
|
|
|
street: model::Street::new(street.clone()),
|
|
|
|
city: model::City::new(city.clone()),
|
|
|
|
country: model::Country::new(country.clone()),
|
|
|
|
zip: model::Zip::new(zip.clone()),
|
|
|
|
account_id: account_id.clone(),
|
|
|
|
is_default: is_default.clone(),
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
address,
|
|
|
|
model::AccountAddress {
|
2022-06-04 16:05:18 +02:00
|
|
|
id: address.id,
|
2022-05-29 13:36:25 +02:00
|
|
|
name: model::Name::new(name.clone()),
|
|
|
|
email: model::Email::new(email.clone()),
|
|
|
|
phone: model::Phone::new(phone.clone()),
|
|
|
|
street: model::Street::new(street.clone()),
|
|
|
|
city: model::City::new(city.clone()),
|
|
|
|
country: model::Country::new(country.clone()),
|
|
|
|
zip: model::Zip::new(zip.clone()),
|
|
|
|
account_id: account.id,
|
|
|
|
is_default,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
address
|
|
|
|
};
|
|
|
|
|
|
|
|
let found = db
|
|
|
|
.send(FindAccountAddress {
|
|
|
|
account_id: account.id,
|
|
|
|
address_id: address.id,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(found, address);
|
|
|
|
|
|
|
|
let changed = db
|
|
|
|
.send(UpdateAccountAddress {
|
|
|
|
id: address.id,
|
|
|
|
name: address.name.clone(),
|
|
|
|
email: address.email.clone(),
|
|
|
|
phone: address.phone.clone(),
|
|
|
|
street: address.street.clone(),
|
|
|
|
city: address.city.clone(),
|
|
|
|
country: address.country.clone(),
|
|
|
|
zip: address.zip.clone(),
|
|
|
|
account_id: address.account_id.clone(),
|
|
|
|
is_default: true,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
address.is_default = true;
|
|
|
|
|
|
|
|
assert_eq!(changed, address);
|
|
|
|
|
|
|
|
let default_address = db
|
|
|
|
.send(DefaultAccountAddress {
|
|
|
|
account_id: account.id,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(default_address, address);
|
|
|
|
}
|
|
|
|
}
|