diff --git a/src/application/__pycache__/exceptions.cpython-314.pyc b/src/application/__pycache__/exceptions.cpython-314.pyc index 6841d90..00e33ab 100644 Binary files a/src/application/__pycache__/exceptions.cpython-314.pyc and b/src/application/__pycache__/exceptions.cpython-314.pyc differ diff --git a/src/application/exceptions.py b/src/application/exceptions.py index 5c6014b..2a82d07 100644 --- a/src/application/exceptions.py +++ b/src/application/exceptions.py @@ -1,2 +1,7 @@ class UserAlreadyExists(Exception): pass +class OrderNotFound(Exception): + pass + +class OrderAlreadyExists(Exception): + pass \ No newline at end of file diff --git a/src/application/ports/__pycache__/order_repository.cpython-314.pyc b/src/application/ports/__pycache__/order_repository.cpython-314.pyc new file mode 100644 index 0000000..71f040f Binary files /dev/null and b/src/application/ports/__pycache__/order_repository.cpython-314.pyc differ diff --git a/src/application/ports/order_repository.py b/src/application/ports/order_repository.py new file mode 100644 index 0000000..b78eea4 --- /dev/null +++ b/src/application/ports/order_repository.py @@ -0,0 +1,23 @@ +from abc import ABC, abstractmethod +from domain.orders import Order + +class OrderRepository(ABC): + @abstractmethod + def save(self, order: Order): + ... + + @abstractmethod + def viewAll(self) -> list[Order]: + ... + + @abstractmethod + def viewById(self, order_id: int): + ... + + @abstractmethod + def removeOrder(self, order_id: int): + ... + + @abstractmethod + def editOrder(self, order_id: int, order: Order): + ... diff --git a/src/application/services/__pycache__/order_services.cpython-314.pyc b/src/application/services/__pycache__/order_services.cpython-314.pyc new file mode 100644 index 0000000..66c3f45 Binary files /dev/null and b/src/application/services/__pycache__/order_services.cpython-314.pyc differ diff --git a/src/application/services/order_services.py b/src/application/services/order_services.py new file mode 100644 index 0000000..37199f7 --- /dev/null +++ b/src/application/services/order_services.py @@ -0,0 +1,68 @@ +from domain.orders import Order +from application.ports.order_repository import OrderRepository + +class CreateOrder: + def __init__(self, repo: OrderRepository): + if not isinstance(repo, OrderRepository): + raise TypeError("repo must implement OrderRepository") + self.repo = repo + + def execute(self, user_id: int, description: str, quantity: int, total_price: float): + from datetime import datetime + order = Order( + order_id=0, + user_id=user_id, + description=description, + quantity=quantity, + total_price=total_price, + created_at=datetime.now() + ) + self.repo.save(order) + return order + +class ViewOrders: + def __init__(self, repo: OrderRepository): + if not isinstance(repo, OrderRepository): + raise TypeError("repo must implement OrderRepository") + self.repo = repo + + def execute(self): + orders = self.repo.viewAll() + return orders + +class ViewOrderById: + def __init__(self, repo: OrderRepository): + if not isinstance(repo, OrderRepository): + raise TypeError("repo must implement OrderRepository") + self.repo = repo + + def execute(self, order_id: int) -> Order | None: + order = self.repo.viewById(order_id) + return order + +class RemoveOrderById: + def __init__(self, repo: OrderRepository): + if not isinstance(repo, OrderRepository): + raise TypeError("repo must implement OrderRepository") + self.repo = repo + + def execute(self, order_id: int) -> Order: + order = self.repo.removeOrder(order_id) + return {"Result": f"Order {order_id} deleted succesfully"} + +class EditOrderById: + def __init__(self, repo: OrderRepository): + if not isinstance(repo, OrderRepository): + raise TypeError("repo must implement OrderRepository") + self.repo = repo + + def execute(self, order_id: int, user_id: int, description: str, quantity: int, total_price: float) -> Order | None: + order = Order( + order_id=order_id, + user_id=user_id, + description=description, + quantity=quantity, + total_price=total_price, + created_at=None + ) + return self.repo.editOrder(order_id, order) diff --git a/src/domain/__pycache__/orders.cpython-314.pyc b/src/domain/__pycache__/orders.cpython-314.pyc new file mode 100644 index 0000000..2dede9d Binary files /dev/null and b/src/domain/__pycache__/orders.cpython-314.pyc differ diff --git a/src/domain/orders.py b/src/domain/orders.py new file mode 100644 index 0000000..f5e25fb --- /dev/null +++ b/src/domain/orders.py @@ -0,0 +1,11 @@ +from dataclasses import dataclass +from datetime import datetime + +@dataclass +class Order: + order_id: int + user_id: int + description: str + quantity: int + total_price: float + created_at: datetime diff --git a/src/infrastructure/adapters/persistence/__pycache__/models.cpython-314.pyc b/src/infrastructure/adapters/persistence/__pycache__/models.cpython-314.pyc index 138ba89..7c947c5 100644 Binary files a/src/infrastructure/adapters/persistence/__pycache__/models.cpython-314.pyc and b/src/infrastructure/adapters/persistence/__pycache__/models.cpython-314.pyc differ diff --git a/src/infrastructure/adapters/persistence/__pycache__/order_repository_sql.cpython-314.pyc b/src/infrastructure/adapters/persistence/__pycache__/order_repository_sql.cpython-314.pyc new file mode 100644 index 0000000..6d4e029 Binary files /dev/null and b/src/infrastructure/adapters/persistence/__pycache__/order_repository_sql.cpython-314.pyc differ diff --git a/src/infrastructure/adapters/persistence/models.py b/src/infrastructure/adapters/persistence/models.py index 3cc098b..a69c639 100644 --- a/src/infrastructure/adapters/persistence/models.py +++ b/src/infrastructure/adapters/persistence/models.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, Float, DateTime from infrastructure.adapters.persistence.db import Base class UserModel(Base): @@ -7,4 +7,14 @@ class UserModel(Base): user_id = Column(Integer, primary_key=True, index=True) name = Column(String, unique=False, index=True) email = Column(String, unique=True, index=True) - phone = Column(String, unique=True, index=True) \ No newline at end of file + phone = Column(String, unique=True, index=True) + +class OrderModel(Base): + __tablename__ = "orders" + + order_id = Column(Integer, primary_key=True, index=True) + user_id = Column(Integer, index=True) + description = Column(String, index=True) + quantity = Column(Integer, index=True) + total_price = Column(Float, index=True) + created_at = Column(DateTime, index=True) diff --git a/src/infrastructure/adapters/persistence/order_repository_sql.py b/src/infrastructure/adapters/persistence/order_repository_sql.py new file mode 100644 index 0000000..084c1b5 --- /dev/null +++ b/src/infrastructure/adapters/persistence/order_repository_sql.py @@ -0,0 +1,108 @@ +from sqlalchemy.orm import Session +from sqlalchemy.exc import IntegrityError +from domain.orders import Order +from application.ports.order_repository import OrderRepository +from infrastructure.adapters.persistence.db import SessionLocal +from infrastructure.adapters.persistence.models import OrderModel + +class SqlOrderRepository(OrderRepository): + def __init__(self): + self.db: Session = SessionLocal() + + def save(self, order: Order): + model = OrderModel( + user_id=order.user_id, + description=order.description, + quantity=order.quantity, + total_price=order.total_price, + created_at=order.created_at + ) + + self.db.add(model) + + try: + self.db.commit() + except IntegrityError: + self.db.rollback() + raise Exception("An error occurred while creating the order.") + + self.db.refresh(model) + + return Order( + order_id=model.order_id, + user_id=model.user_id, + description=model.description, + quantity=model.quantity, + total_price=model.total_price, + created_at=model.created_at + ) + + def viewAll(self) -> list[Order]: + models = self.db.query(OrderModel).all() + + return [ + Order( + order_id=model.order_id, + user_id=model.user_id, + description=model.description, + quantity=model.quantity, + total_price=model.total_price, + created_at=model.created_at + ) for model in models + ] + + def viewById(self, order_id: int) -> Order | None: + model = self.db.query(OrderModel).filter(OrderModel.order_id == order_id).first() + + if not model: + return None + + return Order( + order_id=model.order_id, + user_id=model.user_id, + description=model.description, + quantity=model.quantity, + total_price=model.total_price, + created_at=model.created_at + ) + + def removeOrder(self, order_id: int): + model = self.db.query(OrderModel).filter(OrderModel.order_id == order_id).first() + + if not model: + return False + + try: + self.db.delete(model) + self.db.commit() + return True + except IntegrityError: + self.db.rollback() + raise Exception("Cannot delete order due to existing references.") + + def editOrder(self, order_id: int, order: Order) -> Order | None: + model = self.db.query(OrderModel).filter(OrderModel.order_id == order_id).first() + + if not model: + return None + + try: + model.user_id = order.user_id + model.description = order.description + model.quantity = order.quantity + model.total_price = order.total_price + + self.db.commit() + self.db.refresh(model) + + return Order( + order_id=model.order_id, + user_id=model.user_id, + description=model.description, + quantity=model.quantity, + total_price=model.total_price, + created_at=model.created_at + ) + except IntegrityError: + self.db.rollback() + raise Exception("An error occurred while updating the order.") diff --git a/src/infrastructure/api/orders/__init__.py b/src/infrastructure/api/orders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/infrastructure/api/orders/__pycache__/__init__.cpython-314.pyc b/src/infrastructure/api/orders/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000..9396fec Binary files /dev/null and b/src/infrastructure/api/orders/__pycache__/__init__.cpython-314.pyc differ diff --git a/src/infrastructure/api/orders/__pycache__/app.cpython-314.pyc b/src/infrastructure/api/orders/__pycache__/app.cpython-314.pyc new file mode 100644 index 0000000..e49c949 Binary files /dev/null and b/src/infrastructure/api/orders/__pycache__/app.cpython-314.pyc differ diff --git a/src/infrastructure/api/orders/__pycache__/orders.cpython-314.pyc b/src/infrastructure/api/orders/__pycache__/orders.cpython-314.pyc new file mode 100644 index 0000000..81a7119 Binary files /dev/null and b/src/infrastructure/api/orders/__pycache__/orders.cpython-314.pyc differ diff --git a/src/infrastructure/api/orders/__pycache__/root.cpython-314.pyc b/src/infrastructure/api/orders/__pycache__/root.cpython-314.pyc new file mode 100644 index 0000000..e7ee25d Binary files /dev/null and b/src/infrastructure/api/orders/__pycache__/root.cpython-314.pyc differ diff --git a/src/infrastructure/api/orders/__pycache__/router.cpython-314.pyc b/src/infrastructure/api/orders/__pycache__/router.cpython-314.pyc new file mode 100644 index 0000000..e1e5484 Binary files /dev/null and b/src/infrastructure/api/orders/__pycache__/router.cpython-314.pyc differ diff --git a/src/infrastructure/api/orders/app.py b/src/infrastructure/api/orders/app.py new file mode 100644 index 0000000..278ec13 --- /dev/null +++ b/src/infrastructure/api/orders/app.py @@ -0,0 +1,10 @@ +from fastapi import FastAPI +from core.config import ConfSettings +from infrastructure.api.orders.router import router + +def create_app() -> FastAPI: + app = FastAPI(title="Orders Microservice", + version="1.0.0", + description="Microservicio de gestión de pedidos") + app.include_router(router) + return app diff --git a/src/infrastructure/api/orders/orders.py b/src/infrastructure/api/orders/orders.py new file mode 100644 index 0000000..65d8547 --- /dev/null +++ b/src/infrastructure/api/orders/orders.py @@ -0,0 +1,77 @@ +from fastapi import APIRouter, HTTPException +from application.services.order_services import CreateOrder, ViewOrders, ViewOrderById, RemoveOrderById, EditOrderById +from infrastructure.adapters.persistence.order_repository_sql import SqlOrderRepository + +router = APIRouter() + +@router.post("/") +def create_order(user_id: int, description: str, quantity: int, total_price: float): + service = CreateOrder(SqlOrderRepository()) + + try: + return service.execute(user_id, description, quantity, total_price) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + +@router.get("/") +def view_all_orders(): + service = ViewOrders(SqlOrderRepository()) + + try: + return service.execute() + except Exception as e: + raise HTTPException( + status_code=500, + detail=str(e) + ) + +@router.get("/{order_id}") +def view_order_by_id(order_id: int): + service = ViewOrderById(SqlOrderRepository()) + + result = service.execute(order_id) + + if result is None: + raise HTTPException( + status_code=404, + detail="Order not found" + ) + + return result + +@router.delete("/{order_id}") +def delete_order_by_id(order_id: int): + service = RemoveOrderById(SqlOrderRepository()) + + result = service.execute(order_id) + + if result is None: + raise HTTPException( + status_code=404, + detail="Order not found" + ) + + return result + +@router.put("/{order_id}") +def edit_order_by_id(order_id: int, user_id: int, description: str, quantity: int, total_price: float): + service = EditOrderById(SqlOrderRepository()) + + try: + result = service.execute(order_id=order_id, user_id=user_id, description=description, quantity=quantity, total_price=total_price) + except Exception as e: + raise HTTPException( + status_code=500, + detail=str(e) + ) + + if result is None: + raise HTTPException( + status_code=404, + detail="Order not found" + ) + + return result diff --git a/src/infrastructure/api/orders/root.py b/src/infrastructure/api/orders/root.py new file mode 100644 index 0000000..a84bc57 --- /dev/null +++ b/src/infrastructure/api/orders/root.py @@ -0,0 +1,16 @@ +from fastapi import APIRouter +import platform + +router = APIRouter() + +@router.get("/") +def root(): + uname = platform.uname() + return { + "machine": { + "OS": f"{uname.system} {uname.release}", + "Arch": uname.machine + }, + "Status": "Running", + "Docs": "/docs" + } diff --git a/src/infrastructure/api/orders/router.py b/src/infrastructure/api/orders/router.py new file mode 100644 index 0000000..4a52cd3 --- /dev/null +++ b/src/infrastructure/api/orders/router.py @@ -0,0 +1,17 @@ +from fastapi import APIRouter +from infrastructure.api.orders.orders import router as orders_router +from infrastructure.api.orders.root import router as root_router + +router = APIRouter() + +router.include_router( + orders_router, + prefix="/orders", + tags=["orders"] +) + +router.include_router( + root_router, + prefix='', + tags=["root"] +) diff --git a/src/main.py b/src/main.py index aa15ff6..a4c4fcd 100644 --- a/src/main.py +++ b/src/main.py @@ -1,17 +1,52 @@ -from infrastructure.api.users.app import create_app +from infrastructure.api.users.app import create_app as create_users_app +from infrastructure.api.orders.app import create_app as create_orders_app from core.config import ConfSettings +import threading +import uvicorn -app = create_app() - -def run(): - import uvicorn +def run_users_api(): + """Ejecuta la API de usuarios en puerto 8000""" + app_users = create_users_app() uvicorn.run( - "main:app", + app_users, host=ConfSettings.host, - port=ConfSettings.port, - reload=ConfSettings.reload, + port=8000, + reload=False, log_level=ConfSettings.log_level, ) +def run_orders_api(): + """Ejecuta la API de pedidos en puerto 8001""" + app_orders = create_orders_app() + uvicorn.run( + app_orders, + host=ConfSettings.host, + port=8001, + reload=False, + log_level=ConfSettings.log_level, + ) + +def run(): + """Inicia ambas APIs en threads separados""" + print("Iniciando microservicios...") + + users_thread = threading.Thread(target=run_users_api, daemon=True, name="Users-API") + orders_thread = threading.Thread(target=run_orders_api, daemon=True, name="Orders-API") + + users_thread.start() + orders_thread.start() + + print("✓ API de Usuarios ejecutándose en http://0.0.0.0:8000") + print("✓ API de Pedidos ejecutándose en http://0.0.0.0:8001") + print("\nDocumentación disponible en:") + print(" - Usuarios: http://localhost:8000/docs") + print(" - Pedidos: http://localhost:8001/docs") + + try: + users_thread.join() + orders_thread.join() + except KeyboardInterrupt: + print("\n\nAteniendo señal de salida...") + if __name__ == "__main__": run() \ No newline at end of file diff --git a/src/main_orders.py b/src/main_orders.py new file mode 100644 index 0000000..00d78d7 --- /dev/null +++ b/src/main_orders.py @@ -0,0 +1,17 @@ +from infrastructure.api.orders.app import create_app +from core.config import ConfSettings + +app = create_app() + +def run(): + import uvicorn + uvicorn.run( + "main_orders:app", + host=ConfSettings.host, + port=8001, + reload=ConfSettings.reload, + log_level=ConfSettings.log_level, + ) + +if __name__ == "__main__": + run() diff --git a/src/users.db b/src/users.db index c120b74..0a471ff 100644 Binary files a/src/users.db and b/src/users.db differ