Added routers
This commit is contained in:
		
							
								
								
									
										24
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								README.md
									
									
									
									
									
								
							| @@ -16,6 +16,7 @@ This is a template for building APIs with Clean Architecture. It contains two ex | |||||||
| ## Prepare | ## Prepare | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
|  | cp envExample .env | ||||||
| make build network | make build network | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| @@ -25,20 +26,27 @@ make build network | |||||||
| make api.fastapi.run | 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 | ## Run Flask | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| make api.flask.run | make api.flask.run | ||||||
| ``` | ``` | ||||||
|  | ## API | ||||||
|  |  | ||||||
| Now, you can test the API with the following command: | ### Welcome | ||||||
|  |  | ||||||
| ```bash | ```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
									
								
							
							
						
						
									
										0
									
								
								envExample
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										37
									
								
								src/core/use_case/alerts_use_case.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/core/use_case/alerts_use_case.py
									
									
									
									
									
										Normal 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", | ||||||
|  |             ]), | ||||||
|  |             } | ||||||
|  |         } | ||||||
| @@ -2,3 +2,4 @@ FROM base-core-app | |||||||
|  |  | ||||||
| # launcher | # launcher | ||||||
| ENTRYPOINT flask --app main run --host=0.0.0.0 --debug | ENTRYPOINT flask --app main run --host=0.0.0.0 --debug | ||||||
|  | #ENTRYPOINT gunicorn main:app --worker-class gevent --bind 0.0.0.0:5000 | ||||||
|   | |||||||
| @@ -1,21 +1,20 @@ | |||||||
| import os | 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.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__) | app = Flask(__name__) | ||||||
|  |  | ||||||
| register_middlewares(app) | register_middlewares(app) | ||||||
|  |  | ||||||
|  | # Blueprints | ||||||
|  | app.register_blueprint(documents_routes) | ||||||
|  | app.register_blueprint(sse_routes) | ||||||
|  |  | ||||||
| @app.route("/api/v1/documents/") | @app.route('/') | ||||||
| def documents_list(): | def index(): | ||||||
|     storage = AzureStorage( |     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.'} | ||||||
|         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) |  | ||||||
|   | |||||||
| @@ -69,6 +69,7 @@ def register_middlewares(app): | |||||||
|         """ |         """ | ||||||
|         Convert the keys of the response to camel case (snake_case to camelCase) in all levels |         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( |             response.data = json.dumps( | ||||||
|                 convert_keys_to_camel_case(json.loads(response.data)) |                 convert_keys_to_camel_case(json.loads(response.data)) | ||||||
|             ) |             ) | ||||||
| @@ -79,6 +80,7 @@ def register_middlewares(app): | |||||||
|         """ |         """ | ||||||
|         Convert the response to JSON format |         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" |             response.headers["Content-Type"] = "application/json" | ||||||
|         return response |         return response | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1 +1,3 @@ | |||||||
| flask | flask | ||||||
|  | gunicorn | ||||||
|  | gevent | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								src/infra/api/flask/routes/api_documents_routes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/infra/api/flask/routes/api_documents_routes.py
									
									
									
									
									
										Normal 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) | ||||||
							
								
								
									
										24
									
								
								src/infra/api/flask/routes/sse_routes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/infra/api/flask/routes/sse_routes.py
									
									
									
									
									
										Normal 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) | ||||||
		Reference in New Issue
	
	Block a user