DataSeeder.java

package com.stocks.stockease.config;

import org.springframework.context.annotation.Profile;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import com.stocks.stockease.model.Product;
import com.stocks.stockease.model.User;
import com.stocks.stockease.repository.ProductRepository;
import com.stocks.stockease.repository.UserRepository;

import jakarta.annotation.PostConstruct;

/**
 * Database seeder for development and test environments.
 * 
 * Purpose:
 * - Populates database with fixture data (users, products) on startup
 * - Enables API testing without manual setup
 * - Supports demo/PoC deployments with pre-loaded inventory
 * 
 * Lifecycle:
 * - Bean created during Spring startup
 * - @PostConstruct seedData() called after bean fully constructed
 * - Data inserted only if tables empty (idempotent via count checks)
 * 
 * Profile activation:
 * - @Profile("!prod"): Active in dev, test profiles; DISABLED in production
 * - Prevents accidental data seeding in prod environments
 * - Explicitly exclude with @Profile("prod") or spring.profiles.active=prod
 * 
 * Fixture data:
 * - Users: admin (ROLE_ADMIN), user (ROLE_USER) for testing role-based access
 * - Products: 5 sample products with name, quantity, price for CRUD testing
 * - Passwords: BCrypt-encoded (admin123, user123 for demo)
 * 
 * @author Team StockEase
 * @version 1.0
 * @since 2025-01-01
 */
@Component
@Profile("!prod")
public class DataSeeder {

    /**
     * Repository for Product entity persistence operations.
     */
    private final ProductRepository productRepository;

    /**
     * Repository for User entity persistence operations.
     */
    private final UserRepository userRepository;

    /**
     * Password encoder (BCrypt) for hashing user credentials before storage.
     */
    private final PasswordEncoder passwordEncoder;

    /**
     * Constructor for dependency injection via Spring constructor autowiring.
     * 
     * Spring automatically injects dependencies:
     * - No @Autowired needed on constructor (Spring 4.3+)
     * - Constructor injection preferred over field injection (immutability, testability)
     * 
     * @param productRepository repository for product data access
     * @param userRepository repository for user data access
     * @param passwordEncoder BCrypt encoder for credential hashing
     */
    public DataSeeder(ProductRepository productRepository, UserRepository userRepository, PasswordEncoder passwordEncoder) {
        this.productRepository = productRepository;
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
    }

    /**
     * Initializes database with fixture data on bean creation.
     * 
     * Execution timing:
     * - Called by Spring after constructor completes (@PostConstruct)
     * - Before @RestController beans receive requests
     * - Runs once per application startup
     * 
     * Idempotency:
     * - Checks count() before inserting (prevents duplicates on restart)
     * - Safe to call multiple times (conditional inserts)
     * 
     * User fixtures:
     * - admin / admin123 → ROLE_ADMIN (can create, update, delete products)
     * - user / user123 → ROLE_USER (can read, update quantities only)
     * 
     * Product fixtures (name, quantity, price):
     * - Product 1: qty=10, price=50.0
     * - Product 2: qty=5, price=30.0
     * - Product 3: qty=3, price=20.0
     * - Product 4: qty=3, price=10.0
     * - Product 5: qty=20, price=40.0
     * 
     * Database state after seeding:
     * - 2 users in USER table (admin, user)
     * - 5 products in PRODUCT table (with timestamps)
     * - Ready for API testing: login → list products → CRUD operations
     */
    @PostConstruct
    public void seedData() {
        System.out.println("Seeding data...");

        // Seed users if database is empty (idempotent via count check)
        if (userRepository.count() == 0) {
            // Create ADMIN user: can perform all CRUD operations
            userRepository.save(new User("admin", passwordEncoder.encode("admin123"), "ROLE_ADMIN"));
            // Create regular USER: can read and update quantities only
            userRepository.save(new User("user", passwordEncoder.encode("user123"), "ROLE_USER"));
        }

        // Seed products if database is empty (idempotent via count check)
        if (productRepository.count() == 0) {
            // Sample inventory data for testing and demo purposes
            productRepository.save(new Product("Product 1", 10, 50.0));
            productRepository.save(new Product("Product 2", 5, 30.0));
            productRepository.save(new Product("Product 3", 3, 20.0));
            productRepository.save(new Product("Product 4", 3, 10.0));
            productRepository.save(new Product("Product 5", 20, 40.0));
        }
    }
}