diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-12-09 13:07:14 +0100 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-12-09 13:07:14 +0100 |
| commit | c91dce4f77ae12453203f0a28b91efb6533cc095 (patch) | |
| tree | 4f50e755dff7f717d45309b08f9fe2c8c87f88bd /crates/rocie-server/src/api/get/product.rs | |
| parent | chore(rocie-client): Regenerate (diff) | |
| download | server-c91dce4f77ae12453203f0a28b91efb6533cc095.zip | |
feat(rocie-server): Implement basic user handling and authentication
Diffstat (limited to 'crates/rocie-server/src/api/get/product.rs')
| -rw-r--r-- | crates/rocie-server/src/api/get/product.rs | 237 |
1 files changed, 0 insertions, 237 deletions
diff --git a/crates/rocie-server/src/api/get/product.rs b/crates/rocie-server/src/api/get/product.rs deleted file mode 100644 index 4216f9b..0000000 --- a/crates/rocie-server/src/api/get/product.rs +++ /dev/null @@ -1,237 +0,0 @@ -use actix_web::{HttpRequest, HttpResponse, Responder, Result, get, web}; -use log::info; -use percent_encoding::percent_decode_str; - -use crate::{ - app::App, - storage::sql::{ - product::{Product, ProductId, ProductIdStub}, - product_amount::ProductAmount, - product_parent::{ProductParent, ProductParentId, ProductParentIdStub}, - }, -}; - -/// A String, that is not url-decoded on parse. -struct UrlEncodedString(String); - -impl UrlEncodedString { - /// Percent de-encode a given string - fn percent_decode(&self) -> Result<String, std::str::Utf8Error> { - percent_decode_str(self.0.replace('+', "%20").as_str()) - .decode_utf8() - .map(|s| s.to_string()) - .inspect(|s| info!("Decoded `{}` as `{s}`", self.0)) - } - - fn from_str(inner: &str) -> Self { - Self(inner.to_owned()) - } -} - -/// Get Product by id -#[utoipa::path( - responses( - (status = OK, description = "Product found from database", body = Product), - (status = NOT_FOUND, description = "Product not found in database"), - (status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String) - ), - params( - ("id" = ProductId, description = "Product id" ), - ) -)] -#[get("/product/by-id/{id}")] -pub(crate) async fn product_by_id( - app: web::Data<App>, - id: web::Path<ProductIdStub>, -) -> Result<impl Responder> { - let id = id.into_inner(); - - match Product::from_id(&app, id.into()).await? { - Some(product) => Ok(HttpResponse::Ok().json(product)), - None => Ok(HttpResponse::NotFound().finish()), - } -} - -/// Get Product by name -#[utoipa::path( - responses( - (status = OK, description = "Product found from database", body = Product), - (status = NOT_FOUND, description = "Product not found in database"), - (status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String) - ), - params( - ("name" = String, description = "Name of the product" ), - ) -)] -#[get("/product/by-name/{name}")] -pub(crate) async fn product_by_name( - app: web::Data<App>, - req: HttpRequest, - name: web::Path<String>, -) -> Result<impl Responder> { - drop(name); - - let name = UrlEncodedString::from_str( - req.path() - .strip_prefix("/product/by-name/") - .expect("Will always exists"), - ); - let name = name.percent_decode()?; - - match Product::from_name(&app, name).await? { - Some(product) => Ok(HttpResponse::Ok().json(product)), - None => Ok(HttpResponse::NotFound().finish()), - } -} - -/// Get Product suggestion by name -#[utoipa::path( - responses( - (status = OK, description = "Product suggestions found from database", body = Vec<Product>), - (status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String) - ), - params( - ("name" = String, description = "Partial name of a product" ), - ) -)] -#[get("/product/by-part-name/{name}")] -pub(crate) async fn product_suggestion_by_name( - app: web::Data<App>, - req: HttpRequest, - name: web::Path<String>, -) -> Result<impl Responder> { - drop(name); - - let name = UrlEncodedString::from_str( - req.path() - .strip_prefix("/product/by-part-name/") - .expect("Will always exists"), - ); - let name = &name.percent_decode()?; - - let all = Product::get_all(&app).await?; - - let matching = all - .into_iter() - .filter(|product| product.name.starts_with(name.as_str())) - .collect::<Vec<_>>(); - - Ok(HttpResponse::Ok().json(matching)) -} - -/// Return all registered products -#[utoipa::path( - responses( - (status = OK, description = "All products found", body = Vec<Product>), - (status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String) - ), -)] -#[get("/products_registered/")] -pub(crate) async fn products_registered(app: web::Data<App>) -> Result<impl Responder> { - let all = Product::get_all(&app).await?; - - Ok(HttpResponse::Ok().json(all)) -} - -/// Return all products, which non-null amount in storage -#[utoipa::path( - responses( - (status = OK, description = "All products found", body = Vec<Product>), - (status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String) - ), -)] -#[get("/products_in_storage/")] -pub(crate) async fn products_in_storage(app: web::Data<App>) -> Result<impl Responder> { - let all = Product::get_all(&app).await?; - - let mut output_products = Vec::with_capacity(all.len()); - for product in all { - let amount = ProductAmount::from_id(&app, product.id).await?; - - if amount.is_some_and(|amount| amount.amount.value > 0) { - output_products.push(product); - } - } - - Ok(HttpResponse::Ok().json(output_products)) -} - -/// Get Products by it's product parent id -/// -/// This will also return all products below this product parent id -#[utoipa::path( - responses( - (status = OK, description = "Products found from database", body = Vec<Product>), - (status = NOT_FOUND, description = "Product parent id not found in database"), - (status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String) - ), - params( - ("id" = ProductParentId, description = "Product parent id" ), - ) -)] -#[get("/product/by-product-parent-id-indirect/{id}")] -pub(crate) async fn products_by_product_parent_id_indirect( - app: web::Data<App>, - id: web::Path<ProductParentIdStub>, -) -> Result<impl Responder> { - let id = id.into_inner(); - - if let Some(parent) = ProductParent::from_id(&app, id.into()).await? { - async fn collect_products(app: &App, parent: ProductParent) -> Result<Vec<Product>> { - let mut all = Product::get_all(app) - .await? - .into_iter() - .filter(|prod| prod.parent.is_some_and(|val| val == parent.id)) - .collect::<Vec<_>>(); - - if let Some(child) = ProductParent::get_all(app) - .await? - .into_iter() - .find(|pp| pp.parent.is_some_and(|id| id == parent.id)) - { - all.extend(Box::pin(collect_products(app, child)).await?); - } - - Ok(all) - } - - let all = collect_products(&app, parent).await?; - - Ok(HttpResponse::Ok().json(all)) - } else { - Ok(HttpResponse::NotFound().finish()) - } -} - -/// Get Products by it's product parent id -/// -/// This will only return products directly associated with this product parent id -#[utoipa::path( - responses( - (status = OK, description = "Products found from database", body = Vec<Product>), - (status = NOT_FOUND, description = "Product parent id not found in database"), - (status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String) - ), - params( - ("id" = ProductParentId, description = "Product parent id" ), - ) -)] -#[get("/product/by-product-parent-id-direct/{id}")] -pub(crate) async fn products_by_product_parent_id_direct( - app: web::Data<App>, - id: web::Path<ProductParentIdStub>, -) -> Result<impl Responder> { - let id = id.into_inner(); - - if let Some(parent) = ProductParent::from_id(&app, id.into()).await? { - let all = Product::get_all(&app) - .await? - .into_iter() - .filter(|prod| prod.parent.is_some_and(|val| val == parent.id)) - .collect::<Vec<_>>(); - - Ok(HttpResponse::Ok().json(all)) - } else { - Ok(HttpResponse::NotFound().finish()) - } -} |
