Added routers

This commit is contained in:
Andros Fenollosa 2025-02-07 08:28:39 +01:00
parent 6a7abd3f95
commit 5fddd43c02
9 changed files with 113 additions and 24 deletions

View File

@ -16,6 +16,7 @@ This is a template for building APIs with Clean Architecture. It contains two ex
## Prepare
```bash
cp envExample .env
make build network
```
@ -25,20 +26,27 @@ make build network
make api.fastapi.run
```
Now, you can test the API with the following command:
```bash
curl -X 'GET' 'http://localhost:8000/api/v1/documents/?appName=app_test&clientId=client_test' -H 'accept: application/json' | jq
```
## Run Flask
```bash
make api.flask.run
```
## API
Now, you can test the API with the following command:
### Welcome
```bash
curl -X 'GET' 'http://localhost:5000/api/v1/documents/?appName=app_test&clientId=client_test' -H 'accept: application/json' | jq
curl -X 'GET' 'http://localhost:5000' -H 'accept: application/json'
```
### Documents
```bash
curl -X 'GET' 'http://localhost:5000/api/v1/documents/?appName=app_test&clientId=client_test' -H 'accept: application/json'
```
## SEE
```bash
curl -N -H "Accept:text/event-stream" http://localhost:5000/sse/alerts/
```

0
envExample Normal file
View File

View File

@ -0,0 +1,37 @@
from pydantic import BaseModel, StrictStr, StrictInt
from src.core.decorators import check_params
from faker import Faker
from src.core.entity.ResponseTypes import ResponseTypes
import random
class GetNextAlertModel(BaseModel):
type_alert: StrictStr
point: StrictInt
faker = Faker()
@check_params(GetNextAlertModel)
def get_next_alert(params) -> dict:
"""
Get next alert
:param GetNextAlertModel params:
:return: dict
"""
return {
"type": ResponseTypes.SUCCESS,
"error": None,
"data": {
"id": faker.uuid4(),
"type": random.choice(["ok", "warning", "danger"]),
"message": random.choice([
f"The node number {random.randint(1, 8)} needs attention",
f"The connection with the node number {random.randint(1, 8)} is unstable",
f"The node number {random.randint(1, 8)} is offline",
f"The node number {random.randint(1, 8)} is overheating",
f"The node number {random.randint(1, 8)} is under maintenance",
]),
}
}

View File

@ -2,3 +2,4 @@ FROM base-core-app
# launcher
ENTRYPOINT flask --app main run --host=0.0.0.0 --debug
#ENTRYPOINT gunicorn main:app --worker-class gevent --bind 0.0.0.0:5000

View File

@ -1,21 +1,20 @@
import os
import time
import random
from flask import Flask, request
from flask import Flask, request, Response
from src.core.use_case import documents_use_case
from src.infra.storage.AzureStorage import AzureStorage
from src.infra.api.flask.middlewares import register_middlewares
from src.infra.api.flask.routes.sse_routes import sse_routes
from src.infra.api.flask.routes.api_documents_routes import documents_routes
app = Flask(__name__)
register_middlewares(app)
# Blueprints
app.register_blueprint(documents_routes)
app.register_blueprint(sse_routes)
@app.route("/api/v1/documents/")
def documents_list():
storage = AzureStorage(
account_name=os.getenv("AZURE_STORAGE_ACCOUNT_NAME", ""),
account_key=os.getenv("AZURE_STORAGE_ACCOUNT_KEY", ""),
)
params = request.args
return documents_use_case.documents_list(storage=storage, params=params)
@app.route('/')
def index():
return {'Welcome to Clean Architecture template!': 'The endpoint you are looking for is not in this castle. Read the documentation to find the right path.'}

View File

@ -69,6 +69,7 @@ def register_middlewares(app):
"""
Convert the keys of the response to camel case (snake_case to camelCase) in all levels
"""
if response.status_code == 200 and request.headers.get("Accept", None) != "text/event-stream":
response.data = json.dumps(
convert_keys_to_camel_case(json.loads(response.data))
)
@ -79,6 +80,7 @@ def register_middlewares(app):
"""
Convert the response to JSON format
"""
if response.status_code == 200 and request.headers.get("Accept", None) != "text/event-stream":
response.headers["Content-Type"] = "application/json"
return response

View File

@ -1 +1,3 @@
flask
gunicorn
gevent

View File

@ -0,0 +1,16 @@
import os
from flask import Blueprint, request
from src.core.use_case import documents_use_case
from src.infra.storage.AzureStorage import AzureStorage
documents_routes = Blueprint("api_documents_routes", __name__, url_prefix="/api/v1/documents")
@documents_routes.route("/")
def documents_list():
storage = AzureStorage(
account_name=os.getenv("AZURE_STORAGE_ACCOUNT_NAME", ""),
account_key=os.getenv("AZURE_STORAGE_ACCOUNT_KEY", ""),
)
params = request.args
return documents_use_case.documents_list(storage=storage, params=params)

View File

@ -0,0 +1,24 @@
import time
import random
from flask import Blueprint, Response
from src.core.use_case import alerts_use_case
sse_routes = Blueprint("sse_routes", __name__, url_prefix="/sse")
@sse_routes.route("/alerts/", methods=["GET"])
def sse_alerts():
"""
SSE endpoint to stream alerts
"""
def generate():
try:
while True:
new_data = alerts_use_case.get_next_alert(params={"type_alert": "test", "point": 1})['data']
yield f"data: {new_data}\n\n"
time.sleep(random.randint(1, 5))
except GeneratorExit:
print("SSE stream closed")
except Exception as e:
print(f"Error in SSE stream: {e}")
return Response(generate(), content_type="text/event-stream", status=200)