More tests, documentation and prepare to add other payment methods
This commit is contained in:
parent
4bc914b6df
commit
69bf0b5dff
30
Cargo.lock
generated
30
Cargo.lock
generated
@ -559,9 +559,9 @@ checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.53"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600"
|
||||
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1183,6 +1183,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"config",
|
||||
"fake",
|
||||
@ -3127,11 +3128,11 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.38"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4003,13 +4004,13 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.94"
|
||||
version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
|
||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4219,6 +4220,7 @@ dependencies = [
|
||||
"rand_core",
|
||||
"serde",
|
||||
"sha2",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -4565,6 +4567,12 @@ version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.19"
|
||||
@ -4580,12 +4588,6 @@ version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
|
@ -31,5 +31,7 @@ itertools = { version = "0.10.3" }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
async-trait = { version = "0.1.56" }
|
||||
|
||||
[dev-dependencies]
|
||||
testx = { path = "../../shared/testx" }
|
||||
|
@ -248,7 +248,7 @@ mod test {
|
||||
|
||||
#[actix::test]
|
||||
async fn full_check() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
// account
|
||||
let account = test_create_account(&mut t).await;
|
||||
|
@ -268,7 +268,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create_account() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let login: String = fake::faker::internet::en::Username().fake();
|
||||
let email: String = fake::faker::internet::en::FreeEmail().fake();
|
||||
@ -302,7 +302,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn all_accounts() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_create_account(&mut t, None, None, None).await;
|
||||
test_create_account(&mut t, None, None, None).await;
|
||||
@ -316,7 +316,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn update_account_without_pass() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let original_login: String = fake::faker::internet::en::Username().fake();
|
||||
let original_email: String = fake::faker::internet::en::FreeEmail().fake();
|
||||
@ -364,7 +364,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn update_account_with_pass() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let original_login: String = fake::faker::internet::en::Username().fake();
|
||||
let original_email: String = fake::faker::internet::en::FreeEmail().fake();
|
||||
@ -413,7 +413,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn find() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_create_account(&mut t, None, None, None).await;
|
||||
|
||||
@ -432,7 +432,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn find_identity_email() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_create_account(&mut t, None, None, None).await;
|
||||
|
||||
@ -452,7 +452,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn find_identity_login() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_create_account(&mut t, None, None, None).await;
|
||||
|
||||
|
@ -72,6 +72,16 @@ macro_rules! db_async_handler {
|
||||
Box::pin(async { $inner_async(msg, pool).await }.into_actor(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl $crate::Queue<$msg> for crate::Transaction {
|
||||
type Result = $res;
|
||||
|
||||
async fn handle(&mut self, msg: $msg) -> Result<Self::Result> {
|
||||
let t = &mut self.t;
|
||||
$async(msg, t).await
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -159,6 +169,7 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub struct Database {
|
||||
pool: PgPool,
|
||||
config: SharedAppConfig,
|
||||
}
|
||||
|
||||
pub type SharedDatabase = actix::Addr<Database>;
|
||||
@ -167,6 +178,7 @@ impl Clone for Database {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
pool: self.pool.clone(),
|
||||
config: self.config.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -178,18 +190,55 @@ impl Database {
|
||||
tracing::error!("Failed to connect to database. {e:?}");
|
||||
std::process::exit(1);
|
||||
});
|
||||
Database { pool }
|
||||
Self { pool, config }
|
||||
}
|
||||
|
||||
pub fn pool(&self) -> &PgPool {
|
||||
&self.pool
|
||||
}
|
||||
|
||||
pub async fn begin(&self) -> sqlx::Result<Transaction> {
|
||||
Ok(Transaction {
|
||||
t: self.pool.begin().await?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for Database {
|
||||
type Context = Context<Self>;
|
||||
}
|
||||
|
||||
pub struct Transaction {
|
||||
t: sqlx::Transaction<'static, sqlx::Postgres>,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub async fn commit(self) -> Result<()> {
|
||||
self.t.commit().await.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
dbg!(e);
|
||||
Error::TransactionFailed
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn rollback(self) -> Result<()> {
|
||||
self.t.rollback().await.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
dbg!(e);
|
||||
Error::TransactionFailed
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Queue<Msg> {
|
||||
type Result;
|
||||
|
||||
async fn handle(&mut self, msg: Msg) -> Result<Self::Result>;
|
||||
}
|
||||
|
||||
/// Multi-query load for large amount of records to read
|
||||
///
|
||||
/// Examples
|
||||
@ -203,7 +252,11 @@ impl Actor for Database {
|
||||
/// &mut t,
|
||||
/// "SELECT id, name FROM products WHERE ",
|
||||
/// " id = "
|
||||
/// );
|
||||
/// )
|
||||
/// // order by id
|
||||
/// .with_sorting("id ASC")
|
||||
/// // 100 rows per db query
|
||||
/// .with_size(100);
|
||||
/// let products: Vec<model::Product> = multi.load(4, vec![1, 2, 3, 4].into_iter(), |_| Error::All.into())
|
||||
/// .await.unwrap();
|
||||
/// t.commit().await.unwrap();
|
||||
@ -214,6 +267,7 @@ pub struct MultiLoad<'transaction, 'transaction2, 'header, 'condition, T> {
|
||||
header: &'header str,
|
||||
condition: &'condition str,
|
||||
sort: Option<String>,
|
||||
size: usize,
|
||||
__phantom: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
@ -232,6 +286,7 @@ where
|
||||
header,
|
||||
condition,
|
||||
sort: None,
|
||||
size: 20,
|
||||
__phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -241,6 +296,11 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_size(mut self, size: usize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn load<'query, Error, Ids>(
|
||||
&mut self,
|
||||
len: usize,
|
||||
@ -252,12 +312,14 @@ where
|
||||
Error: Fn(sqlx::Error) -> crate::Error,
|
||||
{
|
||||
let mut res = Vec::new();
|
||||
let size = self.size;
|
||||
|
||||
for ids in items.fold(
|
||||
Vec::<Vec<model::RecordId>>::with_capacity(len),
|
||||
|mut v, id| {
|
||||
if matches!(v.last().map(|v| v.len()), Some(20) | None) {
|
||||
v.push(Vec::with_capacity(20));
|
||||
let last_len = v.last().map(|v| v.len());
|
||||
if last_len == Some(size) || last_len == None {
|
||||
v.push(Vec::with_capacity(size));
|
||||
}
|
||||
v.last_mut().unwrap().push(id);
|
||||
v
|
||||
|
@ -184,7 +184,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_order_address(&mut t).await;
|
||||
|
||||
@ -193,7 +193,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn update() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let original = test_order_address(&mut t).await;
|
||||
let updated = super::update_order_address(
|
||||
@ -231,7 +231,7 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn order_address() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_order_address(&mut t).await;
|
||||
|
||||
|
@ -284,7 +284,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_order_item(&mut t, None).await;
|
||||
|
||||
@ -293,7 +293,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn order_items() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let order1 = test_order(&mut t).await;
|
||||
test_order_item(&mut t, Some(order1.id)).await;
|
||||
|
@ -404,7 +404,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn empty_order_without_cart() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let address_id = test_order_address(&mut t).await.id;
|
||||
test_empty_order_without_cart(&mut t, None, address_id).await;
|
||||
@ -414,7 +414,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn empty_order_with_cart() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let buyer_id = test_account(&mut t).await.id;
|
||||
let address_id = test_order_address(&mut t).await.id;
|
||||
@ -440,7 +440,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn non_empty_order_with_cart() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let buyer_id = test_account(&mut t).await.id;
|
||||
let address_id = test_order_address(&mut t).await.id;
|
||||
@ -477,7 +477,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn non_empty_order_without_cart() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let buyer_id = test_account(&mut t).await.id;
|
||||
let address_id = test_order_address(&mut t).await.id;
|
||||
@ -510,7 +510,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn update_by_ext() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let address_id = test_order_address(&mut t).await.id;
|
||||
let original = test_empty_order_without_cart(&mut t, None, address_id).await;
|
||||
|
@ -192,7 +192,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create_photo() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_photo(&mut t, None, None, None).await;
|
||||
|
||||
@ -201,7 +201,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn all() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let p1 = test_photo(&mut t, None, None, None).await;
|
||||
let p2 = test_photo(&mut t, None, None, None).await;
|
||||
@ -215,7 +215,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn products_photos() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let product_1 = test_product(&mut t).await;
|
||||
let p1 = test_product_photo(&mut t, product_1.id).await;
|
||||
|
@ -165,7 +165,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create_photo() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_product_photo(&mut t).await;
|
||||
|
||||
@ -174,7 +174,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn delete() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let p1 = test_product_photo(&mut t).await;
|
||||
let p2 = test_product_photo(&mut t).await;
|
||||
@ -192,7 +192,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_product_photo(&mut t).await;
|
||||
|
||||
|
@ -360,7 +360,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_product(&mut t, None, None, None, None, None, None).await;
|
||||
|
||||
@ -369,7 +369,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn all() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let p1 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let p2 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
@ -383,7 +383,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn find() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let p1 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let p2 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
@ -401,7 +401,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn update() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let original = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let updated = update_product(
|
||||
|
@ -541,7 +541,7 @@ WHERE buyer_id = $1
|
||||
|
||||
#[actix::test]
|
||||
async fn create() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_shopping_cart_item(&mut t, None, None).await;
|
||||
|
||||
@ -550,7 +550,7 @@ WHERE buyer_id = $1
|
||||
|
||||
#[actix::test]
|
||||
async fn all() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account_id = test_account(&mut t, None, None, None).await.id;
|
||||
|
||||
@ -581,7 +581,7 @@ WHERE buyer_id = $1
|
||||
|
||||
#[actix::test]
|
||||
async fn account_cart_with_cart_id() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account_id = test_account(&mut t, None, None, None).await.id;
|
||||
|
||||
@ -618,7 +618,7 @@ WHERE buyer_id = $1
|
||||
|
||||
#[actix::test]
|
||||
async fn account_cart_without_cart_id() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account_id = test_account(&mut t, None, None, None).await.id;
|
||||
|
||||
@ -655,7 +655,7 @@ WHERE buyer_id = $1
|
||||
|
||||
#[actix::test]
|
||||
async fn update() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
let account_id = test_account(&mut t, None, None, None).await.id;
|
||||
let cart1 = test_shopping_cart(&mut t, Some(account_id), ShoppingCartState::Closed).await;
|
||||
let item = test_shopping_cart_item(&mut t, Some(cart1.id), None).await;
|
||||
|
@ -345,7 +345,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create_shopping_cart() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_account(&mut t, None, None, None).await;
|
||||
|
||||
@ -364,7 +364,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn update_shopping_cart() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_account(&mut t, None, None, None).await;
|
||||
|
||||
@ -399,7 +399,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn without_cart_ensure_shopping_cart() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_account(&mut t, None, None, None).await;
|
||||
|
||||
@ -429,7 +429,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn with_inactive_cart_ensure_shopping_cart() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_account(&mut t, None, None, None).await;
|
||||
|
||||
|
@ -267,7 +267,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create_stock() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_stock(&mut t, None, None, None).await;
|
||||
|
||||
@ -276,7 +276,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn products_stock() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let first = test_stock(&mut t, None, None, None).await;
|
||||
let second = test_stock(&mut t, None, None, None).await;
|
||||
@ -296,7 +296,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn all_stocks() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let first = test_stock(&mut t, None, None, None).await;
|
||||
let second = test_stock(&mut t, None, None, None).await;
|
||||
@ -309,7 +309,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn delete_stock() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let first = test_stock(&mut t, None, None, None).await;
|
||||
let second = test_stock(&mut t, None, None, None).await;
|
||||
@ -332,7 +332,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn update_stock() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let first = test_stock(&mut t, None, None, None).await;
|
||||
let second = test_stock(&mut t, None, None, None).await;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use actix::Message;
|
||||
use model::{AccountId, Audience, Token};
|
||||
use model::{AccountId, Audience, Token, TokenId};
|
||||
|
||||
use crate::{db_async_handler, Result};
|
||||
|
||||
@ -138,6 +138,31 @@ RETURNING id, customer_id, role, issuer, subject, audience, expiration_time, not
|
||||
crate::Error::Token(Error::Create)
|
||||
})
|
||||
}
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "Result<Option<Token>>")]
|
||||
pub struct DeleteToken {
|
||||
pub token_id: TokenId,
|
||||
}
|
||||
|
||||
db_async_handler!(DeleteToken, delete_token, Option<Token>, inner_delete_token);
|
||||
|
||||
pub(crate) async fn delete_token(
|
||||
msg: DeleteToken,
|
||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Option<Token>> {
|
||||
sqlx::query_as(r#"
|
||||
DELETE FROM tokens
|
||||
WHERE id = $1
|
||||
RETURNING id, customer_id, role, issuer, subject, audience, expiration_time, not_before_time, issued_at_time, jwt_id
|
||||
"#)
|
||||
.bind(msg.token_id)
|
||||
.fetch_optional(t)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
crate::Error::Token(Error::Jti)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -205,7 +230,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create_token() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
super::create_token(
|
||||
CreateToken {
|
||||
@ -222,7 +247,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn create_extended_token() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_create_account(&mut t).await;
|
||||
|
||||
@ -231,7 +256,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn find_by_jti() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let original = test_create_token_extended(&mut t, None, None, None, None, None).await;
|
||||
|
||||
@ -250,7 +275,7 @@ mod tests {
|
||||
|
||||
#[actix::test]
|
||||
async fn find_by_jti_expired() {
|
||||
testx::db_t!(t);
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let original = test_create_token_extended(
|
||||
&mut t,
|
||||
|
@ -217,7 +217,7 @@ pub(crate) async fn request_payment(
|
||||
},
|
||||
Error::UnavailableShoppingCart
|
||||
);
|
||||
let mut items =
|
||||
let items =
|
||||
cart_items
|
||||
.iter()
|
||||
.fold(HashMap::with_capacity(cart_items.len()), |mut agg, item| {
|
||||
@ -233,60 +233,34 @@ pub(crate) async fn request_payment(
|
||||
Error::UnavailableShoppingCart
|
||||
);
|
||||
|
||||
// let payment_required = {
|
||||
// let l = config.lock();
|
||||
// l.payment().optional_payment() != false
|
||||
// };
|
||||
let redirect_uri = {
|
||||
let pay_u::res::CreateOrder {
|
||||
status: _,
|
||||
redirect_uri,
|
||||
order_id,
|
||||
ext_order_id: _,
|
||||
} = {
|
||||
client
|
||||
.lock()
|
||||
.create_order(
|
||||
pay_u::req::OrderCreate::build(
|
||||
msg.buyer.into(),
|
||||
msg.customer_ip,
|
||||
msg.currency,
|
||||
format!("Order #{}", db_order.id),
|
||||
)
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::InvalidOrder
|
||||
})?
|
||||
.with_products(cart_products.into_iter().map(|p| {
|
||||
pay_u::Product::new(
|
||||
p.name.to_string(),
|
||||
**p.price,
|
||||
items
|
||||
.remove(&p.id)
|
||||
.map(|(quantity, _)| **quantity as u32)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}))
|
||||
.with_ext_order_id(db_order.order_ext_id.to_string())
|
||||
.with_notify_url(notify_uri)
|
||||
.with_continue_url(continue_uri),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})?
|
||||
};
|
||||
let redirect_uri = match msg.payment_method {
|
||||
PaymentMethod::PayU => {
|
||||
let (redirect_uri, ext_order_id) = pay_u_adapter::CreatePayment {
|
||||
client,
|
||||
buyer: msg.buyer,
|
||||
customer_ip: msg.customer_ip,
|
||||
currency: msg.currency,
|
||||
description: format!("Order #{}", db_order.id),
|
||||
cart_products,
|
||||
items,
|
||||
order_ext_id: db_order.order_ext_id.to_string(),
|
||||
notify_uri,
|
||||
continue_uri,
|
||||
}
|
||||
.create_payment()
|
||||
.await?;
|
||||
|
||||
query_db!(
|
||||
db,
|
||||
database_manager::SetOrderServiceId {
|
||||
service_order_id: order_id.0,
|
||||
id: db_order.id,
|
||||
},
|
||||
Error::CreateOrder
|
||||
);
|
||||
redirect_uri
|
||||
query_db!(
|
||||
db,
|
||||
database_manager::SetOrderServiceId {
|
||||
service_order_id: ext_order_id.into_inner(),
|
||||
id: db_order.id,
|
||||
},
|
||||
Error::CreateOrder
|
||||
);
|
||||
redirect_uri
|
||||
}
|
||||
PaymentMethod::PaymentOnTheSpot => unreachable!(),
|
||||
};
|
||||
|
||||
let order_items = query_db!(
|
||||
|
@ -0,0 +1,69 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use model::*;
|
||||
|
||||
use crate::{Buyer, Error, PayUClient, Result};
|
||||
|
||||
pub struct CreatePayment {
|
||||
pub client: PayUClient,
|
||||
pub buyer: Buyer,
|
||||
pub customer_ip: String,
|
||||
pub currency: String,
|
||||
pub description: String,
|
||||
pub cart_products: Vec<model::Product>,
|
||||
pub items: HashMap<ProductId, (model::Quantity, QuantityUnit)>,
|
||||
pub order_ext_id: String,
|
||||
pub notify_uri: String,
|
||||
pub continue_uri: String,
|
||||
}
|
||||
|
||||
impl CreatePayment {
|
||||
pub(crate) async fn create_payment(self) -> Result<(String, ExtOrderId)> {
|
||||
let CreatePayment {
|
||||
client,
|
||||
buyer,
|
||||
customer_ip,
|
||||
currency,
|
||||
description,
|
||||
cart_products,
|
||||
mut items,
|
||||
order_ext_id,
|
||||
notify_uri,
|
||||
continue_uri,
|
||||
} = self;
|
||||
|
||||
let pay_u::res::CreateOrder {
|
||||
status: _,
|
||||
redirect_uri,
|
||||
order_id,
|
||||
ext_order_id: _,
|
||||
} = client
|
||||
.lock()
|
||||
.create_order(
|
||||
pay_u::req::OrderCreate::build(buyer.into(), customer_ip, currency, description)
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::InvalidOrder
|
||||
})?
|
||||
.with_products(cart_products.into_iter().map(|p| {
|
||||
pay_u::Product::new(
|
||||
p.name.to_string(),
|
||||
**p.price,
|
||||
items
|
||||
.remove(&p.id)
|
||||
.map(|(quantity, _)| **quantity as u32)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}))
|
||||
.with_ext_order_id(order_ext_id)
|
||||
.with_notify_url(notify_uri)
|
||||
.with_continue_url(continue_uri),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::PaymentFailed
|
||||
})?;
|
||||
Ok((redirect_uri, ExtOrderId::new(order_id.0)))
|
||||
}
|
||||
}
|
@ -35,3 +35,6 @@ sha2 = { version = "0.10", features = [] }
|
||||
tokio = { version = "1.17", features = ["full"] }
|
||||
futures = { version = "0.3", features = [] }
|
||||
futures-util = { version = "0.3", features = [] }
|
||||
|
||||
[dev-dependencies]
|
||||
testx = { path = "../../shared/testx" }
|
||||
|
@ -1,3 +1,75 @@
|
||||
//! Tokens management system.
|
||||
//! It's responsible for creating and validating all tokens.
|
||||
//!
|
||||
//! Application flow goes like this:
|
||||
//!
|
||||
//! ```ascii
|
||||
//! Client API TokenManager Database
|
||||
//!
|
||||
//! │ │ │ │
|
||||
//! │ │ │ │
|
||||
//! │ │ │ ┌───────────────►│
|
||||
//! ├────────────────►├──────────────────►├──────►│ │
|
||||
//! │ Sign In │ CreatePair │ └───────────────►│
|
||||
//! │ │ │ Create │
|
||||
//! │ │ │ * AccessToken │
|
||||
//! │ │ │ * RefreshToken │
|
||||
//! │ │ │ │
|
||||
//! │ │ │ │
|
||||
//!
|
||||
//! │ │ │ │
|
||||
//! ├────────────────►├──────────────────►├───────────────────────►│
|
||||
//! │ Validate token │ ValidateToken │ Load token │
|
||||
//! │ │ (string) │◄───────────────────────┤
|
||||
//! │ │ │ │
|
||||
//! │ │ Is Valid? │
|
||||
//! │ │ │ │
|
||||
//! │ │◄──────────────── YES │
|
||||
//! │ │ AccessToken │ │
|
||||
//! │ │ │ │
|
||||
//! │ │ │ │
|
||||
//! │ │◄──────────────── NO │
|
||||
//! │ │ Error │ │
|
||||
//!
|
||||
//! │ │ │ │
|
||||
//! │ │ │ │
|
||||
//! │ │ │ ┌───────────────►│
|
||||
//! ├────────────────►├──────────────────►├──────►│ │
|
||||
//! │ Refresh token │ CreatePair │ └───────────────►│
|
||||
//! │ │ │ Create │
|
||||
//! │ │◄──────────────────┤ * AccessToken │
|
||||
//! │ │ Access Token │ * RefreshToken │
|
||||
//! │ │ Refresh Token │ │
|
||||
//! │ │ │ │
|
||||
//! ```
|
||||
//!
|
||||
//! If you need to operate on tokens from API or any other actor you should
|
||||
//! always use this actor and never touch database directly.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use actix::{Actor, Addr};
|
||||
//! use config::SharedAppConfig;
|
||||
//! use database_manager::Database;
|
||||
//! use token_manager::*;
|
||||
//! use model::*;
|
||||
//!
|
||||
//! async fn tokens(db: Addr<Database>, config: SharedAppConfig) {
|
||||
//! let manager = TokenManager::new(config, db);
|
||||
//!
|
||||
//! let manager_addr = manager.start();
|
||||
//!
|
||||
//! let AuthPair { access_token, access_token_string, refresh_token_string, .. } = manager_addr.send(CreatePair {
|
||||
//! customer_id: uuid::Uuid::new_v4(),
|
||||
//! account_id: AccountId::from(0),
|
||||
//! role: Role::Admin
|
||||
//! }).await.unwrap().unwrap();
|
||||
//!
|
||||
//! manager_addr.send(Validate { token: access_token_string }).await.unwrap().unwrap();
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -138,6 +210,22 @@ impl TokenManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates single token, it's mostly used by [CreatePair]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use actix::Addr;
|
||||
/// use model::{AccountId, Role};
|
||||
/// use token_manager::*;
|
||||
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>) {
|
||||
/// match token_manager.send(CreateToken { customer_id: uuid::Uuid::new_v4(), role: Role::Admin, subject: AccountId::from(1), audience: None, exp: None }).await {
|
||||
/// Ok(Ok(pair)) => {}
|
||||
/// Ok(Err(manager_error)) => {}
|
||||
/// Err(actor_error) => {}
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "Result<(Token, AccessTokenString)>")]
|
||||
pub struct CreateToken {
|
||||
@ -256,12 +344,28 @@ pub struct AuthPair {
|
||||
pub refresh_token_string: model::RefreshTokenString,
|
||||
}
|
||||
|
||||
/// Creates access token and refresh token
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use actix::Addr;
|
||||
/// use model::{AccountId, Role};
|
||||
/// use token_manager::CreatePair;
|
||||
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>) {
|
||||
/// match token_manager.send(CreatePair { customer_id: uuid::Uuid::new_v4(), account_id: AccountId::from(0), role: Role::Admin }).await {
|
||||
/// Ok(Ok(pair)) => {}
|
||||
/// Ok(Err(manager_error)) => {}
|
||||
/// Err(actor_error) => {}
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "Result<AuthPair>")]
|
||||
pub struct CreatePair {
|
||||
pub customer_id: uuid::Uuid,
|
||||
pub role: Role,
|
||||
pub id: AccountId,
|
||||
pub account_id: AccountId,
|
||||
}
|
||||
|
||||
token_async_handler!(CreatePair, create_pair, AuthPair);
|
||||
@ -276,7 +380,7 @@ pub(crate) async fn create_pair(
|
||||
CreateToken {
|
||||
customer_id: msg.customer_id,
|
||||
role: msg.role,
|
||||
subject: msg.id,
|
||||
subject: msg.account_id,
|
||||
audience: Some(model::Audience::Web),
|
||||
exp: None
|
||||
},
|
||||
@ -287,7 +391,7 @@ pub(crate) async fn create_pair(
|
||||
CreateToken {
|
||||
customer_id: msg.customer_id,
|
||||
role: msg.role,
|
||||
subject: msg.id,
|
||||
subject: msg.account_id,
|
||||
audience: Some(model::Audience::Web),
|
||||
exp: Some((chrono::Utc::now() + chrono::Duration::days(31)).naive_utc())
|
||||
},
|
||||
@ -305,6 +409,22 @@ pub(crate) async fn create_pair(
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if token is still valid
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use actix::Addr;
|
||||
/// use model::{AccessTokenString, AccountId, Role};
|
||||
/// use token_manager::{CreatePair, Validate};
|
||||
/// async fn create_pair(token_manager: Addr<token_manager::TokenManager>, token: AccessTokenString) {
|
||||
/// match token_manager.send(Validate { token }).await {
|
||||
/// Ok(Ok(pair)) => {}
|
||||
/// Ok(Err(manager_error)) => {}
|
||||
/// Err(actor_error) => {}
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "Result<Token>")]
|
||||
pub struct Validate {
|
||||
@ -349,30 +469,14 @@ pub(crate) async fn validate(
|
||||
return Err(Error::Validate);
|
||||
}
|
||||
|
||||
if !validate_pair(&claims, "cti", token.customer_id, validate_uuid) {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
if !validate_pair(&claims, "arl", token.role, |left, right| right == left) {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
if !validate_pair(&claims, "iss", &token.issuer, |left, right| right == left) {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
if !validate_pair(&claims, "sub", token.subject, validate_num) {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
if !validate_pair(&claims, "aud", token.audience, |left, right| right == left) {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
if !validate_pair(&claims, "exp", &token.expiration_time, validate_time) {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
if !validate_pair(&claims, "nbt", &token.not_before_time, validate_time) {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
if !validate_pair(&claims, "iat", &token.issued_at_time, validate_time) {
|
||||
return Err(Error::Invalid);
|
||||
}
|
||||
validate_pair(&claims, "cti", token.customer_id, validate_uuid)?;
|
||||
validate_pair(&claims, "arl", token.role, eq)?;
|
||||
validate_pair(&claims, "iss", &token.issuer, eq)?;
|
||||
validate_pair(&claims, "sub", token.subject, validate_num)?;
|
||||
validate_pair(&claims, "aud", token.audience, eq)?;
|
||||
validate_pair(&claims, "exp", &token.expiration_time, validate_time)?;
|
||||
validate_pair(&claims, "nbt", &token.not_before_time, validate_time)?;
|
||||
validate_pair(&claims, "iat", &token.issued_at_time, validate_time)?;
|
||||
|
||||
tracing::info!("JWT token valid");
|
||||
Ok(token)
|
||||
@ -383,31 +487,155 @@ fn build_key(secret: String) -> Result<Hmac<Sha256>> {
|
||||
Ok(key) => Ok(key),
|
||||
Err(e) => {
|
||||
tracing::error!("{e:?}");
|
||||
dbg!(e);
|
||||
Err(Error::ValidateInternal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_pair<F, V>(claims: &BTreeMap<String, String>, key: &str, v: V, cmp: F) -> bool
|
||||
#[inline(always)]
|
||||
fn validate_pair<F, V>(
|
||||
claims: &BTreeMap<String, String>,
|
||||
key: &str,
|
||||
v: V,
|
||||
cmp: F,
|
||||
) -> std::result::Result<(), Error>
|
||||
where
|
||||
F: FnOnce(&str, V) -> bool,
|
||||
F: for<'s> FnOnce(V, &'s str) -> bool,
|
||||
V: PartialEq,
|
||||
{
|
||||
claims.get(key).map(|s| cmp(s, v)).unwrap_or_default()
|
||||
claims
|
||||
.get(key)
|
||||
.map(|s| cmp(v, s.as_str()))
|
||||
.unwrap_or_default()
|
||||
.then_some(())
|
||||
.ok_or(Error::Invalid)
|
||||
}
|
||||
|
||||
fn validate_time(left: &str, right: &NaiveDateTime) -> bool {
|
||||
chrono::DateTime::parse_from_str(left, "%+")
|
||||
.map(|t| t.naive_utc() == *right)
|
||||
#[inline(always)]
|
||||
fn eq<V>(value: V, text: &str) -> bool
|
||||
where
|
||||
V: for<'s> PartialEq<&'s str>,
|
||||
{
|
||||
value == text
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn validate_time(left: &NaiveDateTime, right: &str) -> bool {
|
||||
chrono::DateTime::parse_from_str(right, "%+")
|
||||
.map(|t| t.naive_utc() == *left)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn validate_num(left: &str, right: i32) -> bool {
|
||||
left.parse::<i32>().map(|n| n == right).unwrap_or_default()
|
||||
#[inline(always)]
|
||||
fn validate_num(left: i32, right: &str) -> bool {
|
||||
right.parse::<i32>().map(|n| left == n).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn validate_uuid(left: &str, right: uuid::Uuid) -> bool {
|
||||
uuid::Uuid::from_str(left)
|
||||
.map(|u| u == right)
|
||||
#[inline(always)]
|
||||
fn validate_uuid(left: uuid::Uuid, right: &str) -> bool {
|
||||
uuid::Uuid::from_str(right)
|
||||
.map(|u| u == left)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix::Actor;
|
||||
use config::UpdateConfig;
|
||||
use database_manager::Database;
|
||||
use model::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct NoOpts;
|
||||
|
||||
impl UpdateConfig for NoOpts {}
|
||||
|
||||
#[actix::test]
|
||||
async fn create_token() {
|
||||
testx::db!(config, db);
|
||||
let db = db.start();
|
||||
|
||||
let (token, _text) = super::create_token(
|
||||
CreateToken {
|
||||
customer_id: Default::default(),
|
||||
role: Role::Admin,
|
||||
subject: AccountId::from(1),
|
||||
audience: None,
|
||||
exp: None,
|
||||
},
|
||||
db.clone(),
|
||||
config,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.send(database_manager::DeleteToken { token_id: token.id })
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn create_pair() {
|
||||
testx::db!(config, db);
|
||||
let db = db.start();
|
||||
|
||||
let AuthPair {
|
||||
access_token,
|
||||
access_token_string: _,
|
||||
refresh_token_string: _,
|
||||
_refresh_token,
|
||||
} = super::create_pair(
|
||||
CreatePair {
|
||||
customer_id: Default::default(),
|
||||
role: Role::Admin,
|
||||
account_id: AccountId::from(0),
|
||||
},
|
||||
db.clone(),
|
||||
config,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.send(database_manager::DeleteToken {
|
||||
token_id: access_token.id,
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
|
||||
db.send(database_manager::DeleteToken {
|
||||
token_id: _refresh_token.id,
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
async fn validate() {
|
||||
testx::db!(config, db);
|
||||
let db = db.start();
|
||||
|
||||
let (token, text) = super::create_token(
|
||||
CreateToken {
|
||||
customer_id: Default::default(),
|
||||
role: Role::Admin,
|
||||
subject: AccountId::from(1),
|
||||
audience: None,
|
||||
exp: None,
|
||||
},
|
||||
db.clone(),
|
||||
config.clone(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
super::validate(Validate { token: text }, db.clone(), config.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
db.send(database_manager::DeleteToken { token_id: token.id })
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ async fn sign_in(
|
||||
.send(token_manager::CreatePair {
|
||||
customer_id: account.customer_id,
|
||||
role: account.role,
|
||||
id: account.id,
|
||||
account_id: account.id,
|
||||
})
|
||||
.await
|
||||
.map_err(|_| routes::Error::CriticalFailure)??;
|
||||
|
@ -52,7 +52,7 @@ async fn refresh_token(
|
||||
.send(token_manager::CreatePair {
|
||||
customer_id: account.customer_id,
|
||||
role: account.role,
|
||||
id: account.id,
|
||||
account_id: account.id,
|
||||
})
|
||||
.await
|
||||
.map_err(|_| routes::Error::CriticalFailure)??;
|
||||
|
@ -163,7 +163,7 @@ pub async fn create_account(
|
||||
.send(token_manager::CreatePair {
|
||||
customer_id: account.customer_id,
|
||||
role: account.role,
|
||||
id: account.id,
|
||||
account_id: account.id,
|
||||
})
|
||||
.await
|
||||
.map_err(|_| routes::Error::CriticalFailure)??;
|
||||
@ -205,7 +205,7 @@ async fn sign_in(
|
||||
.send(token_manager::CreatePair {
|
||||
customer_id: account.customer_id,
|
||||
role: account.role,
|
||||
id: account.id,
|
||||
account_id: account.id,
|
||||
})
|
||||
.await
|
||||
.map_err(|_| routes::Error::CriticalFailure)??;
|
||||
|
@ -3,4 +3,4 @@
|
||||
psql postgres postgres -c "DROP DATABASE bazzar_test"
|
||||
psql postgres postgres -c "CREATE DATABASE bazzar_test"
|
||||
sqlx migrate run --database-url='postgres://postgres@localhost/bazzar_test'
|
||||
cargo test
|
||||
cargo test --all
|
||||
|
@ -901,24 +901,34 @@ pub struct Stock {
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Display, Deref)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Display, Deref)]
|
||||
#[serde(transparent)]
|
||||
pub struct OrderAddressId(RecordId);
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Display, Deref)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Display, Deref)]
|
||||
#[serde(transparent)]
|
||||
pub struct OrderId(RecordId);
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Display, Deref)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Display, Deref)]
|
||||
#[serde(transparent)]
|
||||
pub struct ExtOrderId(String);
|
||||
|
||||
impl ExtOrderId {
|
||||
pub fn new<S: Into<String>>(s: S) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> String {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -1,5 +1,5 @@
|
||||
#[macro_export]
|
||||
macro_rules! db_t {
|
||||
macro_rules! db_t_ref {
|
||||
($t: ident) => {
|
||||
let config = config::default_load(&mut NoOpts);
|
||||
config
|
||||
@ -13,6 +13,19 @@ macro_rules! db_t {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! db {
|
||||
($config: ident, $db: ident) => {
|
||||
let $config = config::default_load(&mut NoOpts);
|
||||
$config
|
||||
.lock()
|
||||
.database_mut()
|
||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||
|
||||
let $db = Database::build($config.clone()).await;
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! db_rollback {
|
||||
($t: expr) => {
|
||||
|
Loading…
Reference in New Issue
Block a user