Skip to content

Commit 3eed2f6

Browse files
increase version
extends opentelemetry instrumentation properly threat mongo db errors refactors environment summary serialization Minor refactoring - Enhances error handling and logging - Fine tune hybrid locks on main repository initialization and finalization refactor repositories accesses fine tunes db config turn off db connection close fixes repo name fixes get environment call on flight route
1 parent 49a629b commit 3eed2f6

File tree

17 files changed

+453
-195
lines changed

17 files changed

+453
-195
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ RUN apt-get update && \
1616

1717
COPY ./lib /app/lib
1818

19-
CMD ["gunicorn", "-c", "lib/settings/gunicorn.py", "-w", "1", "--threads=2", "-k", "uvicorn.workers.UvicornWorker", "lib.api:app", "--log-level", "Debug", "-b", "0.0.0.0:3000", "--timeout", "30"]
19+
CMD ["gunicorn", "-c", "lib/settings/gunicorn.py", "-w", "1", "--threads=2", "-k", "uvicorn.workers.UvicornWorker", "lib.api:app", "--log-level", "Debug", "-b", "0.0.0.0:3000", "--timeout", "35"]

lib/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ def parse_error(error):
2323
exc_type = type(error).__name__
2424
exc_obj = f"{error}".replace("\n", " ").replace(" ", " ")
2525
return f"{exc_type} exception: {exc_obj}"
26+
27+
28+
from lib.api import app

lib/api.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from fastapi.openapi.utils import get_openapi
1010
from fastapi.responses import RedirectResponse, JSONResponse
1111

12+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
13+
from opentelemetry.instrumentation.requests import RequestsInstrumentor
14+
1215
from lib import logger, parse_error
1316
from lib.routes import flight, environment, motor, rocket
1417

@@ -30,6 +33,9 @@
3033
app.include_router(motor.router)
3134
app.include_router(rocket.router)
3235

36+
FastAPIInstrumentor.instrument_app(app)
37+
RequestsInstrumentor().instrument()
38+
3339
# Compress responses above 1KB
3440
app.add_middleware(GZipMiddleware, minimum_size=1000)
3541

@@ -39,7 +45,7 @@ def custom_openapi():
3945
return app.openapi_schema
4046
openapi_schema = get_openapi(
4147
title="RocketPy Infinity-API",
42-
version="1.2.0 BETA",
48+
version="1.2.2 BETA",
4349
description=(
4450
"<p style='font-size: 18px;'>RocketPy Infinity-API is a RESTful Open API for RocketPy, a rocket flight simulator.</p>"
4551
"<br/>"

lib/controllers/environment.py

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
from typing import Union
22

33
import jsonpickle
4-
from rocketpy.environment.environment import Environment as RocketPyEnvironment
54
from fastapi import HTTPException, status
5+
from pymongo.errors import PyMongoError
66

77
from lib import logger, parse_error
88
from lib.models.environment import Env
9+
from lib.services.environment import EnvironmentService
910
from lib.repositories.environment import EnvRepository
1011
from lib.views.environment import (
1112
EnvSummary,
12-
EnvData,
13-
EnvPlots,
1413
EnvCreated,
1514
EnvDeleted,
1615
EnvUpdated,
@@ -26,7 +25,7 @@ class EnvController:
2625
env: models.Env
2726
2827
Enables:
29-
- Simulation of RocketPyEnvironment from models.Env
28+
- Simulation of a RocketPy Environment from models.Env
3029
- CRUD operations over models.Env on the database
3130
"""
3231

@@ -41,25 +40,6 @@ def env(self) -> Env:
4140
def env(self, env: Env):
4241
self._env = env
4342

44-
@staticmethod
45-
def get_rocketpy_env(env: Env) -> RocketPyEnvironment:
46-
"""
47-
Get the rocketpy env object.
48-
49-
Returns:
50-
RocketPyEnvironment
51-
"""
52-
rocketpy_env = RocketPyEnvironment(
53-
latitude=env.latitude,
54-
longitude=env.longitude,
55-
elevation=env.elevation,
56-
date=env.date,
57-
)
58-
rocketpy_env.set_atmospheric_model(
59-
type=env.atmospheric_model_type, file=env.atmospheric_model_file
60-
)
61-
return rocketpy_env
62-
6343
async def create_env(self) -> Union[EnvCreated, HTTPException]:
6444
"""
6545
Create a env in the database.
@@ -71,6 +51,16 @@ async def create_env(self) -> Union[EnvCreated, HTTPException]:
7151
async with EnvRepository() as env_repo:
7252
env_repo.fetch_env(self.env)
7353
await env_repo.create_env()
54+
except PyMongoError as e:
55+
logger.error(
56+
f"controllers.environment.create_env: PyMongoError {e}"
57+
)
58+
raise HTTPException(
59+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
60+
detail="Failed to create environment in db",
61+
) from e
62+
except HTTPException as e:
63+
raise e from e
7464
except Exception as e:
7565
exc_str = parse_error(e)
7666
logger.error(f"controllers.environment.create_env: {exc_str}")
@@ -103,6 +93,16 @@ async def get_env_by_id(env_id: str) -> Union[Env, HTTPException]:
10393
async with EnvRepository() as env_repo:
10494
await env_repo.get_env_by_id(env_id)
10595
read_env = env_repo.env
96+
except PyMongoError as e:
97+
logger.error(
98+
f"controllers.environment.get_env_by_id: PyMongoError {e}"
99+
)
100+
raise HTTPException(
101+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
102+
detail="Failed to read environment from db",
103+
) from e
104+
except HTTPException as e:
105+
raise e from e
106106
except Exception as e:
107107
exc_str = parse_error(e)
108108
logger.error(f"controllers.environment.get_env_by_id: {exc_str}")
@@ -141,7 +141,7 @@ async def get_rocketpy_env_as_jsonpickle(
141141
"""
142142
try:
143143
read_env = await cls.get_env_by_id(env_id)
144-
rocketpy_env = cls.get_rocketpy_env(read_env)
144+
rocketpy_env = EnvironmentService.from_env_model(read_env)
145145
except HTTPException as e:
146146
raise e from e
147147
except Exception as e:
@@ -182,6 +182,16 @@ async def update_env_by_id(
182182
env_repo.fetch_env(self.env)
183183
await env_repo.create_env()
184184
await env_repo.delete_env_by_id(env_id)
185+
except PyMongoError as e:
186+
logger.error(
187+
f"controllers.environment.update_env: PyMongoError {e}"
188+
)
189+
raise HTTPException(
190+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
191+
detail="Failed to update environment from db",
192+
) from e
193+
except HTTPException as e:
194+
raise e from e
185195
except Exception as e:
186196
exc_str = parse_error(e)
187197
logger.error(f"controllers.environment.update_env: {exc_str}")
@@ -215,6 +225,16 @@ async def delete_env_by_id(
215225
try:
216226
async with EnvRepository() as env_repo:
217227
await env_repo.delete_env_by_id(env_id)
228+
except PyMongoError as e:
229+
logger.error(
230+
f"controllers.environment.delete_env: PyMongoError {e}"
231+
)
232+
raise HTTPException(
233+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
234+
detail="Failed to delete environment from db",
235+
) from e
236+
except HTTPException as e:
237+
raise e from e
218238
except Exception as e:
219239
exc_str = parse_error(e)
220240
logger.error(f"controllers.environment.delete_env: {exc_str}")
@@ -240,24 +260,15 @@ async def simulate_env(
240260
env_id: str.
241261
242262
Returns:
243-
views.EnvSummary
263+
EnvSummary
244264
245265
Raises:
246266
HTTP 404 Not Found: If the env does not exist in the database.
247267
"""
248268
try:
249269
read_env = await cls.get_env_by_id(env_id)
250-
rocketpy_env = cls.get_rocketpy_env(read_env)
251-
252-
env_simulation_numbers = EnvData.parse_obj(
253-
rocketpy_env.all_info_returned()
254-
)
255-
env_simulation_plots = EnvPlots.parse_obj(
256-
rocketpy_env.all_plot_info_returned()
257-
)
258-
env_summary = EnvSummary(
259-
env_data=env_simulation_numbers, env_plots=env_simulation_plots
260-
)
270+
rocketpy_env = EnvironmentService.from_env_model(read_env)
271+
env_summary = rocketpy_env.get_env_summary()
261272
except HTTPException as e:
262273
raise e from e
263274
except Exception as e:

lib/controllers/flight.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Union
22
from fastapi import HTTPException, status
3+
from pymongo.errors import PyMongoError
34

45
from rocketpy.simulation.flight import Flight as RocketPyFlight
56

@@ -31,6 +32,7 @@
3132
from lib.repositories.flight import FlightRepository
3233
from lib.controllers.environment import EnvController
3334
from lib.controllers.rocket import RocketController
35+
from lib.services.environment import EnvironmentService
3436

3537

3638
class FlightController:
@@ -87,7 +89,7 @@ def get_rocketpy_flight(flight: Flight) -> RocketPyFlight:
8789
RocketPyFlight
8890
"""
8991
rocketpy_rocket = RocketController.get_rocketpy_rocket(flight.rocket)
90-
rocketpy_env = EnvController.get_rocketpy_env(flight.environment)
92+
rocketpy_env = EnvironmentService.from_env_model(flight.environment)
9193
rocketpy_flight = RocketPyFlight(
9294
rocket=rocketpy_rocket,
9395
inclination=flight.inclination,
@@ -111,6 +113,14 @@ async def create_flight(self) -> Union[FlightCreated, HTTPException]:
111113
motor_kind=self.motor_kind,
112114
rocket_option=self.rocket_option,
113115
)
116+
except PyMongoError as e:
117+
logger.error(f"controllers.flight.create_flight: PyMongoError {e}")
118+
raise HTTPException(
119+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
120+
detail="Failed to create flight in db",
121+
) from e
122+
except HTTPException as e:
123+
raise e from e
114124
except Exception as e:
115125
exc_str = parse_error(e)
116126
logger.error(f"controllers.flight.create_flight: {exc_str}")
@@ -143,6 +153,16 @@ async def get_flight_by_id(flight_id: str) -> Union[Flight, HTTPException]:
143153
async with FlightRepository() as flight_repo:
144154
await flight_repo.get_flight_by_id(flight_id)
145155
read_flight = flight_repo.flight
156+
except PyMongoError as e:
157+
logger.error(
158+
f"controllers.flight.get_flight_by_id: PyMongoError {e}"
159+
)
160+
raise HTTPException(
161+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
162+
detail="Failed to read flight from db",
163+
) from e
164+
except HTTPException as e:
165+
raise e from e
146166
except Exception as e:
147167
exc_str = parse_error(e)
148168
logger.error(f"controllers.flight.get_flight_by_id: {exc_str}")
@@ -225,6 +245,14 @@ async def update_flight_by_id(
225245
rocket_option=self.rocket_option,
226246
)
227247
await flight_repo.delete_flight_by_id(flight_id)
248+
except PyMongoError as e:
249+
logger.error(f"controllers.flight.update_flight: PyMongoError {e}")
250+
raise HTTPException(
251+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
252+
detail="Failed to update flight in db",
253+
) from e
254+
except HTTPException as e:
255+
raise e from e
228256
except Exception as e:
229257
exc_str = parse_error(e)
230258
logger.error(f"controllers.flight.update_flight: {exc_str}")
@@ -268,6 +296,14 @@ async def update_env_by_flight_id(
268296
rocket_option=read_flight.rocket.rocket_option,
269297
)
270298
await flight_repo.delete_flight_by_id(flight_id)
299+
except PyMongoError as e:
300+
logger.error(
301+
f"controllers.flight.update_env_by_flight_id: PyMongoError {e}"
302+
)
303+
raise HTTPException(
304+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
305+
detail="Failed to update environment from db",
306+
) from e
271307
except HTTPException as e:
272308
raise e from e
273309
except Exception as e:
@@ -315,6 +351,14 @@ async def update_rocket_by_flight_id(
315351
motor_kind=motor_kind, rocket_option=rocket_option
316352
)
317353
await flight_repo.delete_flight_by_id(flight_id)
354+
except PyMongoError as e:
355+
logger.error(
356+
f"controllers.flight.update_rocket_by_flight_id: PyMongoError {e}"
357+
)
358+
raise HTTPException(
359+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
360+
detail="Failed to update rocket from db",
361+
) from e
318362
except HTTPException as e:
319363
raise e from e
320364
except Exception as e:
@@ -350,6 +394,14 @@ async def delete_flight_by_id(
350394
try:
351395
async with FlightRepository() as flight_repo:
352396
await flight_repo.delete_flight_by_id(flight_id)
397+
except PyMongoError as e:
398+
logger.error(f"controllers.flight.delete_flight: PyMongoError {e}")
399+
raise HTTPException(
400+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
401+
detail="Failed to delete flight from db",
402+
) from e
403+
except HTTPException as e:
404+
raise e from e
353405
except Exception as e:
354406
exc_str = parse_error(e)
355407
logger.error(f"controllers.flight.delete_flight: {exc_str}")

lib/controllers/motor.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Union
22
from fastapi import HTTPException, status
3+
from pymongo.errors import PyMongoError
34
from rocketpy.motors.solid_motor import SolidMotor
45
from rocketpy.motors.liquid_motor import LiquidMotor
56
from rocketpy.motors.hybrid_motor import HybridMotor
@@ -119,6 +120,14 @@ async def create_motor(self) -> Union[MotorCreated, HTTPException]:
119120
async with MotorRepository() as motor_repo:
120121
motor_repo.fetch_motor(self.motor)
121122
await motor_repo.create_motor(motor_kind=self.motor_kind)
123+
except PyMongoError as e:
124+
logger.error(f"controllers.motor.create_motor: PyMongoError {e}")
125+
raise HTTPException(
126+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
127+
detail="Failed to create motor in db",
128+
) from e
129+
except HTTPException as e:
130+
raise e from e
122131
except Exception as e:
123132
exc_str = parse_error(e)
124133
logger.error(f"controllers.motor.create_motor: {exc_str}")
@@ -151,6 +160,16 @@ async def get_motor_by_id(motor_id: str) -> Union[Motor, HTTPException]:
151160
async with MotorRepository() as motor_repo:
152161
await motor_repo.get_motor_by_id(motor_id)
153162
read_motor = motor_repo.motor
163+
except PyMongoError as e:
164+
logger.error(
165+
f"controllers.motor.get_motor_by_id: PyMongoError {e}"
166+
)
167+
raise HTTPException(
168+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
169+
detail="Failed to read motor from db",
170+
) from e
171+
except HTTPException as e:
172+
raise e from e
154173
except Exception as e:
155174
exc_str = parse_error(e)
156175
logger.error(f"controllers.motor.get_motor_by_id: {exc_str}")
@@ -230,6 +249,14 @@ async def update_motor_by_id(
230249
motor_repo.fetch_motor(self.motor)
231250
await motor_repo.create_motor(motor_kind=self.motor_kind)
232251
await motor_repo.delete_motor_by_id(motor_id)
252+
except PyMongoError as e:
253+
logger.error(f"controllers.motor.update_motor: PyMongoError {e}")
254+
raise HTTPException(
255+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
256+
detail="Failed to update motor in db",
257+
) from e
258+
except HTTPException as e:
259+
raise e from e
233260
except Exception as e:
234261
exc_str = parse_error(e)
235262
logger.error(f"controllers.motor.update_motor: {exc_str}")
@@ -263,6 +290,14 @@ async def delete_motor_by_id(
263290
try:
264291
async with MotorRepository() as motor_repo:
265292
await motor_repo.delete_motor_by_id(motor_id)
293+
except PyMongoError as e:
294+
logger.error(f"controllers.motor.delete_motor: PyMongoError {e}")
295+
raise HTTPException(
296+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
297+
detail="Failed to delete motor from db",
298+
) from e
299+
except HTTPException as e:
300+
raise e from e
266301
except Exception as e:
267302
exc_str = parse_error(e)
268303
logger.error(f"controllers.motor.delete_motor: {exc_str}")

0 commit comments

Comments
 (0)