feat: Semaine 8
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
Dockerfile
|
||||
__pycache__
|
||||
*.pyc
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN pip install fastapi uvicorn httpx
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
@@ -0,0 +1,34 @@
|
||||
import time
|
||||
|
||||
class CircuitBreaker:
|
||||
def __init__(self, failure_threshold: int = 3, recovery_timeout: int = 10) -> None:
|
||||
self.failure_threshold = failure_threshold
|
||||
self.recovery_timeout = recovery_timeout
|
||||
self.failure_count = 0
|
||||
self.last_failure_time = None
|
||||
self.state = "CLOSED" # OPEN, CLOSED, HALF-OPEN
|
||||
|
||||
def call(self, function, *args, **kwargs):
|
||||
if self.state == "OPEN" and self.last_failure_time is not None:
|
||||
if time.time() - self.last_failure_time >= self.recovery_timeout:
|
||||
self.state = "HALF-OPEN"
|
||||
else:
|
||||
raise Exception("Circuit is open")
|
||||
|
||||
try:
|
||||
result = function(*args, **kwargs)
|
||||
self.on_success()
|
||||
return result
|
||||
except Exception as error:
|
||||
self.on_failure()
|
||||
raise error
|
||||
|
||||
def on_success(self):
|
||||
self.failure_count = 0
|
||||
self.state = "CLOSED"
|
||||
|
||||
def on_failure(self):
|
||||
self.failure_count += 1
|
||||
self.last_failure_time = time.time()
|
||||
if self.failure_count >= self.failure_threshold:
|
||||
self.state = "OPEN"
|
||||
@@ -0,0 +1,89 @@
|
||||
import httpx
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from circuit_breaker import CircuitBreaker
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
USER_SERVICE_URL = "http://user-service:8000"
|
||||
CATALOG_SERVICE_URL = "http://catalog-service:8000"
|
||||
|
||||
orders_db = {}
|
||||
next_id = 1
|
||||
|
||||
catalog_cb = CircuitBreaker(failure_threshold=3, recovery_timeout=10)
|
||||
|
||||
class CreateOrderDTO(BaseModel):
|
||||
user_id: int
|
||||
product_id: int
|
||||
quantity: int
|
||||
|
||||
TIMEOUT = 5.0
|
||||
|
||||
def get_product(product_id: int):
|
||||
return catalog_cb.call(
|
||||
httpx.get,
|
||||
f"{CATALOG_SERVICE_URL}/products/{product_id}",
|
||||
timeout=TIMEOUT
|
||||
)
|
||||
|
||||
@app.post("/orders", status_code=201)
|
||||
async def create_order(payload: CreateOrderDTO):
|
||||
global next_id
|
||||
|
||||
user_response = httpx.get(f"{USER_SERVICE_URL}/users/{payload.user_id}")
|
||||
try:
|
||||
user_response = httpx.get(f"{USER_SERVICE_URL}/users/{payload.user_id}", timeout=TIMEOUT)
|
||||
except httpx.TimeoutException:
|
||||
raise HTTPException(status_code=503, detail="User service unavailable")
|
||||
if user_response.status_code == 404:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
user = user_response.json()
|
||||
|
||||
try:
|
||||
product_response = get_product(payload.product_id)
|
||||
except httpx.TimeoutException:
|
||||
raise HTTPException(status_code=503, detail="Catalog service unavailable")
|
||||
if product_response.status_code == 404:
|
||||
raise HTTPException(status_code=404, detail="Product not found")
|
||||
|
||||
product = product_response.json()
|
||||
|
||||
try:
|
||||
update_response = httpx.put(f"{CATALOG_SERVICE_URL}/products/{payload.product_id}/stock", json= {"quantity": payload.quantity}, timeout=TIMEOUT)
|
||||
except httpx.TimeoutException:
|
||||
raise HTTPException(status_code=503, detail="Catalog service unavailable")
|
||||
if update_response.status_code == 400:
|
||||
raise HTTPException(status_code= 400, detail= "Stock Insuffisant")
|
||||
if update_response.status_code != 200:
|
||||
raise HTTPException(status_code= 502, detail= "Erreur catalog-service")
|
||||
|
||||
order_id = next_id
|
||||
next_id += 1
|
||||
|
||||
new_order = {
|
||||
"id": order_id,
|
||||
"user_id": user["id"],
|
||||
"user_name": user["name"],
|
||||
"product_id": product["id"],
|
||||
"product_name": product["name"],
|
||||
"unit_price": product["price"],
|
||||
"quantity": payload.quantity,
|
||||
"total_price": product["price"] * payload.quantity
|
||||
}
|
||||
|
||||
orders_db[order_id] = new_order
|
||||
return new_order
|
||||
|
||||
@app.get("/orders/{order_id}")
|
||||
async def get_order(order_id: int):
|
||||
order = orders_db.get(order_id)
|
||||
if not order:
|
||||
raise HTTPException(status_code=404, detail="Order not found")
|
||||
return order
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
return {"status": "ok", "service": "order-service"}
|
||||
Reference in New Issue
Block a user