exercises

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

main.rs (3940B)


      1 //! Products Service
      2 //!
      3 //! A microservice for managing product catalog, details, and ratings.
      4 //!
      5 //! # Features
      6 //! - Product listing with pagination and filtering
      7 //! - Product detail retrieval
      8 //! - Product rating/review management
      9 //! - PostgreSQL database with products schema
     10 //! - CORS enabled for web client access
     11 //!
     12 //! # API Endpoints
     13 //! - `GET /products` - List products with filters and pagination
     14 //! - `GET /products/{id}` - Get detailed product information
     15 //! - `PUT /products/{id}/ratings` - Create or update a product rating
     16 //!
     17 //! # Configuration
     18 //! The service is configured via:
     19 //! - Environment variables (loaded from .env if present)
     20 //! - config.toml file
     21 //!
     22 //! # Database
     23 //! Uses the `products` schema in PostgreSQL with automatic search path configuration.
     24 //! Migrations are run automatically on startup.
     25 
     26 mod config;
     27 mod db;
     28 mod handlers;
     29 mod models;
     30 mod telemetry;
     31 mod utils;
     32 
     33 use anyhow::Result;
     34 use axum::{
     35     routing::{get, put},
     36     Router,
     37 };
     38 use axum_otel_metrics::HttpMetricsLayerBuilder;
     39 use axum_tracing_opentelemetry::middleware::{OtelAxumLayer, OtelInResponseLayer};
     40 use std::net::SocketAddr;
     41 use tower_http::cors::CorsLayer;
     42 use tracing::info;
     43 
     44 use config::Config;
     45 use db::Database;
     46 
     47 #[tokio::main]
     48 async fn main() -> Result<()> {
     49     // Load environment variables from .env file if present
     50     // This is optional - the service will work without it
     51     dotenvy::dotenv().ok();
     52 
     53     let _telemetry_guard = telemetry::init_telemetry("products");
     54 
     55     // Load configuration from config.toml
     56     let config = Config::load()?;
     57 
     58     // Print startup information
     59     println!("Starting Products Service...");
     60     println!("Server port: {}", config.server.port);
     61     println!("Database URL: {}", config.database.url);
     62 
     63     // Initialize database connection pool
     64     // This sets the search path to the products schema for all connections
     65     let db = Database::new(&config.database.url, config.database.max_connections).await?;
     66 
     67     // Register observable gauges for connection pool health metrics
     68     let meter = opentelemetry::global::meter("inventory-service");
     69     telemetry::register_pool_metrics(&meter, db.pool().clone());
     70 
     71     // Build automatic HTTP RED metrics layer
     72     let metrics = HttpMetricsLayerBuilder::new().build();
     73 
     74     // Build the application router with all endpoints
     75     let app = Router::new()
     76         // Product endpoints
     77         .route("/products", get(handlers::list_products))
     78         .route("/products/{id}", get(handlers::get_product_by_id))
     79         // Rating endpoints
     80         .route("/products/{id}/ratings", put(handlers::upsert_rating))
     81         // Enable CORS for all routes
     82         // This allows the Angular frontend to make API calls
     83         .layer(CorsLayer::permissive())
     84         // Add database pool to application state
     85         // All handlers will have access to this via State extractor
     86         .with_state(db.pool().clone())
     87         // Automatic RED metrics (request rate, error rate, duration)
     88         .layer(metrics)
     89         .layer(OtelInResponseLayer)
     90         .layer(OtelAxumLayer::default())
     91         .layer(CorsLayer::permissive());
     92 
     93     // Create socket address from configuration
     94     let addr = SocketAddr::from(([0, 0, 0, 0], config.server.port));
     95 
     96     // Print API documentation
     97     info!(port = config.server.port, "Products Service listening on ");
     98     info!("\nProducts Service listening on {}", addr);
     99     info!("\nAvailable endpoints:");
    100     info!("  GET    /products              - List products (with pagination & filters)");
    101     info!("  GET    /products/{{id}}         - Get product details");
    102     info!("  PUT    /products/{{id}}/ratings - Create/update product rating");
    103 
    104     // Start the HTTP server
    105     let listener = tokio::net::TcpListener::bind(addr).await?;
    106     axum::serve(listener, app).await?;
    107 
    108     // Telemetry is flushed and shut down by the `Drop` impl on `_telemetry`
    109     // when this function returns.
    110 
    111     Ok(())
    112 }