Stock almost done

This commit is contained in:
Adrian Woźniak 2022-11-18 16:31:52 +01:00
parent bc80d1329b
commit e5fb90ae0e
No known key found for this signature in database
GPG Key ID: 0012845A89C7352B
17 changed files with 949 additions and 95 deletions

251
Cargo.lock generated
View File

@ -103,7 +103,7 @@ dependencies = [
"mime",
"percent-encoding",
"pin-project-lite",
"rand",
"rand 0.8.5",
"sha1",
"smallvec",
"tracing",
@ -347,7 +347,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c"
dependencies = [
"autocfg",
"autocfg 1.1.0",
]
[[package]]
@ -367,6 +367,15 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "autocfg"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
dependencies = [
"autocfg 1.1.0",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -585,6 +594,15 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@ -649,7 +667,7 @@ dependencies = [
"base64",
"hmac",
"percent-encoding",
"rand",
"rand 0.8.5",
"sha2",
"subtle",
"time 0.3.16",
@ -732,7 +750,7 @@ version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
dependencies = [
"autocfg",
"autocfg 1.1.0",
"cfg-if 1.0.0",
"crossbeam-utils",
"memoffset",
@ -866,7 +884,7 @@ dependencies = [
"itertools",
"model",
"pretty_env_logger",
"rand",
"rand 0.8.5",
"rumqttc",
"serde",
"sqlx",
@ -1090,10 +1108,23 @@ dependencies = [
"chrono",
"dummy",
"http",
"rand",
"rand 0.8.5",
"uuid 1.2.1",
]
[[package]]
name = "fakeit"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "853c484c6a2a2a7e7d5f0d7863b4a64b90875b60cea1ccab68cc4da21c785656"
dependencies = [
"chrono",
"libmath",
"rand 0.6.5",
"simplerand",
"uuid 0.8.2",
]
[[package]]
name = "fastrand"
version = "1.8.0"
@ -1264,6 +1295,12 @@ dependencies = [
"uuid 1.2.1",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
@ -1726,7 +1763,7 @@ version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"autocfg 1.1.0",
"hashbrown",
"serde",
]
@ -1909,6 +1946,15 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "libmath"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfd3416934a853ae80d5c3b006f632dfcbaf320300c5167e88a469e9ac214502"
dependencies = [
"rand 0.3.23",
]
[[package]]
name = "libz-sys"
version = "1.1.8"
@ -1960,7 +2006,7 @@ version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"autocfg 1.1.0",
"scopeguard",
]
@ -2019,7 +2065,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
"autocfg 1.1.0",
]
[[package]]
@ -2105,8 +2151,8 @@ dependencies = [
"derive_more",
"fake",
"password-hash",
"rand",
"rand_core",
"rand 0.8.5",
"rand_core 0.6.4",
"serde",
"sqlx",
"sqlx-core",
@ -2205,7 +2251,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"autocfg 1.1.0",
"num-integer",
"num-traits",
]
@ -2216,7 +2262,7 @@ version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"autocfg 1.1.0",
"num-traits",
]
@ -2226,7 +2272,7 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
"autocfg 1.1.0",
]
[[package]]
@ -2303,7 +2349,7 @@ version = "0.9.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a"
dependencies = [
"autocfg",
"autocfg 1.1.0",
"cc",
"libc",
"pkg-config",
@ -2325,7 +2371,7 @@ dependencies = [
"lazy_static",
"percent-encoding",
"pin-project",
"rand",
"rand 0.8.5",
"thiserror",
]
@ -2393,7 +2439,7 @@ dependencies = [
"once_cell",
"opentelemetry_api",
"percent-encoding",
"rand",
"rand 0.8.5",
"thiserror",
]
@ -2442,7 +2488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
dependencies = [
"base64ct",
"rand_core",
"rand_core 0.6.4",
"subtle",
]
@ -2626,6 +2672,48 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
dependencies = [
"libc",
"rand 0.4.6",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi 0.3.9",
]
[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
dependencies = [
"autocfg 0.1.8",
"libc",
"rand_chacha 0.1.1",
"rand_core 0.4.2",
"rand_hc",
"rand_isaac",
"rand_jitter",
"rand_os",
"rand_pcg",
"rand_xorshift",
"winapi 0.3.9",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -2633,8 +2721,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
dependencies = [
"autocfg 0.1.8",
"rand_core 0.3.1",
]
[[package]]
@ -2644,9 +2742,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.6.4"
@ -2656,13 +2769,75 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
"libc",
"rand_core 0.4.2",
"winapi 0.3.9",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
dependencies = [
"cloudabi",
"fuchsia-cprng",
"libc",
"rand_core 0.4.2",
"rdrand",
"winapi 0.3.9",
]
[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
dependencies = [
"autocfg 0.1.8",
"rand_core 0.4.2",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rayon"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
dependencies = [
"autocfg",
"autocfg 1.1.0",
"crossbeam-deque",
"either",
"rayon-core",
@ -2680,6 +2855,15 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -3014,7 +3198,7 @@ dependencies = [
"gloo-utils",
"indexmap",
"js-sys",
"rand",
"rand 0.8.5",
"serde",
"serde_json",
"uuid 1.2.1",
@ -3171,13 +3355,22 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803"
[[package]]
name = "simplerand"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70455a0a21cb984df0f86033ca19f37cced497deb779603e7fa7b032f5b8897b"
dependencies = [
"lazy_static",
]
[[package]]
name = "slab"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg",
"autocfg 1.1.0",
]
[[package]]
@ -3297,7 +3490,7 @@ dependencies = [
"once_cell",
"paste",
"percent-encoding",
"rand",
"rand 0.8.5",
"rust_decimal",
"rustls",
"rustls-pemfile 1.0.1",
@ -3365,7 +3558,7 @@ dependencies = [
"db-utils",
"derive_more",
"dotenv",
"fake",
"fakeit",
"futures 0.3.25",
"insta",
"model",
@ -3431,7 +3624,7 @@ dependencies = [
"humantime 2.1.0",
"opentelemetry 0.17.0",
"pin-project",
"rand",
"rand 0.8.5",
"serde",
"static_assertions",
"tarpc-plugins",
@ -3636,7 +3829,7 @@ dependencies = [
"parking_lot",
"password-hash",
"pretty_env_logger",
"rand_core",
"rand_core 0.6.4",
"rumqttc",
"serde",
"sha2",
@ -3653,7 +3846,7 @@ version = "1.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
dependencies = [
"autocfg",
"autocfg 1.1.0",
"bytes",
"libc",
"memchr",

View File

@ -36,8 +36,18 @@ pub enum Error {
DeleteProduct(ProductId),
#[error("Failed to create variant of product {0:?}")]
CreateProductVariant(ProductId),
#[error("Failed to update variant {0:?}")]
UpdateProductVariant(ProductVariantId),
#[error("Failed to create stock of variant {0:?}")]
CreateVariantStock(ProductVariantId),
#[error("Failed to add photo to variant {0:?}")]
AddProductPhoto(ProductVariantId),
#[error("Failed to delete photo for variant {0:?}")]
DeleteProductPhoto(ProductVariantId),
#[error("Failed to create stock for variant {0:?}")]
CreateProductStock(ProductVariantId),
#[error("Failed to update stock for variant {0:?}")]
UpdateProductStock(ProductVariantId),
}
pub mod rpc {

View File

@ -5,6 +5,7 @@ pub mod add_product_photo {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Input {
pub product_variant_id: ProductVariantId,
pub local_path: LocalPath,
pub file_name: FileName,
pub unique_name: UniqueName,

View File

@ -1,3 +1,8 @@
use model::v2::{ProductVariant, ProductVariantId};
use rumqttc::QoS;
use crate::AsyncClient;
pub mod create_product_variant {
use model::v2::*;
@ -29,7 +34,7 @@ pub mod update_product_variant {
pub struct Input {
pub id: ProductVariantId,
pub product_id: ProductId,
pub name: ProductName,
pub name: ProductVariantName,
pub short_description: ProductShortDesc,
pub long_description: ProductLongDesc,
pub price: Price,
@ -62,3 +67,57 @@ pub mod delete_product_variant {
pub type Output = Result<Details, Error>;
}
pub enum Topic {
ProductVariantCreated,
ProductVariantUpdated,
ProductVariantDeleted,
}
impl Topic {
pub fn to_str(&self) -> &str {
match self {
Topic::ProductVariantCreated => "product-variant/created",
Topic::ProductVariantUpdated => "product-variant/updated",
Topic::ProductVariantDeleted => "product-variant/deleted",
}
}
}
impl Into<String> for Topic {
fn into(self) -> String {
self.to_str().into()
}
}
impl AsyncClient {
pub async fn emit_product_variant_created(&self, product: &ProductVariant) {
self.publish_or_log(
Topic::ProductVariantCreated,
QoS::AtLeastOnce,
true,
product,
)
.await
}
pub async fn emit_product_variant_updated(&self, product: &ProductVariant) {
self.publish_or_log(
Topic::ProductVariantUpdated,
QoS::AtLeastOnce,
true,
product,
)
.await
}
pub async fn emit_product_variant_deleted(&self, product: &ProductVariantId) {
self.publish_or_log(
Topic::ProductVariantDeleted,
QoS::AtLeastOnce,
true,
product,
)
.await
}
}

View File

@ -32,6 +32,6 @@ tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
uuid = { version = "1.2.1", features = ['v4'] }
[dev-dependencies]
fake = { version = "2.5.0" }
testx = { path = "../testx" }
insta = { version = "1.21.0" }
fakeit = { version = "1.1.1" }

View File

@ -106,7 +106,7 @@ async fn inner_detailed_products(
let stocks = match dbm.run(&mut *t).await {
Ok(stocks) => stocks,
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
return Err(Error::VariantStocks(
variants.into_iter().map(|p| p.id).collect(),
));
@ -118,7 +118,7 @@ async fn inner_detailed_products(
let photos = match dbm.run(t).await {
Ok(photos) => photos,
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
return Err(Error::VariantPhotos(
variants.into_iter().map(|p| p.id).collect(),
));

View File

@ -15,7 +15,7 @@ macro_rules! begin_t {
($db: ident, $err: expr) => {
match $db.pool().begin().await {
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
return Err($err);
}
Ok(t) => t,
@ -29,7 +29,7 @@ macro_rules! dbm_run {
match $dbm.run($t).await {
Ok(res) => res,
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
return Err($err);
}
}

View File

@ -18,7 +18,7 @@ pub async fn create_product(
match inner_create_product(input, &mut t).await {
Ok(res) => {
if let Err(e) = t.commit().await {
tracing::error!("{}", e);
tracing::warn!("{}", e);
Err(Error::InternalServerError)
} else {
mqtt.emit_product_created(&res.product).await;
@ -26,7 +26,7 @@ pub async fn create_product(
}
}
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
t.rollback().await.ok();
Err(e)
}
@ -47,7 +47,7 @@ async fn inner_create_product(
let product = match dbm.run(&mut *t).await {
Ok(product) => product,
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
return Err(Error::CreateProduct);
}
};
@ -115,7 +115,7 @@ pub async fn update_product(
match res {
Ok(res) => {
t.commit().await.map_err(|e| {
tracing::error!("{}", e);
tracing::warn!("{}", e);
Error::InternalServerError
})?;
@ -126,7 +126,7 @@ pub async fn update_product(
Err(e) => {
tracing::warn!("{}", e);
t.rollback().await.map_err(|e| {
tracing::error!("{}", e);
tracing::warn!("{}", e);
Error::InternalServerError
})?;
Err(e)
@ -167,16 +167,16 @@ pub async fn delete_product(
match res {
Ok((product_id, _maybe_product)) => {
if let Err(e) = t.commit().await {
tracing::error!("{}", e);
tracing::warn!("{}", e);
return Err(Error::InternalServerError);
}
mqtt.emit_product_deleted(&product_id).await;
Ok(delete_product::Details { product_id })
}
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
t.rollback().await.map_err(|e| {
tracing::error!("{}", e);
tracing::warn!("{}", e);
Error::InternalServerError
})?;
Err(Error::DeleteProduct(product_id))

View File

@ -1,23 +1,225 @@
use channels::stocks::{add_product_photo, delete_product_photo};
use channels::stocks::{add_product_photo, delete_product_photo, Error};
use channels::AsyncClient;
use config::SharedAppConfig;
use db_utils::PgT;
use crate::begin_t;
use crate::db::Database;
pub async fn add_product_photo(
_input: add_product_photo::Input,
_db: Database,
input: add_product_photo::Input,
db: Database,
_mqtt: AsyncClient,
_config: SharedAppConfig,
) -> add_product_photo::Output {
todo!()
let id = input.product_variant_id;
let mut t = begin_t!(db, Error::InternalServerError);
let res = inner_add_product_photo(input, &mut t).await;
match res {
Ok(details) => {
if let Err(e) = t.commit().await {
tracing::warn!("{}", e);
Err(Error::AddProductPhoto(id))
} else {
Ok(details)
}
}
Err(e) => {
t.rollback().await.ok();
Err(e)
}
}
}
async fn inner_add_product_photo(
input: add_product_photo::Input,
t: &mut PgT<'_>,
) -> add_product_photo::Output {
let dbm = crate::db::CreatePhoto {
local_path: input.local_path,
file_name: input.file_name,
unique_name: input.unique_name,
};
let photo = match dbm.run(&mut *t).await {
Ok(res) => res,
Err(e) => {
tracing::warn!("{}", e);
return Err(Error::AddProductPhoto(input.product_variant_id));
}
};
let dbm = crate::db::CreateProductPhoto {
product_variant_id: input.product_variant_id,
photo_id: photo.id,
};
match dbm.run(&mut *t).await {
Ok(res) => Ok(add_product_photo::Details {
photo_id: res.id,
product_variant_id: res.product_variant_id,
local_path: photo.local_path,
file_name: photo.file_name,
unique_name: photo.unique_name,
}),
Err(e) => {
tracing::warn!("{}", e);
Err(Error::AddProductPhoto(input.product_variant_id))
}
}
}
pub async fn delete_product_photo(
_input: delete_product_photo::Input,
_db: Database,
input: delete_product_photo::Input,
db: Database,
_mqtt: AsyncClient,
_config: SharedAppConfig,
) -> delete_product_photo::Output {
todo!()
let id = input.product_variant_id;
let mut t = begin_t!(db, Error::InternalServerError);
let res = inner_delete_product_photo(input, &mut t).await;
match res {
Ok(details) => {
if let Err(e) = t.commit().await {
tracing::warn!("{}", e);
Err(Error::DeleteProductPhoto(id))
} else {
Ok(details)
}
}
Err(e) => {
t.rollback().await.ok();
Err(e)
}
}
}
async fn inner_delete_product_photo(
input: delete_product_photo::Input,
t: &mut PgT<'_>,
) -> delete_product_photo::Output {
let dbm = crate::db::DeleteProductPhoto { id: input.photo_id };
match dbm.run(t).await {
Ok(_details) => Ok(delete_product_photo::Details {
photo_id: input.photo_id,
product_variant_id: input.product_variant_id,
}),
Err(e) => {
tracing::warn!("{}", e);
Err(Error::DeleteProductPhoto(input.product_variant_id))
}
}
}
#[cfg(test)]
mod tests {
use channels::stocks::{add_product_photo, delete_product_photo};
use config::UpdateConfig;
use db_utils::PgT;
use model::v2::*;
use crate::actions::product_photo::{inner_add_product_photo, inner_delete_product_photo};
use crate::db::Database;
struct NoOpts;
impl UpdateConfig for NoOpts {}
async fn test_product(t: &mut PgT<'_>) -> Product {
crate::db::CreateProduct {
name: ProductName::new(format!("{}", uuid::Uuid::new_v4())),
category: None,
deliver_days_flag: Days(vec![]),
}
.run(t)
.await
.unwrap()
}
async fn test_product_variant(product_id: ProductId, t: &mut PgT<'_>) -> ProductVariant {
crate::db::CreateProductVariant {
product_id,
name: ProductVariantName::new(format!("{}", uuid::Uuid::new_v4())),
short_description: ProductShortDesc::new(fakeit::words::sentence(4)),
long_description: ProductLongDesc::new(fakeit::words::sentence(16)),
price: Price::from_u32(650),
}
.run(t)
.await
.unwrap()
}
async fn test_photo(t: &mut PgT<'_>) -> Photo {
crate::db::CreatePhoto {
local_path: LocalPath::new(format!("{}", uuid::Uuid::new_v4())),
file_name: FileName::new(format!("{}", uuid::Uuid::new_v4())),
unique_name: UniqueName::new(format!("{}", uuid::Uuid::new_v4())),
}
.run(t)
.await
.unwrap()
}
async fn test_product_photo(
product_variant_id: ProductVariantId,
photo_id: PhotoId,
t: &mut PgT<'_>,
) -> ProductPhoto {
crate::db::CreateProductPhoto {
product_variant_id,
photo_id,
}
.run(t)
.await
.unwrap()
}
#[tokio::test]
async fn add_product_photo() {
testx::db_t_ref!(t);
let product = test_product(&mut t).await;
let variant = test_product_variant(product.id, &mut t).await;
let res = inner_add_product_photo(
add_product_photo::Input {
product_variant_id: variant.id,
local_path: LocalPath::new(format!("{}", uuid::Uuid::new_v4())),
file_name: FileName::new(format!("{}", uuid::Uuid::new_v4())),
unique_name: UniqueName::new(format!("{}", uuid::Uuid::new_v4())),
},
&mut t,
)
.await;
testx::db_rollback!(t);
let _res = res.unwrap();
}
#[tokio::test]
async fn delete_product_photo() {
testx::db_t_ref!(t);
let product = test_product(&mut t).await;
let variant = test_product_variant(product.id, &mut t).await;
let photo = test_photo(&mut t).await;
let product_photo = test_product_photo(variant.id, photo.id, &mut t).await;
let res = inner_delete_product_photo(
delete_product_photo::Input {
photo_id: product_photo.id,
product_variant_id: variant.id,
},
&mut t,
)
.await;
testx::db_rollback!(t);
let res = res.unwrap();
assert_eq!(res.photo_id, product_photo.id);
assert_eq!(res.product_variant_id, variant.id);
}
}

View File

@ -1,23 +1,203 @@
use channels::stocks::{create_product_stock, update_product_stock};
use channels::stocks::{create_product_stock, update_product_stock, Error};
use channels::AsyncClient;
use config::SharedAppConfig;
use db_utils::PgT;
use crate::begin_t;
use crate::db::Database;
pub async fn create_product_stock(
_input: create_product_stock::Input,
_db: Database,
input: create_product_stock::Input,
db: Database,
_mqtt: AsyncClient,
_config: SharedAppConfig,
) -> create_product_stock::Output {
todo!()
let product_variant_id = input.product_variant_id;
let mut t = begin_t!(db, Error::InternalServerError);
let res = inner_create_product_stock(input, &mut t).await;
match res {
Ok(details) => {
if let Err(e) = t.commit().await {
tracing::warn!("{}", e);
Err(Error::CreateProductStock(product_variant_id))
} else {
Ok(details)
}
}
Err(e) => {
if let Err(e) = t.rollback().await {
tracing::warn!("{}", e);
}
Err(e)
}
}
}
async fn inner_create_product_stock(
input: create_product_stock::Input,
t: &mut PgT<'_>,
) -> create_product_stock::Output {
let dbm = crate::db::CreateStock {
product_variant_id: input.product_variant_id,
quantity: input.quantity,
quantity_unit: input.quantity_unit,
};
match dbm.run(t).await {
Ok(product_stock) => Ok(create_product_stock::Details { product_stock }),
Err(e) => {
tracing::warn!("{}", e);
Err(Error::CreateVariantStock(input.product_variant_id))
}
}
}
pub async fn update_product_stock(
_input: update_product_stock::Input,
_db: Database,
input: update_product_stock::Input,
db: Database,
_mqtt: AsyncClient,
_config: SharedAppConfig,
) -> update_product_stock::Output {
todo!()
let product_variant_id = input.product_variant_id;
let mut t = begin_t!(db, Error::InternalServerError);
let res = inner_update_product_stock(input, &mut t).await;
match res {
Ok(details) => {
if let Err(e) = t.commit().await {
tracing::warn!("{}", e);
Err(Error::UpdateProductStock(product_variant_id))
} else {
Ok(details)
}
}
Err(e) => {
if let Err(e) = t.rollback().await {
tracing::warn!("{}", e);
}
Err(e)
}
}
}
async fn inner_update_product_stock(
input: update_product_stock::Input,
t: &mut PgT<'_>,
) -> update_product_stock::Output {
let dbm = crate::db::UpdateStock {
id: input.id,
product_variant_id: input.product_variant_id,
quantity: input.quantity,
quantity_unit: input.quantity_unit,
};
match dbm.run(t).await {
Ok(product_stock) => Ok(update_product_stock::Details { product_stock }),
Err(e) => {
tracing::warn!("{}", e);
Err(Error::CreateVariantStock(input.product_variant_id))
}
}
}
#[cfg(test)]
mod tests {
use channels::stocks::{
add_product_photo, create_product_stock, delete_product_photo, update_product_stock,
};
use config::UpdateConfig;
use db_utils::PgT;
use model::v2::*;
use crate::actions::product_stock::{inner_create_product_stock, inner_update_product_stock};
use crate::db::Database;
struct NoOpts;
impl UpdateConfig for NoOpts {}
async fn test_product(t: &mut PgT<'_>) -> Product {
crate::db::CreateProduct {
name: ProductName::new(format!("{}", uuid::Uuid::new_v4())),
category: None,
deliver_days_flag: Days(vec![]),
}
.run(t)
.await
.unwrap()
}
async fn test_product_variant(product_id: ProductId, t: &mut PgT<'_>) -> ProductVariant {
crate::db::CreateProductVariant {
product_id,
name: ProductVariantName::new(format!("{}", uuid::Uuid::new_v4())),
short_description: ProductShortDesc::new(fakeit::words::sentence(4)),
long_description: ProductLongDesc::new(fakeit::words::sentence(16)),
price: Price::from_u32(650),
}
.run(t)
.await
.unwrap()
}
async fn test_stock(product_variant_id: ProductVariantId, t: &mut PgT<'_>) -> Stock {
crate::db::CreateStock {
product_variant_id,
quantity: Quantity::from_u32(1056),
quantity_unit: QuantityUnit::Gram,
}
.run(t)
.await
.unwrap()
}
#[tokio::test]
async fn create_stock() {
testx::db_t_ref!(t);
let product = test_product(&mut t).await;
let variant = test_product_variant(product.id, &mut t).await;
let res = inner_create_product_stock(
create_product_stock::Input {
product_variant_id: variant.id,
quantity: Quantity::from_u32(4684),
quantity_unit: QuantityUnit::Gram,
},
&mut t,
)
.await;
testx::db_rollback!(t);
let res = res.unwrap();
}
#[tokio::test]
async fn delete_stock() {
testx::db_t_ref!(t);
let product = test_product(&mut t).await;
let variant = test_product_variant(product.id, &mut t).await;
let stock = test_stock(variant.id, &mut t).await;
let quantity = Quantity::from_u32(6699);
let quantity_unit = QuantityUnit::Kilogram;
let res = inner_update_product_stock(
update_product_stock::Input {
id: stock.id,
product_variant_id: variant.id,
quantity,
quantity_unit,
},
&mut t,
)
.await;
testx::db_rollback!(t);
let res = res.unwrap();
assert_eq!(res.product_stock.id, stock.id);
assert_eq!(res.product_stock.product_variant_id, variant.id);
assert_eq!(res.product_stock.quantity, quantity);
assert_eq!(res.product_stock.quantity_unit, quantity_unit);
}
}

View File

@ -11,21 +11,24 @@ use crate::db::Database;
pub async fn create_product_variant(
input: create_product_variant::Input,
db: Database,
_mqtt: AsyncClient,
mqtt: AsyncClient,
_config: SharedAppConfig,
) -> create_product_variant::Output {
let mut t = begin_t!(db, Error::InternalServerError);
match inner_create_product_variant(input, &mut t).await {
Ok(res) => {
Ok(details) => {
if let Err(e) = t.commit().await {
tracing::error!("{}", e);
return Err(Error::InternalServerError);
tracing::warn!("{}", e);
Err(Error::InternalServerError)
} else {
mqtt.emit_product_variant_created(&details.product_variant)
.await;
Ok(details)
}
Ok(res)
}
Err(e) => {
t.rollback().await.map_err(|e| {
tracing::error!("{}", e);
tracing::warn!("{}", e);
Error::InternalServerError
})?;
Err(e)
@ -49,38 +52,117 @@ async fn inner_create_product_variant(
product_variant: variant,
}),
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
Err(Error::CreateProductVariant(input.product_id))
}
}
}
pub async fn update_product_variant(
_input: update_product_variant::Input,
_db: Database,
_mqtt: AsyncClient,
input: update_product_variant::Input,
db: Database,
mqtt: AsyncClient,
_config: SharedAppConfig,
) -> update_product_variant::Output {
todo!()
let id = input.id;
let mut t = begin_t!(db, Error::InternalServerError);
match inner_update_product_variant(input, &mut t).await {
Ok(details) => {
if let Err(e) = t.commit().await {
tracing::warn!("{}", e);
Err(Error::UpdateProductVariant(id))
} else {
mqtt.emit_product_variant_updated(&details.product_variant)
.await;
Ok(details)
}
}
Err(e) => {
t.rollback().await.ok();
Err(e)
}
}
}
async fn inner_update_product_variant(
input: update_product_variant::Input,
t: &mut PgT<'_>,
) -> update_product_variant::Output {
let dbm = crate::db::UpdateProductVariant {
product_variant_id: input.id,
product_id: input.product_id,
name: input.name,
short_description: input.short_description,
long_description: input.long_description,
price: input.price,
};
match dbm.run(t).await {
Ok(product_variant) => Ok(update_product_variant::Details { product_variant }),
Err(e) => {
tracing::warn!("{}", e);
Err(Error::UpdateProductVariant(input.id))
}
}
}
pub async fn delete_product_variant(
_input: delete_product_variant::Input,
_db: Database,
_mqtt: AsyncClient,
input: delete_product_variant::Input,
db: Database,
mqtt: AsyncClient,
_config: SharedAppConfig,
) -> delete_product_variant::Output {
todo!()
let id = input.product_variant_id;
let mut t = begin_t!(db, Error::InternalServerError);
match inner_delete_product_variant(input, &mut t).await {
Ok(details) => {
if let Err(e) = t.commit().await {
tracing::warn!("{}", e);
Err(Error::UpdateProductVariant(id))
} else {
mqtt.emit_product_variant_deleted(&details.product_variant_id)
.await;
Ok(details)
}
}
Err(e) => {
t.rollback().await.ok();
Err(e)
}
}
}
async fn inner_delete_product_variant(
input: delete_product_variant::Input,
t: &mut PgT<'_>,
) -> delete_product_variant::Output {
let dbm = crate::db::DeleteProductVariant {
product_variant_id: input.product_variant_id,
};
match dbm.run(t).await {
Ok(_product_variant) => Ok(delete_product_variant::Details {
product_id: input.product_id,
product_variant_id: input.product_variant_id,
}),
Err(e) => {
tracing::warn!("{}", e);
Err(Error::UpdateProductVariant(input.product_variant_id))
}
}
}
#[cfg(test)]
mod test {
use channels::stocks::create_product_variant;
use channels::stocks::{
create_product_variant, delete_product_variant, update_product_variant,
};
use config::UpdateConfig;
use db_utils::PgT;
use model::v2::*;
use crate::actions::product_variant::inner_create_product_variant;
use crate::actions::product_variant::{
inner_create_product_variant, inner_delete_product_variant, inner_update_product_variant,
};
use crate::db::Database;
struct NoOpts;
@ -97,6 +179,22 @@ mod test {
.unwrap()
}
async fn test_product_variant(product_id: ProductId, t: &mut PgT<'_>) -> ProductVariant {
inner_create_product_variant(
create_product_variant::Input {
product_id,
name: ProductVariantName::new(format!("{}", uuid::Uuid::new_v4())),
short_description: ProductShortDesc::new(fakeit::words::sentence(4)),
long_description: ProductLongDesc::new(fakeit::words::sentence(16)),
price: Price::from_u32(650),
},
t,
)
.await
.unwrap()
.product_variant
}
#[tokio::test]
async fn create_product_variant() {
testx::db_t_ref!(t);
@ -129,4 +227,70 @@ mod test {
assert_eq!(variant.long_description, long_description);
assert_eq!(variant.price, price);
}
#[tokio::test]
async fn update_product_variant() {
testx::db_t_ref!(t);
let product = test_product(&mut t).await;
let variant = test_product_variant(product.id, &mut t).await;
let id = variant.id;
let product_id = product.id;
let name = ProductVariantName::new(format!("{}", uuid::Uuid::new_v4()));
let short_description = ProductShortDesc::new(format!("{}", uuid::Uuid::new_v4()));
let long_description = ProductLongDesc::new(format!("{}", uuid::Uuid::new_v4()));
let price = Price::from_u32(234234);
let res = inner_update_product_variant(
update_product_variant::Input {
id,
product_id,
name: name.clone(),
short_description: short_description.clone(),
long_description: long_description.clone(),
price,
},
&mut t,
)
.await;
testx::db_rollback!(t);
let res = res.unwrap();
let expected = ProductVariant {
id,
product_id,
name,
short_description,
long_description,
price,
};
assert_eq!(res.product_variant, expected);
}
#[tokio::test]
async fn delete_product_variant() {
testx::db_t_ref!(t);
let product = test_product(&mut t).await;
let variant = test_product_variant(product.id, &mut t).await;
let res = inner_delete_product_variant(
delete_product_variant::Input {
product_id: product.id,
product_variant_id: variant.id,
},
&mut t,
)
.await;
testx::db_rollback!(t);
let res = res.unwrap();
assert_eq!(res.product_id, product.id);
assert_eq!(res.product_variant_id, variant.id);
}
}

View File

@ -34,7 +34,7 @@ LIMIT $1 OFFSET $2
.fetch_all(t)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::All
})
}
@ -64,7 +64,7 @@ RETURNING id, local_path, file_name, unique_name
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::Create
})
}
@ -104,7 +104,7 @@ WHERE
self.product_variant_ids.len(),
self.product_variant_ids.into_iter().map(|id| *id),
|e| {
tracing::error!("{}", e);
tracing::warn!("{}", e);
dbg!(e);
Error::PhotosForProductVariants
},

View File

@ -34,7 +34,7 @@ LIMIT $1 OFFSET $2
.fetch_all(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::All
})
}
@ -60,7 +60,7 @@ RETURNING id, product_variant_id, photo_id
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{:?}", e);
tracing::warn!("{:?}", e);
Error::Create
})
}
@ -84,7 +84,7 @@ RETURNING id, product_variant_id, photo_id
.fetch_optional(pool)
.await
.map_err(|e| {
tracing::error!("{:?}", e);
tracing::warn!("{:?}", e);
Error::Delete(self.id)
})
}

View File

@ -49,7 +49,52 @@ RETURNING id,
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{}", e);
tracing::warn!("{}", e);
dbg!(e);
Error::CreateProductVariant
})
}
}
#[derive(Debug)]
pub struct UpdateProductVariant {
pub product_variant_id: ProductVariantId,
pub product_id: ProductId,
pub name: ProductVariantName,
pub short_description: ProductShortDesc,
pub long_description: ProductLongDesc,
pub price: Price,
}
impl UpdateProductVariant {
pub async fn run(self, pool: &mut PgT<'_>) -> Result<ProductVariant> {
sqlx::query_as(
r#"
UPDATE product_variants
SET product_id = $2,
name = $3,
short_description = $4,
long_description = $5,
price = $6
WHERE id = $1
RETURNING id,
product_id,
name,
short_description,
long_description,
price
"#,
)
.bind(self.product_variant_id)
.bind(self.product_id)
.bind(self.name)
.bind(self.short_description)
.bind(self.long_description)
.bind(self.price)
.fetch_one(pool)
.await
.map_err(|e| {
tracing::warn!("{}", e);
dbg!(e);
Error::CreateProductVariant
})
@ -91,7 +136,7 @@ WHERE
self.product_ids.len(),
self.product_ids.iter().copied().map(|id| *id),
|e| {
tracing::error!("{}", e);
tracing::warn!("{}", e);
Error::ProductsVariants(self.product_ids.clone())
},
)

View File

@ -49,7 +49,7 @@ LIMIT $1 OFFSET $2
.fetch_all(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::All
})
}
@ -79,7 +79,7 @@ WHERE id = $1
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::Single(self.product_id)
})
}
@ -113,7 +113,7 @@ RETURNING id,
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
dbg!(e);
Error::Create
})
@ -153,7 +153,7 @@ RETURNING id,
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
dbg!(e);
Error::Update(self.id)
})
@ -184,7 +184,7 @@ RETURNING id,
.fetch_optional(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
eprintln!("{e:?}");
Error::Delete(self.product_id)
})
@ -219,7 +219,7 @@ LIMIT $2 OFFSET $3
.fetch_all(t)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::ShoppingCartProducts(self.shopping_cart_id)
})
}
@ -253,7 +253,7 @@ WHERE
self.product_ids.len(),
self.product_ids.into_iter().map(|id| *id),
|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::FindProducts
},
)

View File

@ -41,7 +41,7 @@ LIMIT $1 OFFSET $2
.fetch_all(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::All
})
}
@ -65,7 +65,7 @@ WHERE id = $1
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
dbg!(e);
Error::NotFound(self.id)
})
@ -94,7 +94,7 @@ RETURNING id, product_variant_id, quantity, quantity_unit
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
dbg!(e);
Error::Create
})
@ -128,7 +128,7 @@ RETURNING id, product_variant_id, quantity, quantity_unit
.fetch_one(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::Update(self.id)
})
}
@ -155,7 +155,7 @@ RETURNING id, product_variant_id, quantity, quantity_unit
.fetch_optional(pool)
.await
.map_err(|e| {
tracing::error!("{e:?}");
tracing::warn!("{e:?}");
Error::Delete(self.stock_id)
})
}

View File

@ -19,7 +19,7 @@ pub async fn start(config: SharedAppConfig, _db: Database) -> channels::AsyncCli
Ok(Event::Incoming(_incoming)) => {}
Ok(Event::Outgoing(_outgoing)) => {}
Err(e) => {
tracing::error!("{}", e);
tracing::warn!("{}", e);
}
}
}