exercises

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

main.rs (5350B)


      1 //! Inventory Service
      2 //!
      3 //! A microservice for managing product inventory, stock levels, and pricing.
      4 //!
      5 //! # Features
      6 //! - Product inventory management (stock levels, reservations)
      7 //! - Pricing management with discounts
      8 //! - Stock reservation for orders
      9 //! - Inventory transaction audit trail
     10 //! - PostgreSQL database with inventory schema
     11 //! - CORS enabled for web client access
     12 //!
     13 //! # API Endpoints
     14 //! ## Inventory
     15 //! - `GET /inventory` - List inventory with filters and pagination
     16 //! - `GET /inventory/{product_uuid}` - Get inventory for a specific product
     17 //! - `PUT /inventory/{product_uuid}` - Update stock quantity
     18 //! - `POST /inventory/reserve` - Reserve stock for an order
     19 //! - `POST /inventory/release` - Release reserved stock
     20 //! - `POST /inventory/confirm-sale` - Confirm sale and decrease stock
     21 //!
     22 //! ## Pricing
     23 //! - `GET /pricing` - List pricing with filters and pagination
     24 //! - `GET /pricing/{product_uuid}` - Get pricing for a specific product
     25 //! - `PUT /pricing/{product_uuid}` - Create or update pricing
     26 //!
     27 //! # Configuration
     28 //! The service is configured via:
     29 //! - Environment variables (loaded from .env if present)
     30 //! - config.toml file
     31 //!
     32 //! # Database
     33 //! Uses the `inventory` schema in PostgreSQL with automatic search path configuration.
     34 //! Migrations are run automatically on startup.
     35 
     36 mod config;
     37 mod db;
     38 mod handlers;
     39 mod metrics;
     40 mod models;
     41 mod telemetry;
     42 mod utils;
     43 
     44 use anyhow::Result;
     45 use axum::{
     46     routing::{get, post, put},
     47     Router,
     48 };
     49 use axum_otel_metrics::HttpMetricsLayerBuilder;
     50 use axum_tracing_opentelemetry::middleware::{OtelAxumLayer, OtelInResponseLayer};
     51 use std::net::SocketAddr;
     52 use tower_http::cors::CorsLayer;
     53 use tracing::info;
     54 
     55 use config::Config;
     56 use db::Database;
     57 
     58 #[tokio::main]
     59 async fn main() -> Result<()> {
     60     // Load environment variables from .env file if present
     61     dotenvy::dotenv().ok();
     62 
     63     // Initialize telemetry (tracing + OpenTelemetry)
     64     let _telemetry_guard = telemetry::init_telemetry("inventory");
     65 
     66     // Load configuration from config.toml
     67     let config = Config::load()?;
     68 
     69     // Log startup information using tracing macros
     70     info!(
     71         port = config.server.port,
     72         database_url = %config.database.url,
     73         "Starting Inventory Service"
     74     );
     75 
     76     // Initialize database connection pool
     77     let db = Database::new(&config.database.url, config.database.max_connections).await?;
     78 
     79     // Register observable gauges for connection pool health metrics
     80     let meter = opentelemetry::global::meter("inventory-service");
     81     telemetry::register_pool_metrics(&meter, db.pool().clone());
     82 
     83     // Build automatic HTTP RED metrics layer
     84     let metrics = HttpMetricsLayerBuilder::new().build();
     85 
     86     // Build the application router with all endpoints
     87     let app = Router::new()
     88         // Inventory endpoints
     89         .route("/inventory", get(handlers::list_inventory))
     90         .route(
     91             "/inventory/{product_uuid}",
     92             get(handlers::get_inventory_by_product),
     93         )
     94         .route("/inventory/{product_uuid}", put(handlers::update_stock))
     95         .route("/inventory/reserve", post(handlers::reserve_stock))
     96         .route("/inventory/release", post(handlers::release_stock))
     97         .route("/inventory/confirm-sale", post(handlers::confirm_sale))
     98         // Pricing endpoints
     99         .route("/pricing", get(handlers::list_pricing))
    100         .route(
    101             "/pricing/{product_uuid}",
    102             get(handlers::get_pricing_by_product),
    103         )
    104         .route("/pricing/{product_uuid}", put(handlers::upsert_pricing))
    105         // Automatic RED metrics (request rate, error rate, duration)
    106         .layer(metrics)
    107         // Include trace context as header into the response
    108         .layer(OtelInResponseLayer::default())
    109         // Start OpenTelemetry trace on incoming request
    110         .layer(OtelAxumLayer::default())
    111         // Enable CORS for all routes
    112         .layer(CorsLayer::permissive())
    113         // Add database pool to application state
    114         .with_state(db.pool().clone());
    115 
    116     // Create socket address from configuration
    117     let addr = SocketAddr::from(([0, 0, 0, 0], config.server.port));
    118 
    119     // Log API documentation
    120     info!(address = %addr, "Inventory Service listening");
    121     info!("Available endpoints:");
    122     info!("  Inventory:");
    123     info!("    GET    /inventory                  - List inventory (with pagination & filters)");
    124     info!("    GET    /inventory/{{product_uuid}}  - Get inventory for product");
    125     info!("    PUT    /inventory/{{product_uuid}}  - Update stock quantity");
    126     info!("    POST   /inventory/reserve          - Reserve stock for order");
    127     info!("    POST   /inventory/release          - Release reserved stock");
    128     info!("    POST   /inventory/confirm-sale     - Confirm sale and decrease stock");
    129     info!("  Pricing:");
    130     info!("    GET    /pricing                    - List pricing (with pagination & filters)");
    131     info!("    GET    /pricing/{{product_uuid}}    - Get pricing for product");
    132     info!("    PUT    /pricing/{{product_uuid}}    - Create or update pricing");
    133 
    134     // Start the HTTP server
    135     let listener = tokio::net::TcpListener::bind(addr).await?;
    136     axum::serve(listener, app).await?;
    137 
    138     // Telemetry is flushed and shut down by the `Drop` impl on `_telemetry`
    139     // when this function returns.
    140 
    141     Ok(())
    142 }