exercises

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

pricing.rs (3221B)


      1 //! Pricing API handlers
      2 //!
      3 //! This module contains the HTTP request handlers for pricing-related endpoints.
      4 //! Database operations are delegated to the repository layer in `db::pricing`.
      5 
      6 use axum::{
      7     extract::{Path, Query, State},
      8     http::StatusCode,
      9     response::{IntoResponse, Json},
     10 };
     11 use sqlx::PgPool;
     12 use uuid::Uuid;
     13 
     14 use crate::db;
     15 use crate::models::{PricingQueryParams, PricingResponse, UpdatePricingRequest};
     16 use crate::utils::{calculate_pagination, calculate_total_pages, internal_error, not_found_error};
     17 
     18 /// List pricing with pagination and filtering
     19 ///
     20 /// # Endpoint
     21 /// `GET /pricing`
     22 ///
     23 /// # Query Parameters
     24 /// - `page` (default: 1) - Page number (1-indexed)
     25 /// - `page_size` (default: 20, max: 100) - Items per page
     26 /// - `product_uuid` - Filter by specific product
     27 /// - `min_price` / `max_price` - Filter by price range
     28 /// - `has_discount` - Filter by discount presence
     29 pub async fn list_pricing(
     30     State(pool): State<PgPool>,
     31     Query(params): Query<PricingQueryParams>,
     32 ) -> impl IntoResponse {
     33     // Apply pagination defaults and constraints
     34     let (page, page_size, offset) = calculate_pagination(params.page, params.page_size);
     35 
     36     // Delegate to repository layer for database operations
     37     let (pricing, total_count) =
     38         match db::list_pricing(&pool, &params, page, page_size, offset).await {
     39             Ok(result) => result,
     40             Err(e) => {
     41                 return internal_error("Failed to fetch pricing", e.to_string());
     42             }
     43         };
     44 
     45     let total_pages = calculate_total_pages(total_count, page_size);
     46 
     47     let response = PricingResponse {
     48         pricing,
     49         total_count,
     50         page,
     51         page_size,
     52         total_pages,
     53     };
     54 
     55     (StatusCode::OK, Json(response)).into_response()
     56 }
     57 
     58 /// Get pricing for a specific product by UUID
     59 ///
     60 /// # Endpoint
     61 /// `GET /pricing/{product_uuid}`
     62 pub async fn get_pricing_by_product(
     63     State(pool): State<PgPool>,
     64     Path(product_uuid): Path<Uuid>,
     65 ) -> impl IntoResponse {
     66     // Delegate to repository layer for database lookup
     67     match db::get_pricing_by_product(&pool, product_uuid).await {
     68         Ok(Some(pricing)) => (StatusCode::OK, Json(pricing)).into_response(),
     69         Ok(None) => not_found_error(
     70             "Pricing not found for product",
     71             serde_json::json!({"product_uuid": product_uuid.to_string()}),
     72         ),
     73         Err(e) => internal_error("Failed to fetch pricing", e.to_string()),
     74     }
     75 }
     76 
     77 /// Create or update pricing for a product
     78 ///
     79 /// # Endpoint
     80 /// `PUT /pricing/{product_uuid}`
     81 pub async fn upsert_pricing(
     82     State(pool): State<PgPool>,
     83     Path(product_uuid): Path<Uuid>,
     84     Json(request): Json<UpdatePricingRequest>,
     85 ) -> impl IntoResponse {
     86     // Delegate to repository layer for pricing upsert
     87     match db::upsert_pricing(
     88         &pool,
     89         product_uuid,
     90         request.final_price,
     91         request.initial_price,
     92         request.currency.unwrap_or_else(|| "USD".to_string()),
     93         request.price_valid_from,
     94         request.price_valid_until,
     95     )
     96     .await
     97     {
     98         Ok(pricing) => (StatusCode::OK, Json(pricing)).into_response(),
     99         Err(e) => internal_error("Failed to update pricing", e.to_string()),
    100     }
    101 }