Skip to content

Commit 74733c0

Browse files
Introduce interfaces
1 parent e70e666 commit 74733c0

30 files changed

+1449
-1984
lines changed

lib/api.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
"""
2-
This is the main API file for the RocketPy API.
3-
"""
4-
51
from fastapi import FastAPI, Request, status
62
from fastapi.exceptions import RequestValidationError
7-
from fastapi.middleware.cors import CORSMiddleware
83
from fastapi.openapi.utils import get_openapi
94
from fastapi.responses import RedirectResponse, JSONResponse
105

@@ -21,14 +16,6 @@
2116
"syntaxHighlight.theme": "obsidian",
2217
}
2318
)
24-
25-
app.add_middleware(
26-
CORSMiddleware,
27-
allow_origins=["*"],
28-
allow_credentials=True,
29-
allow_methods=["*"],
30-
allow_headers=["*"],
31-
)
3219
app.include_router(flight.router)
3320
app.include_router(environment.router)
3421
app.include_router(motor.router)
@@ -46,7 +33,7 @@ def custom_openapi():
4633
return app.openapi_schema
4734
openapi_schema = get_openapi(
4835
title="RocketPy Infinity-API",
49-
version="2.2.0",
36+
version="3.0.0",
5037
description=(
5138
"<p style='font-size: 18px;'>RocketPy Infinity-API is a RESTful Open API for RocketPy, a rocket flight simulator.</p>"
5239
"<br/>"
@@ -87,7 +74,7 @@ async def __perform_healthcheck():
8774
return {"health": "Everything OK!"}
8875

8976

90-
# Errors
77+
# Global exception handler
9178
@app.exception_handler(RequestValidationError)
9279
async def validation_exception_handler(
9380
request: Request, exc: RequestValidationError

lib/controllers/environment.py

Lines changed: 23 additions & 236 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,29 @@
1-
from typing import Union
2-
3-
from fastapi import HTTPException, status
4-
from pymongo.errors import PyMongoError
5-
6-
from lib import logger, parse_error
7-
from lib.models.environment import Env
8-
from lib.services.environment import EnvironmentService
9-
from lib.repositories.environment import EnvRepository
10-
from lib.views.environment import (
11-
EnvSummary,
12-
EnvCreated,
13-
EnvDeleted,
14-
EnvUpdated,
1+
from lib.controllers.interface import (
2+
ControllerInterface,
3+
controller_exception_handler,
154
)
5+
from lib.views.environment import EnvSummary
6+
from lib.models.environment import EnvironmentModel
7+
from lib.services.environment import EnvironmentService
168

179

18-
class EnvController:
10+
class EnvironmentController(ControllerInterface):
1911
"""
2012
Controller for the Environment model.
2113
2214
Enables:
23-
- Simulation of a RocketPy Environment from models.Env
24-
- CRUD operations over models.Env on the database
15+
- Simulation of a RocketPy Environment.
16+
- CRUD for Environment BaseApiModel.
2517
"""
2618

27-
@staticmethod
28-
async def create_env(env: Env) -> Union[EnvCreated, HTTPException]:
29-
"""
30-
Create a env in the database.
19+
def __init__(self):
20+
super().__init__(models=[EnvironmentModel])
3121

32-
Returns:
33-
views.EnvCreated
34-
"""
35-
env_repo = None
36-
try:
37-
async with EnvRepository(env) as env_repo:
38-
await env_repo.create_env()
39-
except PyMongoError as e:
40-
logger.error(
41-
f"controllers.environment.create_env: PyMongoError {e}"
42-
)
43-
raise HTTPException(
44-
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
45-
detail="Failed to create environment in db",
46-
) from e
47-
except HTTPException as e:
48-
raise e from e
49-
except Exception as e:
50-
exc_str = parse_error(e)
51-
logger.error(f"controllers.environment.create_env: {exc_str}")
52-
raise HTTPException(
53-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
54-
detail=f"Failed to create environment: {exc_str}",
55-
) from e
56-
else:
57-
return EnvCreated(env_id=env_repo.env_id)
58-
finally:
59-
env_id = (
60-
getattr(env_repo, 'env_id', 'unknown')
61-
if env_repo
62-
else 'unknown'
63-
)
64-
if env_repo:
65-
logger.info(
66-
f"Call to controllers.environment.create_env completed for Env {env_id}"
67-
)
68-
69-
@staticmethod
70-
async def get_env_by_id(env_id: str) -> Union[Env, HTTPException]:
71-
"""
72-
Get a env from the database.
73-
74-
Args:
75-
env_id: str
76-
77-
Returns:
78-
models.Env
79-
80-
Raises:
81-
HTTP 404 Not Found: If the env is not found in the database.
82-
"""
83-
try:
84-
async with EnvRepository() as env_repo:
85-
await env_repo.get_env_by_id(env_id)
86-
env = env_repo.env
87-
except PyMongoError as e:
88-
logger.error(
89-
f"controllers.environment.get_env_by_id: PyMongoError {e}"
90-
)
91-
raise HTTPException(
92-
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
93-
detail="Failed to read environment from db",
94-
) from e
95-
except HTTPException as e:
96-
raise e from e
97-
except Exception as e:
98-
exc_str = parse_error(e)
99-
logger.error(f"controllers.environment.get_env_by_id: {exc_str}")
100-
raise HTTPException(
101-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
102-
detail=f"Failed to read environment: {exc_str}",
103-
) from e
104-
else:
105-
if env:
106-
return env
107-
raise HTTPException(
108-
status_code=status.HTTP_404_NOT_FOUND,
109-
detail="Environment not found",
110-
)
111-
finally:
112-
logger.info(
113-
f"Call to controllers.environment.get_env_by_id completed for Env {env_id}"
114-
)
115-
116-
@classmethod
22+
@controller_exception_handler
11723
async def get_rocketpy_env_binary(
118-
cls,
24+
self,
11925
env_id: str,
120-
) -> Union[bytes, HTTPException]:
26+
) -> bytes:
12127
"""
12228
Get rocketpy.Environmnet dill binary.
12329
@@ -130,117 +36,14 @@ async def get_rocketpy_env_binary(
13036
Raises:
13137
HTTP 404 Not Found: If the env is not found in the database.
13238
"""
133-
try:
134-
env = await cls.get_env_by_id(env_id)
135-
env_service = EnvironmentService.from_env_model(env)
136-
except HTTPException as e:
137-
raise e from e
138-
except Exception as e:
139-
exc_str = parse_error(e)
140-
logger.error(
141-
f"controllers.environment.get_rocketpy_env_as_binary: {exc_str}"
142-
)
143-
raise HTTPException(
144-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
145-
detail=f"Failed to read environment: {exc_str}",
146-
) from e
147-
else:
148-
return env_service.get_env_binary()
149-
finally:
150-
logger.info(
151-
f"Call to controllers.environment.get_rocketpy_env_binary completed for Env {env_id}"
152-
)
153-
154-
@staticmethod
155-
async def update_env_by_id(
156-
env_id: str, env: Env
157-
) -> Union[EnvUpdated, HTTPException]:
158-
"""
159-
Update a models.Env in the database.
160-
161-
Args:
162-
env_id: str
163-
164-
Returns:
165-
views.EnvUpdated
166-
167-
Raises:
168-
HTTP 404 Not Found: If the env is not found in the database.
169-
"""
170-
try:
171-
async with EnvRepository(env) as env_repo:
172-
await env_repo.update_env_by_id(env_id)
173-
except PyMongoError as e:
174-
logger.error(
175-
f"controllers.environment.update_env: PyMongoError {e}"
176-
)
177-
raise HTTPException(
178-
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
179-
detail="Failed to update environment from db",
180-
) from e
181-
except HTTPException as e:
182-
raise e from e
183-
except Exception as e:
184-
exc_str = parse_error(e)
185-
logger.error(f"controllers.environment.update_env: {exc_str}")
186-
raise HTTPException(
187-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
188-
detail=f"Failed to update environment: {exc_str}",
189-
) from e
190-
else:
191-
return EnvUpdated(env_id=env_id)
192-
finally:
193-
logger.info(
194-
f"Call to controllers.environment.update_env completed for Env {env_id}"
195-
)
196-
197-
@staticmethod
198-
async def delete_env_by_id(
199-
env_id: str,
200-
) -> Union[EnvDeleted, HTTPException]:
201-
"""
202-
Delete a models.Env from the database.
203-
204-
Args:
205-
env_id: str
206-
207-
Returns:
208-
views.EnvDeleted
209-
210-
Raises:
211-
HTTP 404 Not Found: If the env is not found in the database.
212-
"""
213-
try:
214-
async with EnvRepository() as env_repo:
215-
await env_repo.delete_env_by_id(env_id)
216-
except PyMongoError as e:
217-
logger.error(
218-
f"controllers.environment.delete_env: PyMongoError {e}"
219-
)
220-
raise HTTPException(
221-
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
222-
detail="Failed to delete environment from db",
223-
) from e
224-
except HTTPException as e:
225-
raise e from e
226-
except Exception as e:
227-
exc_str = parse_error(e)
228-
logger.error(f"controllers.environment.delete_env: {exc_str}")
229-
raise HTTPException(
230-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
231-
detail=f"Failed to delete environment: {exc_str}",
232-
) from e
233-
else:
234-
return EnvDeleted(env_id=env_id)
235-
finally:
236-
logger.info(
237-
f"Call to controllers.environment.delete_env completed for Env {env_id}"
238-
)
39+
env = await self.get_env_by_id(env_id)
40+
env_service = EnvironmentService.from_env_model(env)
41+
return env_service.get_env_binary()
23942

240-
@classmethod
43+
@controller_exception_handler
24144
async def simulate_env(
242-
cls, env_id: str
243-
) -> Union[EnvSummary, HTTPException]:
45+
self, env_id: str
46+
) -> EnvSummary:
24447
"""
24548
Simulate a rocket environment.
24649
@@ -253,22 +56,6 @@ async def simulate_env(
25356
Raises:
25457
HTTP 404 Not Found: If the env does not exist in the database.
25558
"""
256-
try:
257-
env = await cls.get_env_by_id(env_id)
258-
env_service = EnvironmentService.from_env_model(env)
259-
env_summary = env_service.get_env_summary()
260-
except HTTPException as e:
261-
raise e from e
262-
except Exception as e:
263-
exc_str = parse_error(e)
264-
logger.error(f"controllers.environment.simulate: {exc_str}")
265-
raise HTTPException(
266-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
267-
detail=f"Failed to simulate environment, parameters may contain data that is not physically coherent: {exc_str}",
268-
) from e
269-
else:
270-
return env_summary
271-
finally:
272-
logger.info(
273-
f"Call to controllers.environment.simulate completed for Env {env_id}"
274-
)
59+
env = await self.get_env_by_id(env_id)
60+
env_service = EnvironmentService.from_env_model(env)
61+
return env_service.get_env_summary()

0 commit comments

Comments
 (0)