""" seed.py — Envoie une série de commandes CommandFlow pour observer le flux complet. Usage : uv run python seed.py # envoie 10 commandes normales uv run python seed.py --all # inclut des commandes avec montant invalide (DLQ) uv run python seed.py --count 5 # envoie 5 commandes Ce script publie directement sur Kafka (sans passer par l'API FastAPI). Il utilise OrderProducer, le même producer qu'order-service. Pour observer le flux : 1. Lancer Kafka : docker compose up -d 2. Lancer les consumers dans des terminaux séparés 3. Lancer ce script 4. Observer les logs de chaque consumer 5. Ouvrir Kafdrop sur http://localhost:9000 pour voir les topics """ import sys import os import time import argparse import random # Ajoute les dossiers nécessaires au path BASE_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, BASE_DIR) sys.path.insert(0, os.path.join(BASE_DIR, "order_service")) # order-service contient un tiret → on ne peut pas faire "import order-service.producer" # On ajoute le dossier au path et on importe directement producer.py import importlib.util as _ilu _spec = _ilu.spec_from_file_location( "order_producer", os.path.join(BASE_DIR, "order_service", "producer.py"), ) _mod = _ilu.module_from_spec(_spec) _spec.loader.exec_module(_mod) OrderProducer = _mod.OrderProducer # --------------------------------------------------------------------------- # Données de test # --------------------------------------------------------------------------- RESTAURANTS = [ {"id": 1, "name": "Pizza Roma"}, {"id": 2, "name": "Sushi Zen"}, {"id": 3, "name": "Burger Lab"}, ] MENUS = { 1: [ # Pizza Roma {"dish_id": 101, "name": "Margherita", "price": 12.50}, {"dish_id": 102, "name": "Quattro Stagioni", "price": 14.00}, {"dish_id": 103, "name": "Tiramisu", "price": 5.50}, {"dish_id": 104, "name": "Coca-Cola", "price": 3.00}, ], 2: [ # Sushi Zen {"dish_id": 201, "name": "California Roll x8", "price": 11.00}, {"dish_id": 202, "name": "Saumon Tataki", "price": 13.50}, {"dish_id": 203, "name": "Edamame", "price": 4.50}, {"dish_id": 204, "name": "Thé vert", "price": 2.50}, ], 3: [ # Burger Lab {"dish_id": 301, "name": "Classic Smash", "price": 10.00}, {"dish_id": 302, "name": "Double Bacon", "price": 13.00}, {"dish_id": 303, "name": "Frites maison", "price": 4.00}, {"dish_id": 304, "name": "Milkshake", "price": 5.00}, ], } def random_order(customer_id: int, order_id: int) -> dict: """Génère une commande aléatoire réaliste.""" restaurant = random.choice(RESTAURANTS) menu = MENUS[restaurant["id"]] # 1 à 3 plats aléatoires du menu items = random.sample(menu, k=random.randint(1, 3)) total = round(sum(item["price"] for item in items), 2) return { "order_id": order_id, "customer_id": customer_id, "restaurant_id": restaurant["id"], "items": items, "total_amount": total, } def invalid_order(customer_id: int, order_id: int) -> dict: """Génère une commande avec un montant invalide pour déclencher la DLQ.""" return { "order_id": order_id, "customer_id": customer_id, "restaurant_id": 1, "items": [{"dish_id": 999, "name": "Plat inexistant", "price": -5.00}], "total_amount": -5.00, # ← invalide : payment-service lèvera une ValueError } # --------------------------------------------------------------------------- # Script principal # --------------------------------------------------------------------------- def main(): parser = argparse.ArgumentParser(description="Seed CommandFlow avec des commandes de test.") parser.add_argument("--count", type=int, default=10, help="Nombre de commandes à envoyer (défaut : 10)") parser.add_argument("--all", action="store_true", help="Inclut 2 commandes invalides (pour tester la DLQ)") parser.add_argument("--delay", type=float, default=0.5, help="Délai entre chaque message en secondes (défaut : 0.5)") args = parser.parse_args() producer = OrderProducer() print("=" * 60) print(" CommandFlow — seed") print("=" * 60) print(f" Commandes à envoyer : {args.count}") print(f" Délai entre messages : {args.delay}s") print(f" Commandes invalides : {'oui (DLQ)' if args.all else 'non'}") print("=" * 60) print() orders = [] # Génère les commandes normales for i in range(1, args.count + 1): customer_id = random.randint(1, 50) orders.append(("normal", random_order(customer_id, order_id=i))) # Insère 2 commandes invalides si --all if args.all: bad_id_1 = args.count + 1 bad_id_2 = args.count + 2 orders.insert(len(orders) // 3, ("invalid", invalid_order(99, bad_id_1))) orders.insert(2 * len(orders) // 3, ("invalid", invalid_order(99, bad_id_2))) # Envoi sent = 0 for kind, order in orders: tag = "⚠ INVALIDE" if kind == "invalid" else "✓" print( f"[seed] {tag} order_id={order['order_id']:>3} " f"| customer={order['customer_id']:>2} " f"| resto_id={order['restaurant_id']} " f"| total={order['total_amount']:>6.2f}€ " f"| items={len(order['items'])}" ) producer.publish_order_created(order) sent += 1 time.sleep(args.delay) print() print("=" * 60) print(f" {sent} messages publiés sur le topic 'orders'.") print() print(" Pour observer le flux :") print(" → Kafdrop : http://localhost:9000") print(" → Topics : orders, payments, notifications") if args.all: print(" → DLQ : orders-dlq (2 messages attendus)") print("=" * 60) if __name__ == "__main__": main()