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 }