add input output port to view cart as example. organize usecases in interactors and interfaces
parent
821ef8a8d4
commit
0be0187062
|
|
@ -1,6 +1,6 @@
|
|||
#python imports
|
||||
from typing import List
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
#dependency imports
|
||||
from .cart_item import CartItem
|
||||
|
|
@ -9,6 +9,7 @@ from .product import Product
|
|||
@dataclass
|
||||
class Cart:
|
||||
items: List[CartItem]
|
||||
total_price: float = field(init=False, default=0.0)
|
||||
|
||||
def add_item(self, product: Product, quantity: int):
|
||||
for item in self.items:
|
||||
|
|
@ -21,7 +22,7 @@ class Cart:
|
|||
self.items = [item for item in self.items if item.product.id != product_id]
|
||||
|
||||
def calculate_total_price(self) -> float:
|
||||
return sum(item.calculate_total_price() for item in self.items)
|
||||
self.total_price = sum(item.calculate_total_price() for item in self.items)
|
||||
|
||||
def list_items(self) -> List[str]:
|
||||
return [f"{item.quantity} x {item.product.name} - {item.calculate_total_price()} EUR" for item in self.items]
|
||||
|
|
@ -7,9 +7,12 @@ const Controller = {
|
|||
},
|
||||
|
||||
async showCart() {
|
||||
const userId = 1; // Hardcoded for simplicity
|
||||
const cart = await Model.fetchCart(userId);
|
||||
View.renderCart(cart);
|
||||
const userId = "1"; // Hardcoded for simplicity
|
||||
const cartResponse = await Model.fetchCart(userId);
|
||||
if(cartResponse.status=="success"){
|
||||
const cart = cartResponse.data;
|
||||
View.renderCart(cart);
|
||||
}
|
||||
},
|
||||
|
||||
async showProduct(productId) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
from use_cases.interfaces import BaseInputPort, BaseOutputPort
|
||||
|
||||
class BaseController:
|
||||
def __init__(self, input_port: BaseInputPort) -> None:
|
||||
self.input_port = input_port
|
||||
|
||||
def handle_request(self, request) -> dict:
|
||||
"""Handles the incoming API request and delegates to the use case."""
|
||||
return self.input_port.execute(request)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
from typing import Type
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from .base_controller import BaseController
|
||||
from use_cases.interfaces import BaseOutputPort, BaseInputPort, BaseRepository
|
||||
|
||||
class Endpoint:
|
||||
def __init__(self,
|
||||
router: APIRouter,
|
||||
controller_impl: Type[BaseController],
|
||||
presenter_impl: Type[BaseOutputPort],
|
||||
repository_impl: Type[BaseRepository],
|
||||
use_case_impl: Type[BaseInputPort]
|
||||
):
|
||||
self.router = router
|
||||
self.presenter = presenter_impl()
|
||||
self.repository = repository_impl()
|
||||
self.use_case = use_case_impl(repository=self.repository, output_port=self.presenter)
|
||||
self.controller = controller_impl(input_port=self.use_case)
|
||||
|
||||
def create(self, route: str, request_model: Type, response_model: Type):
|
||||
@self.router.post(route, response_model=response_model)
|
||||
async def endpoint(request: request_model):
|
||||
try:
|
||||
response = self.controller.handle_request(request)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
|
@ -3,18 +3,63 @@ from fastapi import APIRouter, HTTPException
|
|||
from fastapi.responses import JSONResponse
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
from pydantic import BaseModel
|
||||
#dependency imports
|
||||
from use_cases import ViewCart
|
||||
from interface_adapters.dtos import ViewCartRequestDTO, ViewCartResponseDTO, CartItemDTO
|
||||
from use_cases.interactors import ViewCart
|
||||
from use_cases.interfaces import BaseInputPort, BaseOutputPort
|
||||
from interface_adapters.dtos import ViewCartRequestDTO, ViewCartResponseDTO, CartItemDTO, CartDTO
|
||||
from .base_controller import BaseController
|
||||
|
||||
|
||||
|
||||
#move to main
|
||||
from .base_endpoint import Endpoint
|
||||
from interface_adapters.repositories import SQLCartRepository
|
||||
|
||||
# Initialize components
|
||||
cart_repository = SQLCartRepository()
|
||||
view_cart_use_case = ViewCart(cart_repository)
|
||||
class ViewCartRequest(BaseModel):
|
||||
user_id: str
|
||||
|
||||
class ViewCartResponse(BaseModel):
|
||||
status: str
|
||||
data: CartDTO
|
||||
|
||||
class ViewCartController(BaseController):
|
||||
def __init__(self, input_port: BaseInputPort) -> None:
|
||||
self.input_port = input_port
|
||||
|
||||
def handle_request(self, request) -> dict:
|
||||
"""Handles the incoming API request and delegates to the use case."""
|
||||
return self.input_port.execute(request)
|
||||
|
||||
class ViewCartPresenter(BaseOutputPort):
|
||||
def present(self, output) -> dict:
|
||||
"""Format the response data for API."""
|
||||
cart_items=[CartItemDTO(product_id=item.product_id, name=item.name, quantity=item.quantity, price=item.price) for item in output.items]
|
||||
cart_data = CartDTO(items=cart_items,total_price=output.total_price)
|
||||
response = ViewCartResponse(status="success",data=cart_data)
|
||||
return response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
endpoint = Endpoint(router, ViewCartController, ViewCartPresenter, SQLCartRepository, ViewCart)
|
||||
endpoint.create("/view_cart", ViewCartRequest, ViewCartResponse)
|
||||
|
||||
"""
|
||||
# Initialize components
|
||||
presenter = ViewCartPresenter()
|
||||
repository = SQLCartRepository()
|
||||
use_case = ViewCart(repository=repository, output_port=presenter)
|
||||
controller = ViewCartController(input_port=use_case)
|
||||
|
||||
|
||||
@router.post("/view_cart/")
|
||||
async def view_cart(request: ViewCartRequest):
|
||||
try:
|
||||
result = controller.handle_request(request.user_id)
|
||||
return result # Returns a dictionary which FastAPI will convert to JSON
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@router.post("/view_cart", response_model=ViewCartResponseDTO)
|
||||
async def view_cart(request_dto: ViewCartRequestDTO):
|
||||
|
||||
|
|
@ -29,4 +74,4 @@ async def view_cart(request_dto: ViewCartRequestDTO):
|
|||
)
|
||||
|
||||
return response_dto
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from fastapi import FastAPI, Request
|
|||
from fastapi.responses import HTMLResponse
|
||||
|
||||
#dependency imports
|
||||
from use_cases import ViewProduct, ViewAllProducts
|
||||
from use_cases.interactors import ViewProduct, ViewAllProducts
|
||||
from interface_adapters.dtos import ViewProductRequestDTO, ViewProductResponseDTO, ViewAllProductsResponseDTO, ProductDTO
|
||||
from interface_adapters.repositories import SQLProductRepository
|
||||
|
||||
|
|
|
|||
|
|
@ -6,4 +6,8 @@ class CartItemDTO(BaseModel):
|
|||
product_id: int
|
||||
name: str
|
||||
quantity: int
|
||||
price: float
|
||||
price: float
|
||||
|
||||
class CartDTO(BaseModel):
|
||||
items: List[CartItemDTO]
|
||||
total_price: float
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
from .cart_item_dto import CartItemDTO
|
||||
from .cart_dto import CartItemDTO
|
||||
|
||||
|
||||
class ViewCartResponseDTO(BaseModel):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional, List
|
|||
import sqlite3
|
||||
|
||||
#dependency imports
|
||||
from use_cases import ICartRepository
|
||||
from use_cases.interfaces import ICartRepository
|
||||
from entities import Cart, CartItem
|
||||
|
||||
class SQLCartRepository(ICartRepository):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional, List
|
|||
import sqlite3
|
||||
|
||||
#dependency imports
|
||||
from use_cases import IProductRepository
|
||||
from use_cases.interfaces import IProductRepository
|
||||
from entities import Product
|
||||
|
||||
class SQLProductRepository(IProductRepository):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional, List
|
|||
import sqlite3
|
||||
|
||||
#dependency imports
|
||||
from use_cases import IUserRepository
|
||||
from use_cases.interfaces import IUserRepository
|
||||
from entities import User
|
||||
|
||||
class SQLUserRepository(IUserRepository):
|
||||
|
|
|
|||
|
|
@ -32,3 +32,4 @@ async def root():
|
|||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(app, host="127.0.0.1", port=8000)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional, List
|
|||
|
||||
#dependency imports
|
||||
from entities import Product
|
||||
from .product_repository import IProductRepository
|
||||
from use_cases.interfaces.product_repository import IProductRepository
|
||||
|
||||
class ViewAllProducts:
|
||||
def __init__(self, product_repository: IProductRepository):
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#python imports
|
||||
from typing import Optional
|
||||
|
||||
#dependency imports
|
||||
from entities import Cart, CartItem
|
||||
from use_cases.interfaces.cart_repository import ICartRepository
|
||||
from use_cases.interfaces.base_output_port import BaseOutputPort
|
||||
from use_cases.interfaces.base_input_port import BaseInputPort
|
||||
|
||||
"""
|
||||
class ViewCartInputPort():
|
||||
def execute(self, name: str, email: str) -> None:
|
||||
pass
|
||||
|
||||
class ViewCartOutputPort():
|
||||
def present(self, user: dict) -> dict:
|
||||
pass
|
||||
"""
|
||||
class ViewCart(BaseInputPort):
|
||||
def __init__(self, repository: ICartRepository, output_port: BaseOutputPort):
|
||||
self.repository = repository
|
||||
self.output_port = output_port
|
||||
|
||||
def execute(self, input_data) -> Optional[Cart]:
|
||||
"""
|
||||
Fetches the cart data from the repository.
|
||||
"""
|
||||
if not hasattr(input_data, 'user_id'):
|
||||
raise ValueError("Missing 'user_id' in input_data")
|
||||
cart = self.repository.get_cart_by_user_id(input_data.user_id)
|
||||
if not cart:
|
||||
return None
|
||||
cart.calculate_total_price()
|
||||
return self.output_port.present(cart)
|
||||
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
|
||||
#dependency imports
|
||||
from entities import Product
|
||||
from .product_repository import IProductRepository
|
||||
from use_cases.interfaces.product_repository import IProductRepository
|
||||
|
||||
class ViewProduct:
|
||||
def __init__(self, product_repository: IProductRepository):
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import importlib
|
||||
import pkgutil
|
||||
import inspect
|
||||
|
||||
__all__ = []
|
||||
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
|
||||
module = importlib.import_module(f".{module_name}", __name__)
|
||||
for name, obj in inspect.getmembers(module, inspect.isclass):
|
||||
if obj.__module__ == module.__name__:
|
||||
globals()[name] = obj
|
||||
__all__.append(name)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class BaseInputPort:
|
||||
def execute(self, input_data):
|
||||
"""
|
||||
Abstract method to execute use case.
|
||||
"""
|
||||
raise NotImplementedError("execute must be implemented by a subclass")
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class BaseOutputPort:
|
||||
def present(self, output_data):
|
||||
"""
|
||||
Abstract method to present use case output.
|
||||
"""
|
||||
raise NotImplementedError("present must be implemented by a subclass")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
class BaseRepository:
|
||||
pass
|
||||
|
|
@ -2,8 +2,9 @@ from typing import Optional, List
|
|||
|
||||
#dependency imports
|
||||
from entities import Cart
|
||||
from .base_repository import BaseRepository
|
||||
|
||||
class ICartRepository:
|
||||
class ICartRepository(BaseRepository):
|
||||
def get_cart_by_user_id(self, user_id: int) -> Optional[Cart]:
|
||||
"""
|
||||
Abstract method to fetch cart data by user ID.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#python imports
|
||||
from typing import Optional
|
||||
|
||||
#dependency imports
|
||||
from entities import Cart, CartItem
|
||||
from .cart_repository import ICartRepository
|
||||
|
||||
class ViewCart:
|
||||
def __init__(self, cart_repository: ICartRepository):
|
||||
self.cart_repository = cart_repository
|
||||
|
||||
def execute(self, user_id: int) -> Optional[Cart]:
|
||||
"""
|
||||
Fetches the cart data from the repository.
|
||||
"""
|
||||
cart_data = self.cart_repository.get_cart_by_user_id(user_id)
|
||||
if not cart_data:
|
||||
return None
|
||||
|
||||
return cart_data
|
||||
Loading…
Reference in New Issue