From 83b0e4edd1ed145e6e16a234012c330232cb7ab7 Mon Sep 17 00:00:00 2001 From: GabrielBarberini Date: Wed, 27 Dec 2023 16:19:49 -0300 Subject: [PATCH] changes compose naming refactors repositories standardizes controller exception handling refactors application routes fixes repositories connection close timing updates postman collection updates README fixes pylint errors fixes pylint errors apply black style to modules Update pylint.yml Update pylint.yml Update pylint.yml disable warnings --- .github/workflows/pylint.yml | 2 +- README.md | 18 +- compose.yaml | 5 +- lib/api.py | 615 +--- lib/controllers/environment.py | 158 +- lib/controllers/flight.py | 596 ++-- lib/controllers/motor.py | 219 +- lib/controllers/rocket.py | 487 +-- lib/models/aerosurfaces.py | 5 + lib/models/environment.py | 11 +- lib/models/flight.py | 1 + lib/models/motor.py | 46 +- lib/models/parachute.py | 26 +- lib/models/rocket.py | 28 +- lib/repositories/environment.py | 37 +- lib/repositories/flight.py | 47 +- lib/repositories/motor.py | 39 +- lib/repositories/repo.py | 11 +- lib/repositories/rocket.py | 39 +- lib/routes/environment.py | 93 + lib/routes/flight.py | 134 + lib/routes/motor.py | 95 + lib/routes/rocket.py | 100 + lib/secrets.py | 5 +- lib/views/environment.py | 17 +- lib/views/flight.py | 25 +- lib/views/motor.py | 15 +- lib/views/rocket.py | 17 +- test/Infinity-API.postman_collection.json | 3456 ++++++++++++++++----- 29 files changed, 4258 insertions(+), 2089 deletions(-) create mode 100644 lib/routes/environment.py create mode 100644 lib/routes/flight.py create mode 100644 lib/routes/motor.py create mode 100644 lib/routes/rocket.py diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index ff9d51f..d276559 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -21,4 +21,4 @@ jobs: pip install -r requirements.txt - name: Analysing the code with pylint run: | - pylint -d C0200,C0301,C0114,R0903,C0115,W0246,R0914,C0209,E1121,C0103,C2801,R0801,E1101,R0911,C0116 $(git ls-files '*.py') + pylint -d C0200,C0301,C0114,R0903,C0115,W0246,R0914,C0209,E1121,C0103,C2801,R0801,E1101,E0401,E0611,R0911,C0116,W0212,W0719,W0601,W1203,W0123 $(git ls-files '*.py') diff --git a/README.md b/README.md index 1517f5c..c4774c2 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ - Stores simulation input data in mongo-db ## Docker -- touch .env -- docker-compose up --build -d +- set MONGODB_CONNECTION_STRING: `touch .env && echo MONGODB_CONNECTION_STRING="$ConnectionString" > .env` +- run docker compose: `docker-compose up --build -d` ## Setup - [Install python3](https://www.python.org/downloads/) 3.11.5 or above @@ -34,19 +34,25 @@ │   │   ├── motor.py │   │   └── rocket.py │   │   -│   ├── models -│   │   ├── aerosurfaces.py +│   ├── routes │   │   ├── environment.py │   │   ├── flight.py │   │   ├── motor.py -│   │   ├── parachute.py │   │   └── rocket.py │   │   │   ├── repositories +│   │   ├── repo.py │   │   ├── environment.py │   │   ├── flight.py │   │   ├── motor.py -│   │   ├── repo.py +│   │   └── rocket.py +│   │   +│   ├── models +│   │   ├── aerosurfaces.py +│   │   ├── environment.py +│   │   ├── flight.py +│   │   ├── motor.py +│   │   ├── parachute.py │   │   └── rocket.py │   │   │   └── views diff --git a/compose.yaml b/compose.yaml index 7293a1d..b1340e9 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,11 +1,12 @@ -version: '1' +version: '2' services: - fastapi-app: + api: build: context: . dockerfile: Dockerfile image: infinity-api:latest + container_name: infinity-api ports: - "3000:3000" env_file: diff --git a/lib/api.py b/lib/api.py index 595a131..a0e91cc 100644 --- a/lib/api.py +++ b/lib/api.py @@ -2,7 +2,6 @@ This is the main API file for the RocketPy API. """ import logging -from typing import Any, Dict from fastapi import FastAPI, Request, status from fastapi.exceptions import RequestValidationError @@ -10,20 +9,14 @@ from fastapi.openapi.utils import get_openapi from fastapi.responses import RedirectResponse, JSONResponse -from lib.views.flight import FlightSummary, FlightCreated, FlightUpdated, FlightDeleted, FlightPickle -from lib.views.environment import EnvSummary, EnvCreated, EnvUpdated, EnvDeleted, EnvPickle -from lib.views.rocket import RocketSummary, RocketCreated, RocketUpdated, RocketDeleted, RocketPickle -from lib.views.motor import MotorSummary, MotorCreated, MotorUpdated, MotorDeleted, MotorPickle -from lib.models.environment import Env -from lib.models.flight import Flight -from lib.models.rocket import Rocket, RocketOptions -from lib.models.motor import Motor, MotorKinds, MotorEngines -from lib.controllers.flight import FlightController -from lib.controllers.environment import EnvController -from lib.controllers.rocket import RocketController -from lib.controllers.motor import MotorController +from lib.routes import flight, environment, motor, rocket -app = FastAPI(swagger_ui_parameters={"defaultModelsExpandDepth": 0, "syntaxHighlight.theme": "obsidian"}) +app = FastAPI( + swagger_ui_parameters={ + "defaultModelsExpandDepth": 0, + "syntaxHighlight.theme": "obsidian", + } +) app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -31,37 +24,44 @@ allow_methods=["*"], allow_headers=["*"], ) +app.include_router(flight.router) +app.include_router(environment.router) +app.include_router(motor.router) +app.include_router(rocket.router) + def custom_openapi(): if app.openapi_schema: return app.openapi_schema openapi_schema = get_openapi( title="RocketPy Infinity-API", - version="1.0.0 ALPHA", + version="1.0.0 BETA", description=( - "

RocketPy Infinity-API is a RESTful API for RocketPy, a rocket flight simulator.

" - "
" - "" - "
" - "" - "

Create, manage, and simulate rocket flights, environments, rockets, and motors.

" - "

Currently, the API only supports SolidMotor (calisto as power_off/on_drag and Cesaroni as thrust_source) and TrapezoidalFins. We apologize for the limitation, but we are actively working to expand its capabilities soon.

" - "

Please report any bugs at GitHub Issues

" + "

RocketPy Infinity-API is a RESTful Open API for RocketPy, a rocket flight simulator.

" + "
" + "" + "
" + "" + "

Create, manage, and simulate rocket flights, environments, rockets, and motors.

" + "

Currently, the API only supports TrapezoidalFins. We apologize for the limitation, but we are actively working to expand its capabilities soon.

" + "

Please report any bugs at GitHub Issues

" ), routes=app.routes, ) openapi_schema["info"]["x-logo"] = { "url": "https://drive.google.com/uc?id=1xKt6u5mI8x8ZuA5IZvIFDolg2_0iQUf-" } - x_swagger={"visible": False} # Hide the summary in Swagger UI app.openapi_schema = openapi_schema return app.openapi_schema + + app.openapi = custom_openapi + # Main @app.get("/", include_in_schema=False) async def main_page(): @@ -70,560 +70,19 @@ async def main_page(): """ return RedirectResponse(url="/redoc") -# Flight routes -@app.post("/flights/", tags=["FLIGHT"]) -async def create_flight(flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightCreated": - """ - Creates a new flight - - ## Args - - ``` Flight object as JSON ``` - - ## Returns - - HTTP 200 { "message": "Flight created successfully.", id: flight_id_hash } - - ## Raises - - HTTP 422 Unprocessable Entity: If API is unable to parse flight data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to create flight in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await FlightController(flight, rocket_option, motor_kind).create_flight() - -@app.get("/flights/{flight_id}", tags=["FLIGHT"]) -async def read_flight(flight_id: int) -> "Flight": - """ - Reads a flight - - ## Args - - ``` flight_id: Flight ID hash ``` - - ## Returns - - ``` Flight object as JSON ``` - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - """ - return await FlightController.get_flight(flight_id) - -@app.get("/flights/rocketpy/{flight_id}", tags=["FLIGHT"]) -async def read_rocketpy_flight(flight_id: int) -> "FlightPickle": - """ - Reads a rocketpy flight object - - ## Args - - ``` flight_id: Flight ID hash. ``` - - ## Returns - - ``` RocketPy flight object as jsonpickle string ``` - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - """ - return await FlightController.get_rocketpy_flight(flight_id) - -@app.put("/flights/{flight_id}/env", tags=["FLIGHT"]) -async def update_flight_env(flight_id: int, env: Env) -> "FlightUpdated": - """ - Updates flight environment - - ## Args - - ``` - flight_id: Flight ID hash - env: env object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await FlightController.update_env(flight_id, env) - -@app.put("/flights/{flight_id}/rocket", tags=["FLIGHT"]) -async def update_flight_rocket(flight_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightUpdated": - """ - Updates flight rocket. - - ## Args - - ``` - flight_id: Flight ID hash. - rocket: Rocket object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await FlightController.update_rocket(flight_id, rocket, rocket_option, motor_kind) - -@app.put("/flights/{flight_id}", tags=["FLIGHT"]) -async def update_flight(flight_id: int, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "FlightUpdated": - """ - Updates Flight object - - ## Args - ``` - flight_id: Flight ID hash. - flight: Flight object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Flight updated successfully.", new_flight_id: new_flight_id_hash } - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse flight data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update flight in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await FlightController(flight, rocket_option, motor_kind).update_flight(flight_id) - -@app.delete("/flights/{flight_id}", tags=["FLIGHT"]) -async def delete_flight(flight_id: int) -> "FlightDeleted": - """ - Deletes a flight - - ## Args - - ``` flight_id: Flight ID hash ``` - - ## Returns - - HTTP 200 { "message": "Flight deleted successfully." } - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - """ - return await FlightController.delete_flight(flight_id) - -@app.get("/flights/{flight_id}/simulate", tags=["FLIGHT"]) -async def simulate_flight(flight_id: int) -> "FlightSummary": - """ - Simulates a flight - - ## Args - - ``` flight_id: Flight ID hash ``` - - ## Returns - - ``` Flight summary as JSON ``` - - ## Raises - - HTTP 404 Not Found: If flight_id does not exist at database. - """ - return await FlightController.simulate(flight_id) - -# Environment routes -@app.post("/environments/", tags=["ENVIRONMENT"]) -async def create_env(env: Env) -> "EnvCreated": - """ - Creates a new environment - - ## Args - - ``` Env object as a JSON ``` - - ## Returns - - HTTP 200 { "message": "Environment created successfully.", id: env_id_hash } - - ## Raises - - HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to create env in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await EnvController(env).create_env() - -@app.get("/environments/{env_id}", tags=["ENVIRONMENT"]) -async def read_env(env_id: int) -> "Env": - """ - Reads an environment - - ## Args - - ``` env_id: Environment ID hash ``` - - ## Returns - - ``` Env object as JSON. ``` - - ## Raises - - HTTP 404 Not Found: If env_id does not exist at database. - """ - return await EnvController.get_env(env_id) - -@app.put("/environments/{env_id}", tags=["ENVIRONMENT"]) -async def update_env(env_id: int, env: Env) -> "EnvUpdated": - """ - Updates an environment - - ## Args - - ``` - env_id: Environment ID hash - env: Env object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Environment updated successfully.", new_env_id: new_env_id_hash } - - ## Raises - - HTTP 404 Not Found: If env_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse env data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update env in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await EnvController(env).update_env(env_id) - -@app.delete("/environments/{env_id}", tags=["ENVIRONMENT"]) -async def delete_env(env_id: int) -> "EnvDeleted": - """ - Deletes an environment - - ## Args - - ``` env_id: Environment ID hash ``` - - ## Returns - - HTTP 200 { "message": "Environment deleted successfully." } - - ## Raises - - HTTP 404 Not Found: If env_id does not exist at database. - """ - return await EnvController.delete_env(env_id) - -@app.get("/environments/rocketpy/{env_id}", tags=["ENVIRONMENT"]) -async def read_rocketpy_env(env_id: int) -> "EnvPickle": - """ - Reads a rocketpy environment - - ## Args - - ``` env_id: Environment ID hash ``` - - ## Returns - - ``` Rocketpy Environment object as JSONPickle string. ``` - - ## Raises - HTTP 404 Not Found: If env_id does not exist at database. - """ - return await EnvController.get_rocketpy_env(env_id) - -@app.get("/environments/{env_id}/simulate", tags=["ENVIRONMENT"]) -async def simulate_env(env_id: int) -> "EnvSummary": - """ - Simulates an environment - - ## Args - - ``` env_id: Env ID hash ``` - - ## Returns - - ``` Env summary object containig simulation numbers and plots as JSON ``` - - ## Raises - - HTTP 404 Not Found: If env_id does not exist at database. - """ - return await EnvController.simulate(env_id) - -# Motor routes -@app.post("/motors/", tags=["MOTOR"]) -async def create_motor(motor: Motor, motor_kind: MotorKinds) -> "MotorCreated": - """ - Creates a new motor - - ## Args - - ``` Motor object as a JSON ``` - - ## Returns - - HTTP 200 { "message": "Motor created successfully.", id: motor_id_hash } - - ## Raises - - HTTP 422 Unprocessable Entity: If API is unable to parse motor data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to create motor in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await MotorController(motor, motor_kind).create_motor() - -@app.get("/motors/{motor_id}", tags=["MOTOR"]) -async def read_motor(motor_id: int) -> "Motor": - """ - Reads a motor - - ## Args - - ``` motor_id: Motor ID hash ``` - - ## Returns - - Motor object as JSON. - - ## Raises - - HTTP 404 Not Found: If motor_id does not exist at database. - """ - return await MotorController.get_motor(motor_id) - -@app.put("/motors/{motor_id}", tags=["MOTOR"]) -async def update_motor(motor_id: int, motor: Motor, motor_kind: MotorKinds) -> "MotorUpdated": - """ - Updates a motor - - ## Args - - ``` - motor_id: Motor ID hash - motor: Motor object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Motor updated successfully.", new_motor_id: new_motor_id_hash } - - ## Raises - - HTTP 404 Not Found: If motor_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse motor data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update motor in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await MotorController(motor, motor_kind).update_motor(motor_id) - -@app.delete("/motors/{motor_id}", tags=["MOTOR"]) -async def delete_motor(motor_id: int) -> "MotorDeleted": - """ - Deletes a motor - - ## Args - - ``` motor_id: Motor ID hash ``` - - ## Returns - - HTTP 200 { "message": "Motor deleted successfully." } - - ## Raises - - HTTP 404 Not Found: If motor_id does not exist at database. - """ - return await MotorController.delete_motor(motor_id) - -@app.get("/motors/rocketpy/{motor_id}", tags=["MOTOR"]) -async def read_rocketpy_motor(motor_id: int) -> "MotorPickle": - """ - Reads a rocketpy motor - - ## Args - - ``` motor_id: Motor ID hash ``` - - ## Returns - - ``` Rocketpy Motor object as JSONPickle string ``` - - ## Raises - HTTP 404 Not Found: If motor_id does not exist at database. - """ - return await MotorController.get_rocketpy_motor(motor_id) - -@app.get("/motors/{motor_id}/simulate", tags=["MOTOR"]) -async def simulate_motor(motor_id: int) -> "MotorSummary": - """ - Simulates a motor - - ## Args - - ``` motor_id: Motor ID hash ``` - - ## Returns - - ``` Motor summary as JSON ``` - - ## Raises - - HTTP 404 Not Found: If motor_id does not exist at database. - """ - return await MotorController.simulate(motor_id) - -# Rocket routes -@app.post("/rockets/", tags=["ROCKET"]) -async def create_rocket(rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "RocketCreated": - """ - Creates a new rocket - - ## Args - - ``` Rocket object as a JSON ``` - - ## Returns - - HTTP 200 { "message": "Rocket created successfully.", id: rocket_id_hash } - - ## Raises - - HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to create rocket in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await RocketController(rocket, rocket_option, motor_kind).create_rocket() - -@app.get("/rockets/{rocket_id}", tags=["ROCKET"]) -async def read_rocket(rocket_id: int) -> Rocket: - """ - Reads a rocket - - ## Args - - ``` rocket_id: Rocket ID hash ``` - - ## Returns - - ``` Rocket object as JSON ``` - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - """ - return await RocketController.get_rocket(rocket_id) - -@app.put("/rockets/{rocket_id}", tags=["ROCKET"]) -async def update_rocket(rocket_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds) -> "RocketUpdated": - """ - Updates a rocket - - ## Args - - ``` - rocket_id: Rocket ID hash - rocket: Rocket object as JSON - ``` - - ## Returns - - HTTP 200 { "message": "Rocket updated successfully.", new_rocket_id: new_rocket_id_hash } - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - - HTTP 422 Unprocessable Entity: If API is unable to parse rocket data, usually happens when some parameter is invalid, please attend to API docs request specifications. - - HTTP 500 Internal Server Error: If API is either unable to update rocket in mongoDB or valid parameter type/structure provided but content is breaking the API. - """ - return await RocketController(rocket, rocket_option, motor_kind).update_rocket(rocket_id) - -@app.delete("/rockets/{rocket_id}", tags=["ROCKET"]) -async def delete_rocket(rocket_id: int) -> "RocketDeleted": - """ - Deletes a rocket - - ## Args - - ``` rocket_id: Rocket ID hash ``` - - ## Returns - - HTTP 200 { "message": "Rocket deleted successfully." } - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - """ - return await RocketController.delete_rocket(rocket_id) - -@app.get("/rockets/rocketpy/{rocket_id}", tags=["ROCKET"]) -async def read_rocketpy_rocket(rocket_id: int) -> "RocketPickle": - """ - Reads a rocketpy rocket - - ## Args - - ``` rocket_id: Rocket ID hash ``` - - ## Returns - - ``` Rocketpy Rocket object as JSONPickle string ``` - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - """ - return await RocketController.get_rocketpy_rocket(rocket_id) - -@app.get("/rockets/{rocket_id}/simulate", tags=["ROCKET"]) -async def simulate_rocket(rocket_id: int) -> "RocketSummary": - """ - Simulates a rocket - - ## Args - - ``` rocket_id: Rocket ID hash ``` - - ## Returns - - HTTP 200 pydantic rocket summary object containig simulation numbers and plots as JSON. - - ## Raises - - HTTP 404 Not Found: If rocket_id does not exist at database. - """ - return await RocketController.simulate(rocket_id) # Additional routes @app.get("/health", status_code=status.HTTP_200_OK, include_in_schema=False) async def __perform_healthcheck(): - return {'health': 'Everything OK!'} + return {"health": "Everything OK!"} + -# Errors +# Errors @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): - exc_str = f'{exc}'.replace('\n', ' ').replace(' ', ' ') - logging.error(f"{request}: {exc_str}") - content = {'status_code': 10422, 'message': exc_str, 'data': None} - return JSONResponse(content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) + exc_str = f"{exc}".replace("\n", " ").replace(" ", " ") + logging.error(f"{request}: {exc_str}") + content = {"status_code": 10422, "message": exc_str, "data": None} + return JSONResponse( + content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY + ) diff --git a/lib/controllers/environment.py b/lib/controllers/environment.py index 46fc5ac..90c4c6f 100644 --- a/lib/controllers/environment.py +++ b/lib/controllers/environment.py @@ -1,15 +1,24 @@ -from typing import Dict, Any, Union +from typing import Union import jsonpickle from rocketpy.environment.environment import Environment -from fastapi import Response, status +from fastapi import HTTPException, status from lib.models.environment import Env from lib.repositories.environment import EnvRepository -from lib.views.environment import EnvSummary, EnvData, EnvPlots, EnvCreated, EnvDeleted, EnvUpdated, EnvPickle - -class EnvController(): - """ +from lib.views.environment import ( + EnvSummary, + EnvData, + EnvPlots, + EnvCreated, + EnvDeleted, + EnvUpdated, + EnvPickle, +) + + +class EnvController: + """ Controller for the Environment model. Init Attributes: @@ -18,35 +27,39 @@ class EnvController(): Enables: - Create a rocketpy.Environment object from an Env model object. """ + def __init__(self, env: Env): rocketpy_env = Environment( - latitude=env.latitude, - longitude=env.longitude, - elevation=env.elevation, - date=env.date - ) + latitude=env.latitude, + longitude=env.longitude, + elevation=env.elevation, + date=env.date, + ) rocketpy_env.set_atmospheric_model( - type=env.atmospheric_model_type, - file=env.atmospheric_model_file - ) + type=env.atmospheric_model_type, file=env.atmospheric_model_file + ) self.rocketpy_env = rocketpy_env self.env = env - async def create_env(self) -> "Union[EnvCreated, Response]": + async def create_env(self) -> "Union[EnvCreated, HTTPException]": """ Create a env in the database. Returns: - Dict[str, str]: Environment id. + EnvCreated: Environment id. """ env = EnvRepository(environment=self.env) successfully_created_env = await env.create_env() - if successfully_created_env: - return EnvCreated(env_id=str(env.env_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + if not successfully_created_env: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create environment", + ) + + return EnvCreated(env_id=str(env.env_id)) @staticmethod - async def get_env(env_id: int) -> "Union[Env, Response]": + async def get_env(env_id: int) -> "Union[Env, HTTPException]": """ Get a env from the database. @@ -59,14 +72,16 @@ async def get_env(env_id: int) -> "Union[Env, Response]": Raises: HTTP 404 Not Found: If the env is not found in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) + return successfully_read_env @staticmethod - async def get_rocketpy_env(env_id: int) -> "Union[EnvPickle, Response]": + async def get_rocketpy_env(env_id: int) -> "Union[EnvPickle, HTTPException]": """ Get a rocketpy env object encoded as jsonpickle string from the database. @@ -79,17 +94,21 @@ async def get_rocketpy_env(env_id: int) -> "Union[EnvPickle, Response]": Raises: HTTP 404 Not Found: If the env is not found in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) - successfully_read_rocketpy_env = \ - EnvController( successfully_read_env ).rocketpy_env + successfully_read_rocketpy_env = EnvController( + successfully_read_env + ).rocketpy_env - return EnvPickle(jsonpickle_rocketpy_env=jsonpickle.encode(successfully_read_rocketpy_env)) + return EnvPickle( + jsonpickle_rocketpy_env=jsonpickle.encode(successfully_read_rocketpy_env) + ) - async def update_env(self, env_id: int) -> "Union[EnvUpdated, Response]": + async def update_env(self, env_id: int) -> "Union[EnvUpdated, HTTPException]": """ Update a env in the database. @@ -97,25 +116,30 @@ async def update_env(self, env_id: int) -> "Union[EnvUpdated, Response]": env_id (int): env id. Returns: - Dict[str, Any]: env id and message. + EnvUpdated: env id and message. Raises: HTTP 404 Not Found: If the env is not found in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) - successfully_updated_env = \ - await EnvRepository(environment=self.env, env_id=env_id).update_env() + successfully_updated_env = await EnvRepository( + environment=self.env, env_id=env_id + ).update_env() + if not successfully_updated_env: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update environment", + ) - if successfully_updated_env: - return EnvUpdated(new_env_id=str(successfully_updated_env)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return EnvUpdated(new_env_id=str(successfully_updated_env)) @staticmethod - async def delete_env(env_id: int) -> "Union[EnvDeleted, Response]": + async def delete_env(env_id: int) -> "Union[EnvDeleted, HTTPException]": """ Delete a env from the database. @@ -123,24 +147,28 @@ async def delete_env(env_id: int) -> "Union[EnvDeleted, Response]": env_id (int): Environment id. Returns: - Dict[str, str]: Environment id and message. + EnvDeleted: Environment id and message. Raises: HTTP 404 Not Found: If the env is not found in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) + + successfully_deleted_env = await EnvRepository(env_id=env_id).delete_env() + if not successfully_deleted_env: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete environment", + ) - successfully_deleted_env = \ - await EnvRepository(env_id=env_id).delete_env() - if successfully_deleted_env: - return EnvDeleted(deleted_env_id = str(env_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return EnvDeleted(deleted_env_id=str(env_id)) @staticmethod - async def simulate(env_id: int) -> "Union[EnvSummary, Response]": + async def simulate(env_id: int) -> "Union[EnvSummary, HTTPException]": """ Simulate a rocket environment. @@ -153,14 +181,22 @@ async def simulate(env_id: int) -> "Union[EnvSummary, Response]": Raises: HTTP 404 Not Found: If the env does not exist in the database. """ - successfully_read_env = \ - await EnvRepository(env_id=env_id).get_env() + successfully_read_env = await EnvRepository(env_id=env_id).get_env() if not successfully_read_env: - return Response(status_code=status.HTTP_404_NOT_FOUND) - - env = EnvController(successfully_read_env).rocketpy_env - env_simulation_numbers = EnvData.parse_obj(env.all_info_returned()) - env_simulation_plots = EnvPlots.parse_obj(env.all_plot_info_returned()) - - env_summary = EnvSummary( env_data=env_simulation_numbers, env_plots=env_simulation_plots ) - return env_summary + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Environment not found" + ) + + try: + env = EnvController(successfully_read_env).rocketpy_env + env_simulation_numbers = EnvData.parse_obj(env.all_info_returned()) + env_simulation_plots = EnvPlots.parse_obj(env.all_plot_info_returned()) + env_summary = EnvSummary( + env_data=env_simulation_numbers, env_plots=env_simulation_plots + ) + return env_summary + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to simulate environment: {e}", + ) from e diff --git a/lib/controllers/flight.py b/lib/controllers/flight.py index 5c83a5d..0fd6eba 100644 --- a/lib/controllers/flight.py +++ b/lib/controllers/flight.py @@ -1,25 +1,38 @@ -from typing import Dict, Any, Union -from fastapi import Response, status +from typing import Union +from fastapi import HTTPException, status from rocketpy.simulation.flight import Flight as RocketpyFlight import jsonpickle from lib.models.rocket import Rocket, RocketOptions -from lib.models.motor import Motor, MotorEngines, MotorKinds +from lib.models.motor import MotorKinds from lib.models.flight import Flight from lib.models.environment import Env -from lib.views.flight import ( - FlightSummary, SurfaceWindConditions, OutOfRailConditions, BurnoutConditions, ApogeeConditions, MaximumValues, - InitialConditions, NumericalIntegrationSettings, ImpactConditions, EventsRegistered, LaunchRailConditions, FlightData, - FlightCreated, FlightUpdated, FlightDeleted, FlightPickle +from lib.views.flight import ( + FlightSummary, + SurfaceWindConditions, + OutOfRailConditions, + BurnoutConditions, + ApogeeConditions, + MaximumValues, + InitialConditions, + NumericalIntegrationSettings, + ImpactConditions, + EventsRegistered, + LaunchRailConditions, + FlightData, + FlightCreated, + FlightUpdated, + FlightDeleted, + FlightPickle, ) from lib.repositories.flight import FlightRepository from lib.controllers.environment import EnvController from lib.controllers.rocket import RocketController -class FlightController(): +class FlightController: """ Controller for the Flight model. @@ -36,8 +49,13 @@ class FlightController(): - Read a RocketpyFlight object from the database. """ - def __init__(self, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds): - rocketpy_rocket = RocketController(flight.rocket, rocket_option=rocket_option, motor_kind=motor_kind).rocketpy_rocket + + def __init__( + self, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds + ): + rocketpy_rocket = RocketController( + flight.rocket, rocket_option=rocket_option, motor_kind=motor_kind + ).rocketpy_rocket rocketpy_env = EnvController(flight.environment).rocketpy_env rocketpy_flight = RocketpyFlight( @@ -45,7 +63,7 @@ def __init__(self, flight: Flight, rocket_option: RocketOptions, motor_kind: Mot inclination=flight.inclination, heading=flight.heading, environment=rocketpy_env, - rail_length=flight.rail_length + rail_length=flight.rail_length, ) self.rocket_option = rocket_option @@ -53,26 +71,28 @@ def __init__(self, flight: Flight, rocket_option: RocketOptions, motor_kind: Mot self.rocketpy_flight = rocketpy_flight self.flight = flight - async def create_flight(self) -> "Union[FlightCreated, Response]": + async def create_flight(self) -> "Union[FlightCreated, HTTPException]": """ Create a flight in the database. Returns: - Dict[str, str]: Flight id. + FlightCreated: Flight id. """ flight = FlightRepository(flight=self.flight) - successfully_created_flight = \ - await flight.create_flight( - motor_kind = self.motor_kind, - rocket_option = self.rocket_option - ) + successfully_created_flight = await flight.create_flight( + motor_kind=self.motor_kind, rocket_option=self.rocket_option + ) - if successfully_created_flight: - return FlightCreated(flight_id=str(flight.flight_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + if not successfully_created_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create flight.", + ) + + return FlightCreated(flight_id=str(flight.flight_id)) @staticmethod - async def get_flight(flight_id: int) -> "Union[Flight, Response]": + async def get_flight(flight_id: int) -> "Union[Flight, HTTPException]": """ Get a flight from the database. @@ -85,14 +105,20 @@ async def get_flight(flight_id: int) -> "Union[Flight, Response]": Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) + return successfully_read_flight @staticmethod - async def get_rocketpy_flight(flight_id: int) -> "Union[FlightPickle, Response]": + async def get_rocketpy_flight( + flight_id: int, + ) -> "Union[FlightPickle, HTTPException]": """ Get a rocketpy flight object encoded as jsonpickle string from the database. @@ -105,19 +131,29 @@ async def get_rocketpy_flight(flight_id: int) -> "Union[FlightPickle, Response]" Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) - successfully_read_rocketpy_flight = \ - FlightController(flight = successfully_read_flight, - rocket_option = RocketOptions(successfully_read_flight.rocket._rocket_option), - motor_kind = MotorKinds(successfully_read_flight.rocket.motor._motor_kind)).rocketpy_flight + successfully_read_rocketpy_flight = FlightController( + flight=successfully_read_flight, + rocket_option=RocketOptions(successfully_read_flight.rocket._rocket_option), + motor_kind=MotorKinds(successfully_read_flight.rocket.motor._motor_kind), + ).rocketpy_flight - return FlightPickle(jsonpickle_rocketpy_flight=jsonpickle.encode(successfully_read_rocketpy_flight)) + return FlightPickle( + jsonpickle_rocketpy_flight=jsonpickle.encode( + successfully_read_rocketpy_flight + ) + ) - async def update_flight(self, flight_id: int) -> "Union[FlightUpdated, Response]": + async def update_flight( + self, flight_id: int + ) -> "Union[FlightUpdated, HTTPException]": """ Update a flight in the database. @@ -125,28 +161,34 @@ async def update_flight(self, flight_id: int) -> "Union[FlightUpdated, Response] flight_id (int): Flight id. Returns: - Dict[str, Any]: Flight id and message. + FlightUpdated: Flight id and message. Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) - successfully_updated_flight = \ - await FlightRepository(flight=self.flight, flight_id=flight_id).update_flight( - rocket_option = self.rocket_option, - motor_kind = self.motor_kind - ) + successfully_updated_flight = await FlightRepository( + flight=self.flight, flight_id=flight_id + ).update_flight(rocket_option=self.rocket_option, motor_kind=self.motor_kind) + if not successfully_updated_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update flight.", + ) - if successfully_updated_flight: - return FlightUpdated(new_flight_id=str(successfully_updated_flight)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return FlightUpdated(new_flight_id=str(successfully_updated_flight)) @staticmethod - async def update_env(flight_id: int, env: Env) -> "Union[FlightUpdated, Response]": + async def update_env( + flight_id: int, env: Env + ) -> "Union[FlightUpdated, HTTPException]": """ Update the environment of a flight in the database. @@ -155,28 +197,37 @@ async def update_env(flight_id: int, env: Env) -> "Union[FlightUpdated, Response env (models.Env): Environment model object. Returns: - Dict[str, Any]: Flight id and message. + FlightUpdated: Flight id and message. Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) - flight = successfully_read_flight.dict() + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) + flight = successfully_read_flight.dict() flight["environment"] = env flight = Flight(**flight) - successfully_updated_flight = \ - await FlightRepository(flight=flight, flight_id=flight_id).update_flight() + successfully_updated_flight = await FlightRepository( + flight=flight, flight_id=flight_id + ).update_flight() + if not successfully_updated_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update flight.", + ) - if successfully_updated_flight: - return FlightUpdated(new_flight_id=str(successfully_updated_flight)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return FlightUpdated(new_flight_id=str(successfully_updated_flight)) @staticmethod - async def update_rocket(flight_id: int, rocket: Rocket, rocket_option, motor_kind) -> "Union[FlightUpdated, Response]": + async def update_rocket( + flight_id: int, rocket: Rocket, rocket_option, motor_kind + ) -> "Union[FlightUpdated, HTTPException]": """ Update the rocket of a flight in the database. @@ -185,32 +236,38 @@ async def update_rocket(flight_id: int, rocket: Rocket, rocket_option, motor_kin rocket (models.Rocket): Rocket model object. Returns: - Dict[str, Any]: Flight id and message. + FlightUpdated: Flight id and message. Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) - flight = successfully_read_flight.dict() + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) + flight = successfully_read_flight.dict() updated_rocket = rocket.dict() updated_rocket["rocket_option"] = rocket_option updated_rocket["motor"]["motor_kind"] = motor_kind flight["rocket"] = Rocket(**updated_rocket) - flight = Flight(**flight) - successfully_updated_flight = \ - await FlightRepository(flight=flight, flight_id=flight_id).update_flight() + successfully_updated_flight = await FlightRepository( + flight=flight, flight_id=flight_id + ).update_flight() + if not successfully_updated_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update flight.", + ) - if successfully_updated_flight: - return FlightUpdated(new_flight_id=str(successfully_updated_flight)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return FlightUpdated(new_flight_id=str(successfully_updated_flight)) @staticmethod - async def delete_flight(flight_id: int) -> "Union[FlightDeleted, Response]": + async def delete_flight(flight_id: int) -> "Union[FlightDeleted, HTTPException]": """ Delete a flight from the database. @@ -218,24 +275,32 @@ async def delete_flight(flight_id: int) -> "Union[FlightDeleted, Response]": flight_id (int): Flight id. Returns: - Dict[str, str]: Flight id and message. + FlightDeleted: Flight id and message. Raises: HTTP 404 Not Found: If the flight is not found in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) + + successfully_deleted_flight = await FlightRepository( + flight_id=flight_id + ).delete_flight() + if not successfully_deleted_flight: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete flight.", + ) - successfully_deleted_flight = \ - await FlightRepository(flight_id=flight_id).delete_flight() - if successfully_deleted_flight: - return FlightDeleted(deleted_flight_id=str(flight_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return FlightDeleted(deleted_flight_id=str(flight_id)) @staticmethod - async def simulate(flight_id: int) -> "Union[FlightSummary, Response]": + async def simulate(flight_id: int) -> "Union[FlightSummary, HTTPException]": """ Simulate a rocket flight. @@ -248,160 +313,247 @@ async def simulate(flight_id: int) -> "Union[FlightSummary, Response]": Raises: HTTP 404 Not Found: If the flight does not exist in the database. """ - successfully_read_flight = \ - await FlightRepository(flight_id=flight_id).get_flight() + successfully_read_flight = await FlightRepository( + flight_id=flight_id + ).get_flight() if not successfully_read_flight: - return Response(status_code=status.HTTP_404_NOT_FOUND) - - flight = FlightController(flight = successfully_read_flight, - rocket_option = RocketOptions(successfully_read_flight.rocket._rocket_option), - motor_kind = MotorKinds(successfully_read_flight.rocket.motor._motor_kind)).rocketpy_flight - - _initial_conditions = InitialConditions( - initial_altitude = "Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format(flight.e0(0), flight.e1(0), flight.e2(0), flight.e3(0)), - initial_velocity = "Velocity - Vx: {:.2f} m/s | Vy: {:.2f} m/s | Vz: {:.2f} m/s".format(flight.vx(0), flight.vy(0), flight.vz(0)), - initial_position = "Position - x: {:.2f} m | y: {:.2f} m | z: {:.2f} m".format(flight.x(0), flight.y(0), flight.z(0)), - initial_angular_position = "Euler Angles - Spin φ : {:.2f}° | Nutation θ: {:.2f}° | Precession ψ: {:.2f}°".format(flight.phi(0), flight.theta(0), flight.psi(0)), - initial_angular_velocity = "Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s".format(flight.w1(0), flight.w2(0), flight.w3(0)) - ) - - _numerical_integration_settings = NumericalIntegrationSettings( - max_time = "Maximum Allowed Flight Time: {:f} s".format(flight.max_time), - max_time_step = "Maximum Allowed Time Step: {:f} s".format(flight.max_time_step), - min_time_step = "Minimum Allowed Time Step: {:e} s".format(flight.min_time_step), - relative_error_tolerance = f"Relative Error Tolerance: {flight.rtol}", - absolute_error_tolerance = f"Absolute Error Tolerance: {flight.atol}", - time_overshoot = f"Allow Event Overshoot: {flight.time_overshoot}", - terminate_on_apogee = f"Terminate Simulation on Apogee: {flight.terminate_on_apogee}", - number_of_time_steps = f"Number of Time Steps Used: {len(flight.time_steps)}", - function_evaluations_per_time_step = f"Number of Derivative Functions Evaluation: {sum(flight.function_evaluations_per_time_step)}", - avg_function_evaluations_per_time_step = "Average Function Evaluations per Time Step: {:3f}".format(sum(flight.function_evaluations_per_time_step) / len(flight.time_steps)) - ) - - _launch_rail_conditions = LaunchRailConditions( - rail_length = "Launch Rail Length: {:.2f} m".format(flight.rail_length), - flight_inclination = "Launch Rail Inclination: {:.2f}°".format(flight.inclination), - flight_heading = "Launch Rail Heading: {:.2f}°".format(flight.heading) - ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Flight not found." + ) - _surface_wind_conditions = SurfaceWindConditions( - frontal_surface_wind_speed = "Frontal Surface Wind Speed: {:.2f} m/s".format(flight.frontal_surface_wind), - lateral_surface_wind_speed = "Lateral Surface Wind Speed: {:.2f} m/s".format(flight.lateral_surface_wind) - ) + try: + flight = FlightController( + flight=successfully_read_flight, + rocket_option=RocketOptions( + successfully_read_flight.rocket._rocket_option + ), + motor_kind=MotorKinds( + successfully_read_flight.rocket.motor._motor_kind + ), + ).rocketpy_flight + + _initial_conditions = InitialConditions( + initial_altitude="Attitude - e0: {:.3f} | e1: {:.3f} | e2: {:.3f} | e3: {:.3f}".format( + flight.e0(0), flight.e1(0), flight.e2(0), flight.e3(0) + ), + initial_velocity="Velocity - Vx: {:.2f} m/s | Vy: {:.2f} m/s | Vz: {:.2f} m/s".format( + flight.vx(0), flight.vy(0), flight.vz(0) + ), + initial_position="Position - x: {:.2f} m | y: {:.2f} m | z: {:.2f} m".format( + flight.x(0), flight.y(0), flight.z(0) + ), + initial_angular_position="Euler Angles - Spin φ : {:.2f}° | Nutation θ: {:.2f}° | Precession ψ: {:.2f}°".format( + flight.phi(0), flight.theta(0), flight.psi(0) + ), + initial_angular_velocity="Angular Velocity - ω1: {:.2f} rad/s | ω2: {:.2f} rad/s| ω3: {:.2f} rad/s".format( + flight.w1(0), flight.w2(0), flight.w3(0) + ), + ) - _out_of_rail_conditions = OutOfRailConditions( - out_of_rail_time = "Rail Departure Time: {:.3f} s".format(flight.out_of_rail_time), - out_of_rail_velocity = "Rail Departure Velocity: {:.3f} m/s".format(flight.out_of_rail_velocity), - out_of_rail_static_margin = "Rail Departure Static Margin: {:.3f} c".format(flight.rocket.static_margin(flight.out_of_rail_time)), - out_of_rail_angle_of_attack = "Rail Departure Angle of Attack: {:.3f}°".format(flight.angle_of_attack(flight.out_of_rail_time)), - out_of_rail_thrust_weight_ratio = "Rail Departure Thrust-Weight Ratio: {:.3f}".format(flight.rocket.thrust_to_weight(flight.out_of_rail_time)), - out_of_rail_reynolds_number = "Rail Departure Reynolds Number: {:.3e}".format(flight.reynolds_number(flight.out_of_rail_time)) - ) + _numerical_integration_settings = NumericalIntegrationSettings( + max_time="Maximum Allowed Flight Time: {:f} s".format(flight.max_time), + max_time_step="Maximum Allowed Time Step: {:f} s".format( + flight.max_time_step + ), + min_time_step="Minimum Allowed Time Step: {:e} s".format( + flight.min_time_step + ), + relative_error_tolerance=f"Relative Error Tolerance: {flight.rtol}", + absolute_error_tolerance=f"Absolute Error Tolerance: {flight.atol}", + time_overshoot=f"Allow Event Overshoot: {flight.time_overshoot}", + terminate_on_apogee=f"Terminate Simulation on Apogee: {flight.terminate_on_apogee}", + number_of_time_steps=f"Number of Time Steps Used: {len(flight.time_steps)}", + function_evaluations_per_time_step=f"Number of Derivative Functions Evaluation: {sum(flight.function_evaluations_per_time_step)}", + avg_function_evaluations_per_time_step="Average Function Evaluations per Time Step: {:3f}".format( + sum(flight.function_evaluations_per_time_step) + / len(flight.time_steps) + ), + ) - _burnout_conditions = BurnoutConditions( - burnout_time = "Burn out time: {:.3f} s".format(flight.rocket.motor.burn_out_time), - burnout_altitude = "Altitude at burn out: {:.3f} m (AGL)".format( - flight.z(flight.rocket.motor.burn_out_time) - - flight.env.elevation - ), - burnout_rocket_velocity = "Rocket velocity at burn out: {:.3f} m/s".format( - flight.speed(flight.rocket.motor.burn_out_time) - ), - burnout_freestream_velocity = "Freestream velocity at burn out: {:.3f} m/s".format(( - flight.stream_velocity_x(flight.rocket.motor.burn_out_time) ** 2 - + flight.stream_velocity_y(flight.rocket.motor.burn_out_time) ** 2 - + flight.stream_velocity_z(flight.rocket.motor.burn_out_time) ** 2 + _launch_rail_conditions = LaunchRailConditions( + rail_length="Launch Rail Length: {:.2f} m".format(flight.rail_length), + flight_inclination="Launch Rail Inclination: {:.2f}°".format( + flight.inclination + ), + flight_heading="Launch Rail Heading: {:.2f}°".format(flight.heading), ) - ** 0.5), - burnout_mach_number = "Mach Number at burn out: {:.3f}".format(flight.mach_number(flight.rocket.motor.burn_out_time)), - burnout_kinetic_energy = "Kinetic energy at burn out: {:.3e}".format(flight.kinetic_energy(flight.rocket.motor.burn_out_time)) - ) - _apogee_conditions = ApogeeConditions( - apogee_altitude = "Apogee Altitude: {:.3f} m (ASL) | {:.3f} m (AGL)".format(flight.apogee, flight.apogee - flight.env.elevation), - apogee_time = "Apogee Time: {:.3f} s".format(flight.apogee_time), - apogee_freestream_speed = "Apogee Freestream Speed: {:.3f} m/s".format(flight.apogee_freestream_speed) - ) + _surface_wind_conditions = SurfaceWindConditions( + frontal_surface_wind_speed="Frontal Surface Wind Speed: {:.2f} m/s".format( + flight.frontal_surface_wind + ), + lateral_surface_wind_speed="Lateral Surface Wind Speed: {:.2f} m/s".format( + flight.lateral_surface_wind + ), + ) - _maximum_values = MaximumValues( - maximum_speed = "Maximum Speed: {:.3f} m/s at {:.2f} s".format(flight.max_speed, flight.max_speed_time), - maximum_mach_number = "Maximum Mach Number: {:.3f} Mach at {:.2f} s".format(flight.max_mach_number, flight.max_mach_number_time), - maximum_reynolds_number = "Maximum Reynolds Number: {:.3e} at {:.2f} s".format(flight.max_reynolds_number, flight.max_reynolds_number_time), - maximum_dynamic_pressure = "Maximum Dynamic Pressure: {:.3e} Pa at {:.2f} s".format(flight.max_dynamic_pressure, flight.max_dynamic_pressure_time), - maximum_acceleration_during_motor_burn = "Maximum Acceleration During Motor Burn: {:.3f} m/s² at {:.2f} s".format(flight.max_acceleration, flight.max_acceleration_time), - maximum_gs_during_motor_burn = "Maximum Gs During Motor Burn: {:.3f} g at {:.2f} s".format(flight.max_acceleration / flight.env.gravity(flight.z(flight.max_acceleration_time)), flight.max_acceleration_time), - maximum_acceleration_after_motor_burn = "Maximum Acceleration After Motor Burn: {:.3f} m/s² at {:.2f} s".format( - flight.max_acceleration_power_off, - flight.max_acceleration_power_off_time, - ), - maximum_gs_after_motor_burn = "Maximum Gs After Motor Burn: {:.3f} g at {:.2f} s".format( - flight.max_acceleration_power_off / flight.env.standard_g, - flight.max_acceleration_power_off_time, - ), - maximum_upper_rail_button_normal_force = "Maximum Upper Rail Button Normal Force: {:.3f} N".format(flight.max_rail_button1_normal_force), - maximum_upper_rail_button_shear_force = "Maximum Upper Rail Button Shear Force: {:.3f} N".format(flight.max_rail_button1_shear_force), - maximum_lower_rail_button_normal_force = "Maximum Lower Rail Button Normal Force: {:.3f} N".format(flight.max_rail_button2_normal_force), - maximum_lower_rail_button_shear_force = "Maximum Lower Rail Button Shear Force: {:.3f} N".format(flight.max_rail_button2_shear_force) - ) + _out_of_rail_conditions = OutOfRailConditions( + out_of_rail_time="Rail Departure Time: {:.3f} s".format( + flight.out_of_rail_time + ), + out_of_rail_velocity="Rail Departure Velocity: {:.3f} m/s".format( + flight.out_of_rail_velocity + ), + out_of_rail_static_margin="Rail Departure Static Margin: {:.3f} c".format( + flight.rocket.static_margin(flight.out_of_rail_time) + ), + out_of_rail_angle_of_attack="Rail Departure Angle of Attack: {:.3f}°".format( + flight.angle_of_attack(flight.out_of_rail_time) + ), + out_of_rail_thrust_weight_ratio="Rail Departure Thrust-Weight Ratio: {:.3f}".format( + flight.rocket.thrust_to_weight(flight.out_of_rail_time) + ), + out_of_rail_reynolds_number="Rail Departure Reynolds Number: {:.3e}".format( + flight.reynolds_number(flight.out_of_rail_time) + ), + ) - if len(flight.impact_state) != 0: - _impact_conditions = ImpactConditions( - x_impact_position = "X Impact: {:.3f} m".format(flight.x_impact), - y_impact_position = "Y Impact: {:.3f} m".format(flight.y_impact), - time_of_impact = "Time of Impact: {:.3f} s".format(flight.t_final), - impact_velocity = "Velocity at Impact: {:.3f} m/s".format(flight.impact_velocity) + _burnout_conditions = BurnoutConditions( + burnout_time="Burn out time: {:.3f} s".format( + flight.rocket.motor.burn_out_time + ), + burnout_altitude="Altitude at burn out: {:.3f} m (AGL)".format( + flight.z(flight.rocket.motor.burn_out_time) - flight.env.elevation + ), + burnout_rocket_velocity="Rocket velocity at burn out: {:.3f} m/s".format( + flight.speed(flight.rocket.motor.burn_out_time) + ), + burnout_freestream_velocity="Freestream velocity at burn out: {:.3f} m/s".format( + ( + flight.stream_velocity_x(flight.rocket.motor.burn_out_time) ** 2 + + flight.stream_velocity_y(flight.rocket.motor.burn_out_time) + ** 2 + + flight.stream_velocity_z(flight.rocket.motor.burn_out_time) + ** 2 + ) + ** 0.5 + ), + burnout_mach_number="Mach Number at burn out: {:.3f}".format( + flight.mach_number(flight.rocket.motor.burn_out_time) + ), + burnout_kinetic_energy="Kinetic energy at burn out: {:.3e}".format( + flight.kinetic_energy(flight.rocket.motor.burn_out_time) + ), ) - elif flight.terminate_on_apogee is False: - _impact_conditions = ImpactConditions( - time = "Time: {:.3f} s".format(flight.solution[-1][0]), - altitude = "Altitude: {:.3f} m".format(flight.solution[-1][3]) + + _apogee_conditions = ApogeeConditions( + apogee_altitude="Apogee Altitude: {:.3f} m (ASL) | {:.3f} m (AGL)".format( + flight.apogee, flight.apogee - flight.env.elevation + ), + apogee_time="Apogee Time: {:.3f} s".format(flight.apogee_time), + apogee_freestream_speed="Apogee Freestream Speed: {:.3f} m/s".format( + flight.apogee_freestream_speed + ), ) - if len(flight.parachute_events) == 0: - _events_registered = EventsRegistered( - events_trace = "No parachute event registered" + _maximum_values = MaximumValues( + maximum_speed="Maximum Speed: {:.3f} m/s at {:.2f} s".format( + flight.max_speed, flight.max_speed_time + ), + maximum_mach_number="Maximum Mach Number: {:.3f} Mach at {:.2f} s".format( + flight.max_mach_number, flight.max_mach_number_time + ), + maximum_reynolds_number="Maximum Reynolds Number: {:.3e} at {:.2f} s".format( + flight.max_reynolds_number, flight.max_reynolds_number_time + ), + maximum_dynamic_pressure="Maximum Dynamic Pressure: {:.3e} Pa at {:.2f} s".format( + flight.max_dynamic_pressure, flight.max_dynamic_pressure_time + ), + maximum_acceleration_during_motor_burn="Maximum Acceleration During Motor Burn: {:.3f} m/s² at {:.2f} s".format( + flight.max_acceleration, flight.max_acceleration_time + ), + maximum_gs_during_motor_burn="Maximum Gs During Motor Burn: {:.3f} g at {:.2f} s".format( + flight.max_acceleration + / flight.env.gravity(flight.z(flight.max_acceleration_time)), + flight.max_acceleration_time, + ), + maximum_acceleration_after_motor_burn="Maximum Acceleration After Motor Burn: {:.3f} m/s² at {:.2f} s".format( + flight.max_acceleration_power_off, + flight.max_acceleration_power_off_time, + ), + maximum_gs_after_motor_burn="Maximum Gs After Motor Burn: {:.3f} g at {:.2f} s".format( + flight.max_acceleration_power_off / flight.env.standard_g, + flight.max_acceleration_power_off_time, + ), + maximum_upper_rail_button_normal_force="Maximum Upper Rail Button Normal Force: {:.3f} N".format( + flight.max_rail_button1_normal_force + ), + maximum_upper_rail_button_shear_force="Maximum Upper Rail Button Shear Force: {:.3f} N".format( + flight.max_rail_button1_shear_force + ), + maximum_lower_rail_button_normal_force="Maximum Lower Rail Button Normal Force: {:.3f} N".format( + flight.max_rail_button2_normal_force + ), + maximum_lower_rail_button_shear_force="Maximum Lower Rail Button Shear Force: {:.3f} N".format( + flight.max_rail_button2_shear_force + ), ) - else: - events = { } - for event in flight.parachute_events: - trigger_time = event[0] - parachute = event[1] - open_time = trigger_time + parachute.lag - velocity = flight.free_stream_speed(open_time) - altitude = flight.z(open_time) - name = parachute.name.title() - events[name] = [] - events[name].append(name + " Ejection Triggered at: {:.3f} s".format(trigger_time)) - events[name].append(name + " Parachute Inflated at: {:.3f} s".format(open_time)) - events[name].append( - name - + " Parachute Inflated with Freestream Speed of: {:.3f} m/s".format( - velocity - ) + + if len(flight.impact_state) != 0: + _impact_conditions = ImpactConditions( + x_impact_position="X Impact: {:.3f} m".format(flight.x_impact), + y_impact_position="Y Impact: {:.3f} m".format(flight.y_impact), + time_of_impact="Time of Impact: {:.3f} s".format(flight.t_final), + impact_velocity="Velocity at Impact: {:.3f} m/s".format( + flight.impact_velocity + ), ) - events[name].append( - name - + " Parachute Inflated at Height of: {:.3f} m (AGL)".format( - altitude - flight.env.elevation - ) + elif flight.terminate_on_apogee is False: + _impact_conditions = ImpactConditions( + time="Time: {:.3f} s".format(flight.solution[-1][0]), + altitude="Altitude: {:.3f} m".format(flight.solution[-1][3]), ) - _events_registered = EventsRegistered( - events_trace = events - ) - - _flight_data = FlightData( - initial_conditions = _initial_conditions, - numerical_integration_settings = _numerical_integration_settings, - surface_wind_conditions = _surface_wind_conditions, - launch_rail_conditions = _launch_rail_conditions, - out_of_rail_conditions= _out_of_rail_conditions, - burnout_conditions = _burnout_conditions, - apogee_conditions = _apogee_conditions, - maximum_values = _maximum_values, - impact_conditions = _impact_conditions, - events_registered = _events_registered - ) - flight_summary = FlightSummary( flight_data = _flight_data ) + if len(flight.parachute_events) == 0: + _events_registered = EventsRegistered( + events_trace="No parachute event registered" + ) + else: + events = {} + for event in flight.parachute_events: + trigger_time = event[0] + parachute = event[1] + open_time = trigger_time + parachute.lag + velocity = flight.free_stream_speed(open_time) + altitude = flight.z(open_time) + name = parachute.name.title() + events[name] = [] + events[name].append( + name + " Ejection Triggered at: {:.3f} s".format(trigger_time) + ) + events[name].append( + name + " Parachute Inflated at: {:.3f} s".format(open_time) + ) + events[name].append( + name + + " Parachute Inflated with Freestream Speed of: {:.3f} m/s".format( + velocity + ) + ) + events[name].append( + name + + " Parachute Inflated at Height of: {:.3f} m (AGL)".format( + altitude - flight.env.elevation + ) + ) + _events_registered = EventsRegistered(events_trace=events) + + _flight_data = FlightData( + initial_conditions=_initial_conditions, + numerical_integration_settings=_numerical_integration_settings, + surface_wind_conditions=_surface_wind_conditions, + launch_rail_conditions=_launch_rail_conditions, + out_of_rail_conditions=_out_of_rail_conditions, + burnout_conditions=_burnout_conditions, + apogee_conditions=_apogee_conditions, + maximum_values=_maximum_values, + impact_conditions=_impact_conditions, + events_registered=_events_registered, + ) - return flight_summary + flight_summary = FlightSummary(flight_data=_flight_data) + return flight_summary + except Exception as e: + raise HTTPException( + status_code=500, detail=f"Failed to simulate flight: {e}" + ) from e diff --git a/lib/controllers/motor.py b/lib/controllers/motor.py index 24393f9..a01eafb 100644 --- a/lib/controllers/motor.py +++ b/lib/controllers/motor.py @@ -1,16 +1,23 @@ -from fastapi import Response, status -from typing import Any, Dict, Union +from typing import Union +from fastapi import HTTPException, status from rocketpy.motors.solid_motor import SolidMotor from rocketpy.motors.liquid_motor import LiquidMotor from rocketpy.motors.hybrid_motor import HybridMotor -from rocketpy import TankGeometry, Fluid import jsonpickle from lib.models.motor import Motor, MotorKinds from lib.repositories.motor import MotorRepository -from lib.views.motor import MotorSummary, MotorData, MotorCreated, MotorUpdated, MotorDeleted, MotorPickle +from lib.views.motor import ( + MotorSummary, + MotorData, + MotorCreated, + MotorUpdated, + MotorDeleted, + MotorPickle, +) -class MotorController(): + +class MotorController: """ Controller for the motor model. @@ -20,6 +27,7 @@ class MotorController(): Enables: - Create a rocketpy.Motor object from a Motor model object. """ + def __init__(self, motor: Motor, motor_kind): self.guard(motor, motor_kind) motor_core = { @@ -28,14 +36,15 @@ def __init__(self, motor: Motor, motor_kind): "nozzle_radius": motor.nozzle_radius, "dry_mass": motor.dry_mass, "dry_inertia": motor.dry_inertia, - "center_of_dry_mass_position": motor.center_of_dry_mass_position + "center_of_dry_mass_position": motor.center_of_dry_mass_position, } match motor_kind: case MotorKinds.liquid: rocketpy_motor = LiquidMotor(**motor_core) case MotorKinds.hybrid: - rocketpy_motor = HybridMotor(**motor_core, + rocketpy_motor = HybridMotor( + **motor_core, throat_radius=motor.throat_radius, grain_number=motor.grain_number, grain_density=motor.grain_density, @@ -43,10 +52,11 @@ def __init__(self, motor: Motor, motor_kind): grain_initial_inner_radius=motor.grain_initial_inner_radius, grain_initial_height=motor.grain_initial_height, grain_separation=motor.grain_separation, - grains_center_of_mass_position=motor.grains_center_of_mass_position + grains_center_of_mass_position=motor.grains_center_of_mass_position, ) case _: - rocketpy_motor = SolidMotor(**motor_core, + rocketpy_motor = SolidMotor( + **motor_core, grain_number=motor.grain_number, grain_density=motor.grain_density, grain_outer_radius=motor.grain_outer_radius, @@ -55,36 +65,45 @@ def __init__(self, motor: Motor, motor_kind): grains_center_of_mass_position=motor.grains_center_of_mass_position, grain_separation=motor.grain_separation, throat_radius=motor.throat_radius, - interpolation_method=motor.interpolation_method + interpolation_method=motor.interpolation_method, ) if motor_kind != MotorKinds.solid: for tank in motor.tanks: rocketpy_motor.add_tank(tank.tank, tank.position) - - self.motor_kind = motor_kind #tracks motor kind state + + self.motor_kind = motor_kind # tracks motor kind state self.rocketpy_motor = rocketpy_motor self.motor = motor - + def guard(self, motor: Motor, motor_kind): if motor_kind != MotorKinds.solid and motor.tanks is None: - return Response(status_code=status.HTTP_400_BAD_REQUEST, content="Tanks must be provided for liquid and hybrid motors.") + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="Tanks must be provided for liquid and hybrid motors.", + ) - async def create_motor(self) -> "Union[MotorCreated, Response]": + async def create_motor(self) -> "Union[MotorCreated, HTTPException]": """ Create a motor in the database. Returns: - Dict[str, str]: motor id. + MotorCreated: motor id. """ motor = MotorRepository(motor=self.motor) - successfully_created_motor = await motor.create_motor(motor_kind = self.motor_kind) - if successfully_created_motor: - return MotorCreated(motor_id=str(motor.motor_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + successfully_created_motor = await motor.create_motor( + motor_kind=self.motor_kind + ) + if not successfully_created_motor: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create motor.", + ) + + return MotorCreated(motor_id=str(motor.motor_id)) @staticmethod - async def get_motor(motor_id: int) -> "Union[Motor, Response]": + async def get_motor(motor_id: int) -> "Union[Motor, HTTPException]": """ Get a motor from the database. @@ -97,14 +116,16 @@ async def get_motor(motor_id: int) -> "Union[Motor, Response]": Raises: HTTP 404 Not Found: If the motor is not found in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." + ) + return successfully_read_motor @staticmethod - async def get_rocketpy_motor(motor_id: int) -> "Union[MotorPickle, Response]": + async def get_rocketpy_motor(motor_id: int) -> "Union[MotorPickle, HTTPException]": """ Get a rocketpy motor object encoded as jsonpickle string from the database. @@ -117,18 +138,24 @@ async def get_rocketpy_motor(motor_id: int) -> "Union[MotorPickle, Response]": Raises: HTTP 404 Not Found: If the motor is not found in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." + ) - successfully_read_rocketpy_motor = \ - MotorController(motor = successfully_read_motor, - motor_kind = MotorKinds(successfully_read_motor._motor_kind)).rocketpy_motor + successfully_read_rocketpy_motor = MotorController( + motor=successfully_read_motor, + motor_kind=MotorKinds(successfully_read_motor._motor_kind), + ).rocketpy_motor - return MotorPickle(jsonpickle_rocketpy_motor=jsonpickle.encode(successfully_read_rocketpy_motor)) + return MotorPickle( + jsonpickle_rocketpy_motor=jsonpickle.encode( + successfully_read_rocketpy_motor + ) + ) - async def update_motor(self, motor_id: int) -> "Union[MotorUpdated, Response]": + async def update_motor(self, motor_id: int) -> "Union[MotorUpdated, HTTPException]": """ Update a motor in the database. @@ -136,25 +163,30 @@ async def update_motor(self, motor_id: int) -> "Union[MotorUpdated, Response]": motor_id (int): Motor id. Returns: - Dict[str, Any]: motor id and message. + MotorUpdated: motor id and message. Raises: HTTP 404 Not Found: If the motor is not found in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." + ) - successfully_updated_motor = \ - await MotorRepository(motor=self.motor, motor_id=motor_id).update_motor(motor_kind = self.motor_kind) + successfully_updated_motor = await MotorRepository( + motor=self.motor, motor_id=motor_id + ).update_motor(motor_kind=self.motor_kind) + if not successfully_updated_motor: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update motor.", + ) - if successfully_updated_motor: - return MotorUpdated(new_motor_id=str(successfully_updated_motor)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return MotorUpdated(new_motor_id=str(successfully_updated_motor)) @staticmethod - async def delete_motor(motor_id: int) -> "Union[MotorDeleted, Response]": + async def delete_motor(motor_id: int) -> "Union[MotorDeleted, HTTPException]": """ Delete a motor from the database. @@ -162,24 +194,30 @@ async def delete_motor(motor_id: int) -> "Union[MotorDeleted, Response]": motor_id (int): motor id. Returns: - Dict[str, str]: motor id and message. + MotorDeleted: motor id and message. Raises: HTTP 404 Not Found: If the motor is not found in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." + ) - successfully_deleted_motor = \ - await MotorRepository(motor_id=motor_id).delete_motor() - if successfully_deleted_motor: - return MotorDeleted(deleted_motor_id=str(motor_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + successfully_deleted_motor = await MotorRepository( + motor_id=motor_id + ).delete_motor() + if not successfully_deleted_motor: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete motor.", + ) + + return MotorDeleted(deleted_motor_id=str(motor_id)) @staticmethod - async def simulate(motor_id: int) -> "Union[MotorSummary, Response]": + async def simulate(motor_id: int) -> "Union[MotorSummary, HTTPException]": """ Simulate a rocketpy motor. @@ -192,39 +230,48 @@ async def simulate(motor_id: int) -> "Union[MotorSummary, Response]": Raises: HTTP 404 Not Found: If the motor does not exist in the database. """ - successfully_read_motor = \ - await MotorRepository(motor_id=motor_id).get_motor() + successfully_read_motor = await MotorRepository(motor_id=motor_id).get_motor() if not successfully_read_motor: - return Response(status_code=status.HTTP_404_NOT_FOUND) - - motor = MotorController(motor=successfully_read_motor, - motor_kind = MotorKinds(successfully_read_motor._motor_kind)).rocketpy_motor - motor_simulation_numbers = MotorData( - total_burning_time="Total Burning Time: " + str(motor.burn_out_time) + " s", - - total_propellant_mass="Total Propellant Mass: " - + "{:.3f}".format(motor.propellant_initial_mass) - + " kg", - - average_propellant_exhaust_velocity="Average Propellant Exhaust Velocity: " - + "{:.3f}".format( - motor.exhaust_velocity.average(*motor.burn_time) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Motor not found." ) - + " m/s", - - average_thrust="Average Thrust: " + "{:.3f}".format(motor.average_thrust) + " N", - - maximum_thrust="Maximum Thrust: " - + str(motor.max_thrust) - + " N at " - + str(motor.max_thrust_time) - + " s after ignition.", - total_impulse="Total Impulse: " + "{:.3f}".format(motor.total_impulse) + " Ns\n" - ) - #motor_simulation_plots = MotorPlots( - # motor.thrust.plot(lower=lower_limit, upper=upper_limit) - #) - - motor_summary = MotorSummary( motor_data = motor_simulation_numbers ) #, plots=motor_simulation_plots ) - return motor_summary + try: + motor = MotorController( + motor=successfully_read_motor, + motor_kind=MotorKinds(successfully_read_motor._motor_kind), + ).rocketpy_motor + motor_simulation_numbers = MotorData( + total_burning_time="Total Burning Time: " + + str(motor.burn_out_time) + + " s", + total_propellant_mass="Total Propellant Mass: " + + "{:.3f}".format(motor.propellant_initial_mass) + + " kg", + average_propellant_exhaust_velocity="Average Propellant Exhaust Velocity: " + + "{:.3f}".format(motor.exhaust_velocity.average(*motor.burn_time)) + + " m/s", + average_thrust="Average Thrust: " + + "{:.3f}".format(motor.average_thrust) + + " N", + maximum_thrust="Maximum Thrust: " + + str(motor.max_thrust) + + " N at " + + str(motor.max_thrust_time) + + " s after ignition.", + total_impulse="Total Impulse: " + + "{:.3f}".format(motor.total_impulse) + + " Ns\n", + ) + # motor_simulation_plots = MotorPlots( + # motor.thrust.plot(lower=lower_limit, upper=upper_limit) + # ) + motor_summary = MotorSummary( + motor_data=motor_simulation_numbers + ) # , plots=motor_simulation_plots ) + return motor_summary + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to simulate motor: {e}", + ) from e diff --git a/lib/controllers/rocket.py b/lib/controllers/rocket.py index 04b502f..1885709 100644 --- a/lib/controllers/rocket.py +++ b/lib/controllers/rocket.py @@ -1,9 +1,10 @@ -from typing import Dict, Any, Union -import jsonpickle +from typing import Union import ast +import jsonpickle + +from fastapi import HTTPException, status -from fastapi import Response, status -#from inspect import getsourcelines +# from inspect import getsourcelines from rocketpy.rocket.parachute import Parachute as RocketpyParachute from rocketpy.rocket.rocket import Rocket as RocketpyRocket @@ -13,13 +14,25 @@ from lib.controllers.motor import MotorController from lib.models.rocket import Rocket, RocketOptions -from lib.models.motor import MotorKinds +from lib.models.motor import MotorKinds from lib.models.aerosurfaces import NoseCone, TrapezoidalFins, Tail from lib.models.parachute import Parachute from lib.repositories.rocket import RocketRepository -from lib.views.rocket import InertiaDetails, RocketGeometricalParameters, RocketAerodynamicsQuantities, ParachuteData, RocketData, RocketSummary, RocketCreated, RocketUpdated, RocketDeleted, RocketPickle - -class RocketController(): +from lib.views.rocket import ( + InertiaDetails, + RocketGeometricalParameters, + RocketAerodynamicsQuantities, + ParachuteData, + RocketData, + RocketSummary, + RocketCreated, + RocketUpdated, + RocketDeleted, + RocketPickle, +) + + +class RocketController: """ Controller for the Rocket model. @@ -29,56 +42,65 @@ class RocketController(): Enables: create a RocketpyRocket object from a Rocket model object. """ + def __init__(self, rocket: Rocket, rocket_option, motor_kind): rocketpy_rocket = RocketpyRocket( - radius=rocket.radius, - mass=rocket.mass, - inertia=rocket.inertia, - power_off_drag=f"lib/data/{rocket_option.value.lower()}/powerOffDragCurve.csv", - power_on_drag=f"lib/data/{rocket_option.value.lower()}/powerOnDragCurve.csv", - center_of_mass_without_motor=rocket.center_of_mass_without_motor, - coordinate_system_orientation=rocket.coordinate_system_orientation + radius=rocket.radius, + mass=rocket.mass, + inertia=rocket.inertia, + power_off_drag=f"lib/data/{rocket_option.value.lower()}/powerOffDragCurve.csv", + power_on_drag=f"lib/data/{rocket_option.value.lower()}/powerOnDragCurve.csv", + center_of_mass_without_motor=rocket.center_of_mass_without_motor, + coordinate_system_orientation=rocket.coordinate_system_orientation, ) - #RailButtons - rocketpy_rocket.set_rail_buttons(upper_button_position=rocket.rail_buttons.upper_button_position, - lower_button_position=rocket.rail_buttons.lower_button_position, - angular_position=rocket.rail_buttons.angular_position) - rocketpy_rocket.add_motor(MotorController(rocket.motor, motor_kind).rocketpy_motor, - rocket.motor_position) + # RailButtons + rocketpy_rocket.set_rail_buttons( + upper_button_position=rocket.rail_buttons.upper_button_position, + lower_button_position=rocket.rail_buttons.lower_button_position, + angular_position=rocket.rail_buttons.angular_position, + ) + rocketpy_rocket.add_motor( + MotorController(rocket.motor, motor_kind).rocketpy_motor, + rocket.motor_position, + ) - #NoseCone + # NoseCone nose = self.NoseConeController(rocket.nose).rocketpy_nose rocketpy_rocket.aerodynamic_surfaces.add(nose, nose.position) rocketpy_rocket.evaluate_static_margin() - #FinSet - #TBD: re-write this to match overall fins not only TrapezoidalFins + # FinSet + # TBD: re-write this to match overall fins not only TrapezoidalFins finset = self.TrapezoidalFinsController(rocket.fins).rocketpy_finset rocketpy_rocket.aerodynamic_surfaces.add(finset, finset.position) rocketpy_rocket.evaluate_static_margin() - #Tail + # Tail tail = self.TailController(rocket.tail).rocketpy_tail rocketpy_rocket.aerodynamic_surfaces.add(tail, tail.position) rocketpy_rocket.evaluate_static_margin() - #Parachutes + # Parachutes for p in range(len(rocket.parachutes)): parachute_trigger = rocket.parachutes[p].triggers[0] if self.ParachuteController.check_trigger(parachute_trigger): - rocket.parachutes[p].triggers[0] = compile(parachute_trigger, '', 'eval') - parachute = self.ParachuteController(rocket.parachutes, p).rocketpy_parachute + rocket.parachutes[p].triggers[0] = compile( + parachute_trigger, "", "eval" + ) + parachute = self.ParachuteController( + rocket.parachutes, p + ).rocketpy_parachute rocketpy_rocket.parachutes.append(parachute) else: print("Parachute trigger not valid. Skipping parachute.") continue - self.rocket_option = rocket_option #tracks rocket option state + self.rocket_option = rocket_option # tracks rocket option state self.rocketpy_rocket = rocketpy_rocket self.rocket = rocket - class NoseConeController(): + class NoseConeController: """ Controller for the NoseCone model. @@ -88,18 +110,19 @@ class NoseConeController(): Enables: - Create a rocketpy.AeroSurface.NoseCone object from a NoseCone model object. """ + def __init__(self, nose: NoseCone): rocketpy_nose = RocketpyNoseCone( - length=nose.length, - kind=nose.kind, - base_radius=nose.base_radius, - rocket_radius=nose.rocket_radius + length=nose.length, + kind=nose.kind, + base_radius=nose.base_radius, + rocket_radius=nose.rocket_radius, ) rocketpy_nose.position = nose.position self.rocketpy_nose = rocketpy_nose self.nose = nose - class TrapezoidalFinsController(): + class TrapezoidalFinsController: """ Controller for the TrapezoidalFins model. @@ -109,21 +132,22 @@ class TrapezoidalFinsController(): Enables: - Create a rocketpy.AeroSurface.TrapezoidalFins object from a TrapezoidalFins model object. """ + def __init__(self, trapezoidal_fins: TrapezoidalFins): rocketpy_finset = RocketpyTrapezoidalFins( - n=trapezoidal_fins.n, - root_chord=trapezoidal_fins.root_chord, - tip_chord=trapezoidal_fins.tip_chord, - span=trapezoidal_fins.span, - cant_angle=trapezoidal_fins.cant_angle, - rocket_radius=trapezoidal_fins.radius, - airfoil=trapezoidal_fins.airfoil + n=trapezoidal_fins.n, + root_chord=trapezoidal_fins.root_chord, + tip_chord=trapezoidal_fins.tip_chord, + span=trapezoidal_fins.span, + cant_angle=trapezoidal_fins.cant_angle, + rocket_radius=trapezoidal_fins.radius, + airfoil=trapezoidal_fins.airfoil, ) rocketpy_finset.position = trapezoidal_fins.position self.rocketpy_finset = rocketpy_finset self.trapezoidal_fins = trapezoidal_fins - class TailController(): + class TailController: """ Controller for the Tail model. @@ -133,18 +157,19 @@ class TailController(): Enables: - Create a rocketpy.AeroSurface.Tail object from a Tail model object. """ + def __init__(self, tail: Tail): rocketpy_tail = RocketpyTail( - top_radius=tail.top_radius, - bottom_radius=tail.bottom_radius, - length=tail.length, - rocket_radius=tail.radius + top_radius=tail.top_radius, + bottom_radius=tail.bottom_radius, + length=tail.length, + rocket_radius=tail.radius, ) rocketpy_tail.position = tail.position self.rocketpy_tail = rocketpy_tail self.tail = tail - class ParachuteController(): + class ParachuteController: """ Controller for the Parachute model. @@ -154,14 +179,15 @@ class ParachuteController(): Enables: - Create a RocketpyParachute.Parachute object from a Parachute model object. """ + def __init__(self, parachute: Parachute, p: int): rocketpy_parachute = RocketpyParachute( - name=parachute[p].name[0], - cd_s=parachute[p].cd_s[0], - trigger=eval(parachute[p].triggers[0]), - sampling_rate=parachute[p].sampling_rate[0], - lag=parachute[p].lag[0], - noise=parachute[p].noise[0] + name=parachute[p].name[0], + cd_s=parachute[p].cd_s[0], + trigger=eval(parachute[p].triggers[0]), + sampling_rate=parachute[p].sampling_rate[0], + lag=parachute[p].lag[0], + noise=parachute[p].noise[0], ) self.rocketpy_parachute = rocketpy_parachute self.parachute = parachute @@ -180,7 +206,7 @@ def check_trigger(expression: str) -> bool: # Parsing the expression into an AST try: - parsed_expression = ast.parse(expression, mode='eval') + parsed_expression = ast.parse(expression, mode="eval") except SyntaxError: print("Invalid syntax.") return False @@ -189,8 +215,10 @@ def check_trigger(expression: str) -> bool: if isinstance(parsed_expression.body, ast.Constant): return True # Name case (supported after beta v1) - if isinstance(parsed_expression.body, ast.Name) \ - and parsed_expression.body.id == "apogee": + if ( + isinstance(parsed_expression.body, ast.Name) + and parsed_expression.body.id == "apogee" + ): global apogee apogee = "apogee" return True @@ -225,21 +253,27 @@ def check_trigger(expression: str) -> bool: return False return True - async def create_rocket(self) -> "Union[RocketCreated, Response]": + async def create_rocket(self) -> "Union[RocketCreated, HTTPException]": """ Create a rocket in the database. Returns: - Dict[str, str]: Rocket id. + RocketCreated: Rocket id. """ rocket = RocketRepository(rocket=self.rocket) - successfully_created_rocket = await rocket.create_rocket(rocket_option = self.rocket_option) - if successfully_created_rocket: - return RocketCreated(rocket_id=str(rocket.rocket_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + successfully_created_rocket = await rocket.create_rocket( + rocket_option=self.rocket_option + ) + if not successfully_created_rocket: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create rocket.", + ) + + return RocketCreated(rocket_id=str(rocket.rocket_id)) @staticmethod - async def get_rocket(rocket_id: int) -> "Union[Rocket, Response]": + async def get_rocket(rocket_id: int) -> "Union[Rocket, HTTPException]": """ Get a rocket from the database. @@ -252,14 +286,20 @@ async def get_rocket(rocket_id: int) -> "Union[Rocket, Response]": Raises: HTTP 404 Not Found: If the rocket is not found in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) + return successfully_read_rocket @staticmethod - async def get_rocketpy_rocket(rocket_id: int) -> "Union[RocketPickle, Response]": + async def get_rocketpy_rocket( + rocket_id: int, + ) -> "Union[RocketPickle, HTTPException]": """ Get a rocketpy rocket object encoded as jsonpickle string from the database. @@ -272,19 +312,29 @@ async def get_rocketpy_rocket(rocket_id: int) -> "Union[RocketPickle, Response]" Raises: HTTP 404 Not Found: If the rocket is not found in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) - successfully_read_rocketpy_rocket = \ - RocketController(rocket=successfully_read_rocket, - rocket_option = RocketOptions(successfully_read_rocket._rocket_option), - motor_kind = MotorKinds(successfully_read_rocket.motor._motor_kind)).rocketpy_rocket + successfully_read_rocketpy_rocket = RocketController( + rocket=successfully_read_rocket, + rocket_option=RocketOptions(successfully_read_rocket._rocket_option), + motor_kind=MotorKinds(successfully_read_rocket.motor._motor_kind), + ).rocketpy_rocket - return RocketPickle(jsonpickle_rocketpy_rocket=jsonpickle.encode(successfully_read_rocketpy_rocket)) + return RocketPickle( + jsonpickle_rocketpy_rocket=jsonpickle.encode( + successfully_read_rocketpy_rocket + ) + ) - async def update_rocket(self, rocket_id: int) -> "Union[RocketUpdated, Response]": + async def update_rocket( + self, rocket_id: int + ) -> "Union[RocketUpdated, HTTPException]": """ Update a rocket in the database. @@ -292,25 +342,32 @@ async def update_rocket(self, rocket_id: int) -> "Union[RocketUpdated, Response] rocket_id (int): rocket id. Returns: - Dict[str, Any]: rocket id and message. + RocketUpdated: rocket id and message. Raises: HTTP 404 Not Found: If the rocket is not found in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) - successfully_updated_rocket = \ - await RocketRepository(rocket=self.rocket, rocket_id=rocket_id).update_rocket(rocket_option = self.rocket_option) + successfully_updated_rocket = await RocketRepository( + rocket=self.rocket, rocket_id=rocket_id + ).update_rocket(rocket_option=self.rocket_option) + if not successfully_updated_rocket: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update rocket.", + ) - if successfully_updated_rocket: - return RocketUpdated(new_rocket_id=str(successfully_updated_rocket)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return RocketUpdated(new_rocket_id=str(successfully_updated_rocket)) @staticmethod - async def delete_rocket(rocket_id: int) -> "Union[RocketDeleted, Response]": + async def delete_rocket(rocket_id: int) -> "Union[RocketDeleted, HTTPException]": """ Delete a rocket from the database. @@ -318,24 +375,32 @@ async def delete_rocket(rocket_id: int) -> "Union[RocketDeleted, Response]": rocket_id (int): Rocket id. Returns: - Dict[str, str]: Rocket id and message. + RocketDeleted: Rocket id and message. Raises: HTTP 404 Not Found: If the rocket is not found in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) + + successfully_deleted_rocket = await RocketRepository( + rocket_id=rocket_id + ).delete_rocket() + if not successfully_deleted_rocket: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete rocket.", + ) - successfully_deleted_rocket = \ - await RocketRepository(rocket_id=rocket_id).delete_rocket() - if successfully_deleted_rocket: - return RocketDeleted(deleted_rocket_id=str(rocket_id)) - return Response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + return RocketDeleted(deleted_rocket_id=str(rocket_id)) @staticmethod - async def simulate(rocket_id: int) -> "Union[RocketSummary, Response]": + async def simulate(rocket_id: int) -> "Union[RocketSummary, HTTPException]": """ Simulate a rocket rocket. @@ -348,112 +413,148 @@ async def simulate(rocket_id: int) -> "Union[RocketSummary, Response]": Raises: HTTP 404 Not Found: If the rocket does not exist in the database. """ - successfully_read_rocket = \ - await RocketRepository(rocket_id=rocket_id).get_rocket() + successfully_read_rocket = await RocketRepository( + rocket_id=rocket_id + ).get_rocket() if not successfully_read_rocket: - return Response(status_code=status.HTTP_404_NOT_FOUND) - - rocket = RocketController(rocket=successfully_read_rocket, - rocket_option = RocketOptions(successfully_read_rocket._rocket_option), - motor_kind = MotorKinds(successfully_read_rocket.motor._motor_kind)).rocketpy_rocket - - _inertia_details = InertiaDetails( - rocket_mass_without_propellant = "Rocket Mass: {:.3f} kg (No Propellant)".format(rocket.mass), - rocket_mass_with_propellant = "Rocket Mass: {:.3f} kg (With Propellant)".format(rocket.total_mass(0)), - rocket_inertia_with_motor_without_propellant = [ - "Rocket Inertia (with motor, but without propellant) 11: {:.3f} kg*m2".format(rocket.dry_I_11), - "Rocket Inertia (with motor, but without propellant) 22: {:.3f} kg*m2".format(rocket.dry_I_22), - "Rocket Inertia (with motor, but without propellant) 33: {:.3f} kg*m2".format(rocket.dry_I_33), - "Rocket Inertia (with motor, but without propellant) 12: {:.3f} kg*m2".format(rocket.dry_I_12), - "Rocket Inertia (with motor, but without propellant) 13: {:.3f} kg*m2".format(rocket.dry_I_13), - "Rocket Inertia (with motor, but without propellant) 23: {:.3f} kg*m2".format(rocket.dry_I_23) - ] - ) + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Rocket not found." + ) - _rocket_geometrical_parameters = RocketGeometricalParameters( - rocket_maximum_radius = "Rocket Maximum Radius: " + str(rocket.radius) + " m", - rocket_frontal_area = "Rocket Frontal Area: " + "{:.6f}".format(rocket.area) + " m2", + try: + rocket = RocketController( + rocket=successfully_read_rocket, + rocket_option=RocketOptions(successfully_read_rocket._rocket_option), + motor_kind=MotorKinds(successfully_read_rocket.motor._motor_kind), + ).rocketpy_rocket + + _inertia_details = InertiaDetails( + rocket_mass_without_propellant="Rocket Mass: {:.3f} kg (No Propellant)".format( + rocket.mass + ), + rocket_mass_with_propellant="Rocket Mass: {:.3f} kg (With Propellant)".format( + rocket.total_mass(0) + ), + rocket_inertia_with_motor_without_propellant=[ + "Rocket Inertia (with motor, but without propellant) 11: {:.3f} kg*m2".format( + rocket.dry_I_11 + ), + "Rocket Inertia (with motor, but without propellant) 22: {:.3f} kg*m2".format( + rocket.dry_I_22 + ), + "Rocket Inertia (with motor, but without propellant) 33: {:.3f} kg*m2".format( + rocket.dry_I_33 + ), + "Rocket Inertia (with motor, but without propellant) 12: {:.3f} kg*m2".format( + rocket.dry_I_12 + ), + "Rocket Inertia (with motor, but without propellant) 13: {:.3f} kg*m2".format( + rocket.dry_I_13 + ), + "Rocket Inertia (with motor, but without propellant) 23: {:.3f} kg*m2".format( + rocket.dry_I_23 + ), + ], + ) - rocket_codm_nozzle_exit_distance = "Rocket Center of Dry Mass - Nozzle Exit Distance: " - + "{:.3f} m".format( - abs( - rocket.center_of_dry_mass_position - rocket.motor_position - ) - ), + _rocket_geometrical_parameters = RocketGeometricalParameters( + rocket_maximum_radius="Rocket Maximum Radius: " + + str(rocket.radius) + + " m", + rocket_frontal_area="Rocket Frontal Area: " + + "{:.6f}".format(rocket.area) + + " m2", + rocket_codm_nozzle_exit_distance="Rocket Center of Dry Mass - Nozzle Exit Distance: " + + "{:.3f} m".format( + abs(rocket.center_of_dry_mass_position - rocket.motor_position) + ), + rocket_codm_center_of_propellant_mass="Rocket Center of Dry Mass - Center of Propellant Mass: " + + "{:.3f} m".format( + abs( + rocket.center_of_propellant_position(0) + - rocket.center_of_dry_mass_position + ) + ), + rocket_codm_loaded_center_of_mass="Rocket Center of Mass - Rocket Loaded Center of Mass: " + + "{:.3f} m".format( + abs(rocket.center_of_mass(0) - rocket.center_of_dry_mass_position) + ), + ) - rocket_codm_center_of_propellant_mass = "Rocket Center of Dry Mass - Center of Propellant Mass: " - + "{:.3f} m".format( - abs( - rocket.center_of_propellant_position(0) - - rocket.center_of_dry_mass_position + _aerodynamics_lift_coefficient_derivatives = {} + for surface, _position in rocket.aerodynamic_surfaces: + name = surface.name + _aerodynamics_lift_coefficient_derivatives[name] = [] + _aerodynamics_lift_coefficient_derivatives[name].append( + name + + " Lift Coefficient Derivative: {:.3f}".format(surface.clalpha(0)) + + "/rad" ) - ), - rocket_codm_loaded_center_of_mass = "Rocket Center of Mass - Rocket Loaded Center of Mass: " - + "{:.3f} m".format( - abs( - rocket.center_of_mass(0) - - rocket.center_of_dry_mass_position + _aerodynamics_center_of_pressure = {} + for surface, _position in rocket.aerodynamic_surfaces: + name = surface.name + cpz = surface.cp[2] + _aerodynamics_center_of_pressure[name] = [] + _aerodynamics_center_of_pressure[name].append( + name + " Center of Pressure to CM: {:.3f}".format(cpz) + " m" ) - ) - ) - _aerodynamics_lift_coefficient_derivatives = {} - for surface, _position in rocket.aerodynamic_surfaces: - name = surface.name - _aerodynamics_lift_coefficient_derivatives[name] = [] - _aerodynamics_lift_coefficient_derivatives[name].append( - name + " Lift Coefficient Derivative: {:.3f}".format(surface.clalpha(0)) + "/rad" + _rocket_aerodynamics_quantities = RocketAerodynamicsQuantities( + aerodynamics_lift_coefficient_derivatives=_aerodynamics_lift_coefficient_derivatives, + aerodynamics_center_of_pressure=_aerodynamics_center_of_pressure, + distance_cop_to_codm="Distance from Center of Pressure to Center of Dry Mass: " + + "{:.3f}".format(rocket.center_of_mass(0) - rocket.cp_position) + + " m", + initial_static_margin="Initial Static Margin: " + + "{:.3f}".format(rocket.static_margin(0)) + + " c", + final_static_margin="Final Static Margin: " + + "{:.3f}".format(rocket.static_margin(rocket.motor.burn_out_time)) + + " c", ) - _aerodynamics_center_of_pressure = {} - for surface, _position in rocket.aerodynamic_surfaces: - name = surface.name - cpz = surface.cp[2] - _aerodynamics_center_of_pressure[name] = [] - _aerodynamics_center_of_pressure[name].append( - name + " Center of Pressure to CM: {:.3f}".format(cpz) + " m" + _parachute_details = {} + _parachute_ejection_signal_trigger = {} + _parachute_ejection_system_refresh_rate = {} + _parachute_lag = {} + for chute in rocket.parachutes: + _parachute_details[chute.name] = chute.__str__() + + if chute.trigger.__name__ == "": + # line = getsourcelines(chute.trigger)[0][0] + # _parachute_ejection_signal_trigger[chute.name] = "Ejection signal trigger: " + line.split("lambda ")[1].split(",")[0].split("\n")[0] + pass + + else: + _parachute_ejection_signal_trigger[chute.name] = ( + "Ejection signal trigger: " + chute.trigger.__name__ + ) + _parachute_ejection_system_refresh_rate[ + chute.name + ] = "Ejection system refresh rate: {chute.sampling_rate:.3f} Hz" + _parachute_lag[ + chute.name + ] = "Time between ejection signal is triggered and the parachute is fully opened: {chute.lag:.1f} s\n" + + _parachute_data = ParachuteData( + parachute_details=_parachute_details, + # parachute_ejection_signal_trigger = _parachute_ejection_signal_trigger, + parachute_ejection_system_refresh_rate=_parachute_ejection_system_refresh_rate, + parachute_lag=_parachute_lag, ) - _rocket_aerodynamics_quantities = RocketAerodynamicsQuantities( - aerodynamics_lift_coefficient_derivatives = _aerodynamics_lift_coefficient_derivatives, - aerodynamics_center_of_pressure = _aerodynamics_center_of_pressure, - distance_cop_to_codm = "Distance from Center of Pressure to Center of Dry Mass: " + "{:.3f}".format(rocket.center_of_mass(0) - rocket.cp_position) + " m", - initial_static_margin = "Initial Static Margin: " + "{:.3f}".format(rocket.static_margin(0)) + " c", - final_static_margin ="Final Static Margin: " + "{:.3f}".format(rocket.static_margin(rocket.motor.burn_out_time)) + " c" - ) - - _parachute_details = {} - _parachute_ejection_signal_trigger = {} - _parachute_ejection_system_refresh_rate = {} - _parachute_lag = {} - for chute in rocket.parachutes: - _parachute_details[chute.name] = chute.__str__() - - if chute.trigger.__name__ == "": - #line = getsourcelines(chute.trigger)[0][0] - #_parachute_ejection_signal_trigger[chute.name] = "Ejection signal trigger: " + line.split("lambda ")[1].split(",")[0].split("\n")[0] - pass - - else: - _parachute_ejection_signal_trigger[chute.name] = "Ejection signal trigger: " + chute.trigger.__name__ - _parachute_ejection_system_refresh_rate[chute.name] = "Ejection system refresh rate: {chute.sampling_rate:.3f} Hz" - _parachute_lag[chute.name] = "Time between ejection signal is triggered and the parachute is fully opened: {chute.lag:.1f} s\n" - - _parachute_data = ParachuteData( - parachute_details = _parachute_details, - #parachute_ejection_signal_trigger = _parachute_ejection_signal_trigger, - parachute_ejection_system_refresh_rate = _parachute_ejection_system_refresh_rate, - parachute_lag = _parachute_lag - ) - - _rocket_data = RocketData( - inertia_details = _inertia_details, - rocket_geometrical_parameters = _rocket_geometrical_parameters, - rocket_aerodynamics_quantities = _rocket_aerodynamics_quantities, - parachute_data = _parachute_data - ) - - rocket_summary = RocketSummary( rocket_data = _rocket_data ) + _rocket_data = RocketData( + inertia_details=_inertia_details, + rocket_geometrical_parameters=_rocket_geometrical_parameters, + rocket_aerodynamics_quantities=_rocket_aerodynamics_quantities, + parachute_data=_parachute_data, + ) - return rocket_summary + rocket_summary = RocketSummary(rocket_data=_rocket_data) + return rocket_summary + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to simulate rocket: {e}", + ) from e diff --git a/lib/models/aerosurfaces.py b/lib/models/aerosurfaces.py index 490c99f..73284ea 100644 --- a/lib/models/aerosurfaces.py +++ b/lib/models/aerosurfaces.py @@ -1,11 +1,13 @@ from typing import Optional from pydantic import BaseModel + class RailButtons(BaseModel, frozen=True): upper_button_position: Optional[float] = -0.5 lower_button_position: Optional[float] = 0.2 angular_position: Optional[float] = 45 + class NoseCone(BaseModel, frozen=True): length: float = 0.55829 kind: str = "vonKarman" @@ -13,6 +15,7 @@ class NoseCone(BaseModel, frozen=True): base_radius: float = 0.0635 rocket_radius: float = 0.0635 + class Fins(BaseModel, frozen=True): n: int = 4 root_chord: float = 0.12 @@ -23,10 +26,12 @@ class Fins(BaseModel, frozen=True): radius: float = 0.0635 airfoil: str = "" + class TrapezoidalFins(Fins, frozen=True): def __init__(self): super().__init__() + class Tail(BaseModel, frozen=True): top_radius: float = 0.0635 bottom_radius: float = 0.0435 diff --git a/lib/models/environment.py b/lib/models/environment.py index 4a1a5bc..0ce9afe 100644 --- a/lib/models/environment.py +++ b/lib/models/environment.py @@ -2,12 +2,15 @@ from typing import Optional from pydantic import BaseModel + class Env(BaseModel, frozen=True): latitude: float = 0 longitude: float = 0 elevation: Optional[int] = 1400 - #Opional parameters - atmospheric_model_type: Optional[str] = 'standard_atmosphere' - atmospheric_model_file: Optional[str] = 'GFS' - date: Optional[datetime.datetime] = datetime.datetime.today() + datetime.timedelta(days=1) + # Opional parameters + atmospheric_model_type: Optional[str] = "standard_atmosphere" + atmospheric_model_file: Optional[str] = "GFS" + date: Optional[datetime.datetime] = datetime.datetime.today() + datetime.timedelta( + days=1 + ) diff --git a/lib/models/flight.py b/lib/models/flight.py index 20182bd..61343cc 100644 --- a/lib/models/flight.py +++ b/lib/models/flight.py @@ -2,6 +2,7 @@ from lib.models.rocket import Rocket from lib.models.environment import Env + class Flight(BaseModel, frozen=True): environment: Env = Env() rocket: Rocket = Rocket() diff --git a/lib/models/motor.py b/lib/models/motor.py index fb57453..7a2a451 100644 --- a/lib/models/motor.py +++ b/lib/models/motor.py @@ -1,29 +1,40 @@ -from rocketpy import LevelBasedTank, MassBasedTank, MassFlowRateBasedTank, UllageBasedTank, SolidMotor, LiquidMotor, HybridMotor, TankGeometry, Function -from typing import Optional, Tuple, List, Dict from enum import Enum +from typing import Optional, Tuple, List +from rocketpy import ( + LevelBasedTank, + MassBasedTank, + MassFlowRateBasedTank, + UllageBasedTank, + TankGeometry, +) from pydantic import BaseModel, PrivateAttr + class MotorKinds(str, Enum): hybrid: str = "Hybrid" solid: str = "Solid" liquid: str = "Liquid" + class MotorEngines(str, Enum): cesaroni: str = "Cesaroni_M1670" custom: str = "Custom" + class TankKinds(str, Enum): level: str = "Level" mass: str = "Mass" mass_flow: str = "MassFlow" ullage: str = "Ullage" + class TankFluids(BaseModel, frozen=True): name: str = "FluidName" density: float = 100.0 + class MotorTank(BaseModel, frozen=True): - #Required parameters + # Required parameters geometry: "List[Tuple[Tuple[float,float],float]]" = [((0, 5), 1), ((5, 10), 2)] tank_kind: TankKinds = TankKinds.mass_flow gas: TankFluids = TankFluids() @@ -31,8 +42,8 @@ class MotorTank(BaseModel, frozen=True): name: str = "Tank" flux_time: "List[float]" = [0, 8] position: float = 1.0 - - #Optional parameters + + # Optional parameters discretize: Optional[int] = 100 liquid_height: Optional[float] = 0.5 liquid_mass: Optional[float] = 5.0 @@ -49,27 +60,29 @@ def __init__(self, **kwargs): super().__init__(**kwargs) tank_core = { "name": self.name, - "geometry": TankGeometry(geometry_dict = { t:f for t,f in self.geometry }), + "geometry": TankGeometry(geometry_dict=dict(self.geometry)), "flux_time": self.flux_time, "gas": self.gas, "liquid": self.liquid, - "discretize": self.discretize + "discretize": self.discretize, } match self.tank_kind: case TankKinds.level: tank = LevelBasedTank(**tank_core, liquid_height=self.liquid_height) case TankKinds.mass: - tank = MassBasedTank(**tank_core, - liquid_mass=self.liquid_mass, gas_mass=self.gas_mass) + tank = MassBasedTank( + **tank_core, liquid_mass=self.liquid_mass, gas_mass=self.gas_mass + ) case TankKinds.mass_flow: - tank = MassFlowRateBasedTank(**tank_core, - gas_mass_flow_rate_in=self.gas_mass_flow_rate_in, + tank = MassFlowRateBasedTank( + **tank_core, + gas_mass_flow_rate_in=self.gas_mass_flow_rate_in, gas_mass_flow_rate_out=self.gas_mass_flow_rate_out, liquid_mass_flow_rate_in=self.liquid_mass_flow_rate_in, liquid_mass_flow_rate_out=self.liquid_mass_flow_rate_out, initial_liquid_mass=self.initial_liquid_mass, - initial_gas_mass=self.initial_gas_mass + initial_gas_mass=self.initial_gas_mass, ) case TankKinds.ullage: tank = UllageBasedTank(**tank_core, ullage=self.ullage) @@ -80,10 +93,11 @@ def __hash__(self): temp = str(temp) return hash(temp) + class Motor(BaseModel, frozen=True): - #TBD: thrust_source must be the id of a previously uploaded .eng file and a list of "default" files must be provided in the api docs + # TBD: thrust_source must be the id of a previously uploaded .eng file and a list of "default" files must be provided in the api docs - #Required parameters + # Required parameters thrust_source: MotorEngines = MotorEngines.cesaroni burn_time: float = 3.9 nozzle_radius: float = 0.033 @@ -92,7 +106,7 @@ class Motor(BaseModel, frozen=True): center_of_dry_mass_position: float = 0.317 _motor_kind: MotorKinds = PrivateAttr() - #Optional parameters + # Optional parameters tanks: Optional["List[MotorTank]"] = [MotorTank()] grain_number: Optional[int] = 5 grain_density: Optional[float] = 1815 @@ -105,7 +119,7 @@ class Motor(BaseModel, frozen=True): interpolation_method: Optional[str] = "linear" coordinate_system_orientation: Optional[str] = "nozzle_to_combustion_chamber" - def __init__(self, motor_kind = MotorKinds.solid, **kwargs): + def __init__(self, motor_kind=MotorKinds.solid, **kwargs): super().__init__(**kwargs) self._motor_kind = motor_kind diff --git a/lib/models/parachute.py b/lib/models/parachute.py index f1106a3..12bf480 100644 --- a/lib/models/parachute.py +++ b/lib/models/parachute.py @@ -1,23 +1,29 @@ from typing import List, Tuple from pydantic import BaseModel + class Parachute(BaseModel, frozen=True): - name: "List[str]" = ["Main","Drogue"] + name: "List[str]" = ["Main", "Drogue"] cd_s: "List[float]" = [10, 1] lag: "List[float]" = [1.5, 1.5] sampling_rate: "List[int]" = [105, 105] noise: "List[Tuple[float, float, float]]" = [(0, 8.3, 0.5), (0, 8.3, 0.5)] - triggers: "List[str]" = ["lambda p, h, y: y[5] < 0 and h < 800", "lambda p, h, y: y[5] < 0"] + triggers: "List[str]" = [ + "lambda p, h, y: y[5] < 0 and h < 800", + "lambda p, h, y: y[5] < 0", + ] def __hash__(self): - return hash(( - tuple(self.name), - tuple(self.cd_s), - tuple(self.sampling_rate), - tuple(self.lag), - tuple(self.noise), - tuple(self.triggers), - )) + return hash( + ( + tuple(self.name), + tuple(self.cd_s), + tuple(self.sampling_rate), + tuple(self.lag), + tuple(self.noise), + tuple(self.triggers), + ) + ) def __getitem__(self, idx): if isinstance(idx, slice): diff --git a/lib/models/rocket.py b/lib/models/rocket.py index c31abda..09d0172 100644 --- a/lib/models/rocket.py +++ b/lib/models/rocket.py @@ -1,16 +1,18 @@ +from enum import Enum from typing import Optional, Tuple, List from pydantic import BaseModel, PrivateAttr -from enum import Enum from lib.models.motor import Motor from lib.models.aerosurfaces import Fins, NoseCone, Tail, RailButtons from lib.models.parachute import Parachute + class RocketOptions(str, Enum): calisto: str = "Calisto" custom: str = "Custom" + class Rocket(BaseModel, frozen=True): - #Required parameters + # Required parameters rail_buttons: RailButtons = RailButtons() motor: Motor = Motor() nose: NoseCone = NoseCone() @@ -22,23 +24,23 @@ class Rocket(BaseModel, frozen=True): radius: float = 0.0632 mass: float = 16.235 motor_position: float = -1.255 - power_off_drag: "List[Tuple[float, float]]" = [ - (0.01,0.333865758), - (0.02,0.394981721), - (0.03,0.407756063) + power_off_drag: "List[Tuple[float, float]]" = [ + (0.01, 0.333865758), + (0.02, 0.394981721), + (0.03, 0.407756063), ] - power_on_drag: "List[Tuple[float, float]]" = [ - (0.01,0.333865758), - (0.02,0.394981721), - (0.03,0.407756063) + power_on_drag: "List[Tuple[float, float]]" = [ + (0.01, 0.333865758), + (0.02, 0.394981721), + (0.03, 0.407756063), ] _rocket_option: RocketOptions = PrivateAttr() - #Optional parameters - #TBD: a list of possible tailToNose values must be provided in the api docs + # Optional parameters + # TBD: a list of possible tailToNose values must be provided in the api docs coordinate_system_orientation: Optional[str] = "tail_to_nose" - def __init__(self, rocket_option = RocketOptions.calisto, **kwargs): + def __init__(self, rocket_option=RocketOptions.calisto, **kwargs): super().__init__(**kwargs) self._rocket_option = rocket_option diff --git a/lib/repositories/environment.py b/lib/repositories/environment.py index ddc9dbc..8632b65 100644 --- a/lib/repositories/environment.py +++ b/lib/repositories/environment.py @@ -1,8 +1,9 @@ +from typing import Union from pymongo.results import InsertOneResult from pymongo.results import DeleteResult from lib.models.environment import Env from lib.repositories.repo import Repository -from typing import Union + class EnvRepository(Repository): """ @@ -41,9 +42,12 @@ async def create_env(self) -> "InsertOneResult": environment_to_dict = self.environment.dict() environment_to_dict["env_id"] = self.env_id return await self.collection.insert_one(environment_to_dict) - except: - raise Exception("Error creating environment") - return InsertOneResult( acknowledged=True, inserted_id=None ) + except Exception as e: + raise Exception(f"Error creating environment: {str(e)}") from e + finally: + self.__del__() + else: + return InsertOneResult(acknowledged=True, inserted_id=None) async def update_env(self) -> "Union[int, None]": """ @@ -57,29 +61,30 @@ async def update_env(self) -> "Union[int, None]": environment_to_dict["env_id"] = self.environment.__hash__() await self.collection.update_one( - { "env_id": self.env_id }, - { "$set": environment_to_dict } + {"env_id": self.env_id}, {"$set": environment_to_dict} ) self.env_id = environment_to_dict["env_id"] return self.env_id - except: - raise Exception("Error updating environment") + except Exception as e: + raise Exception(f"Error updating environment: {str(e)}") from e + finally: + self.__del__() async def get_env(self) -> "Union[Env, None]": """ Gets a environment from the database - + Returns: models.Env: Model environment object """ try: - environment = await self.collection.find_one({ "env_id": self.env_id }) + environment = await self.collection.find_one({"env_id": self.env_id}) if environment is not None: return Env.parse_obj(environment) return None - except: - raise Exception("Error getting environment") + except Exception as e: + raise Exception(f"Error getting environment: {str(e)}") from e async def delete_env(self) -> "DeleteResult": """ @@ -89,6 +94,8 @@ async def delete_env(self) -> "DeleteResult": DeleteResult: result of the delete operation """ try: - return await self.collection.delete_one({ "env_id": self.env_id }) - except: - raise Exception("Error deleting environment") + return await self.collection.delete_one({"env_id": self.env_id}) + except Exception as e: + raise Exception(f"Error deleting environment: {str(e)}") from e + finally: + self.__del__() diff --git a/lib/repositories/flight.py b/lib/repositories/flight.py index eea93de..6f3f47b 100644 --- a/lib/repositories/flight.py +++ b/lib/repositories/flight.py @@ -4,6 +4,7 @@ from lib.models.flight import Flight from lib.repositories.repo import Repository + class FlightRepository(Repository): """ Flight repository @@ -26,7 +27,9 @@ def __init__(self, flight: Flight = None, flight_id: str = None): def __del__(self): super().__del__() - async def create_flight(self, motor_kind: str = "Solid", rocket_option: str = "Calisto") -> "InsertOneResult": + async def create_flight( + self, motor_kind: str = "Solid", rocket_option: str = "Calisto" + ) -> "InsertOneResult": """ Creates a flight in the database @@ -43,11 +46,16 @@ async def create_flight(self, motor_kind: str = "Solid", rocket_option: str = "C flight_to_dict["rocket"]["rocket_option"] = rocket_option flight_to_dict["rocket"]["motor"]["motor_kind"] = motor_kind return await self.collection.insert_one(flight_to_dict) - except: - raise Exception("Error creating flight") - return InsertOneResult( acknowledged=True, inserted_id=None ) + except Exception as e: + raise Exception(f"Error creating flight: {str(e)}") from e + finally: + self.__del__() + else: + return InsertOneResult(acknowledged=True, inserted_id=None) - async def update_flight(self, motor_kind: str = "Solid", rocket_option: str = "Calisto") -> "Union[int, None]": + async def update_flight( + self, motor_kind: str = "Solid", rocket_option: str = "Calisto" + ) -> "Union[int, None]": """ Updates a flight in the database @@ -60,30 +68,31 @@ async def update_flight(self, motor_kind: str = "Solid", rocket_option: str = "C flight_to_dict["rocket"]["rocket_option"] = rocket_option flight_to_dict["rocket"]["motor"]["motor_kind"] = motor_kind - updated_flight = await self.collection.update_one( - { "flight_id": self.flight_id }, - { "$set": flight_to_dict } + await self.collection.update_one( + {"flight_id": self.flight_id}, {"$set": flight_to_dict} ) self.flight_id = flight_to_dict["flight_id"] - return self.flight_id - except: - raise Exception("Error updating flight") + return self.flight_id + except Exception as e: + raise Exception(f"Error updating flight: {str(e)}") from e + finally: + self.__del__() async def get_flight(self) -> "Union[Flight, None]": """ Gets a flight from the database - + Returns: models.Flight: Model flight object """ try: - flight = await self.collection.find_one({ "flight_id": self.flight_id }) + flight = await self.collection.find_one({"flight_id": self.flight_id}) if flight is not None: return Flight.parse_obj(flight) return None - except: - raise Exception("Error getting flight") + except Exception as e: + raise Exception(f"Error getting flight: {str(e)}") from e async def delete_flight(self) -> "DeleteResult": """ @@ -93,6 +102,8 @@ async def delete_flight(self) -> "DeleteResult": DeleteResult: result of the delete operation """ try: - return await self.collection.delete_one({ "flight_id": self.flight_id }) - except: - raise Exception("Error deleting flight") + return await self.collection.delete_one({"flight_id": self.flight_id}) + except Exception as e: + raise Exception(f"Error deleting flight: {str(e)}") from e + finally: + self.__del__() diff --git a/lib/repositories/motor.py b/lib/repositories/motor.py index 030bf3e..95883f1 100644 --- a/lib/repositories/motor.py +++ b/lib/repositories/motor.py @@ -4,6 +4,7 @@ from lib.models.motor import Motor from lib.repositories.repo import Repository + class MotorRepository(Repository): """ Motor repository @@ -42,9 +43,12 @@ async def create_motor(self, motor_kind: str = "solid") -> "InsertOneResult": motor_to_dict["motor_id"] = self.motor_id motor_to_dict["motor_kind"] = motor_kind return await self.collection.insert_one(motor_to_dict) - except: - raise Exception("Error creating motor") - return InsertOneResult( acknowledged=True, inserted_id=None ) + except Exception as e: + raise Exception(f"Error creating motor: {str(e)}") from e + finally: + self.__del__() + else: + return InsertOneResult(acknowledged=True, inserted_id=None) async def update_motor(self, motor_kind: str = "solid") -> "Union[int, None]": """ @@ -58,30 +62,31 @@ async def update_motor(self, motor_kind: str = "solid") -> "Union[int, None]": motor_to_dict["motor_id"] = self.motor.__hash__() motor_to_dict["motor_kind"] = motor_kind - updated_motor = await self.collection.update_one( - { "motor_id": self.motor_id }, - { "$set": motor_to_dict } + await self.collection.update_one( + {"motor_id": self.motor_id}, {"$set": motor_to_dict} ) self.motor_id = motor_to_dict["motor_id"] - return self.motor_id - except: - raise Exception("Error updating motor") + return self.motor_id + except Exception as e: + raise Exception(f"Error updating motor: {str(e)}") from e + finally: + self.__del__() async def get_motor(self) -> "Union[motor, None]": """ Gets a motor from the database - + Returns: models.motor: Model motor object """ try: - motor = await self.collection.find_one({ "motor_id": self.motor_id }) + motor = await self.collection.find_one({"motor_id": self.motor_id}) if motor is not None: return Motor.parse_obj(motor) return None - except: - raise Exception("Error getting motor") + except Exception as e: + raise Exception(f"Error getting motor: {str(e)}") from e async def delete_motor(self) -> "DeleteResult": """ @@ -91,6 +96,8 @@ async def delete_motor(self) -> "DeleteResult": DeleteResult: result of the delete operation """ try: - return await self.collection.delete_one({ "motor_id": self.motor_id }) - except: - raise Exception("Error deleting motor") + return await self.collection.delete_one({"motor_id": self.motor_id}) + except Exception as e: + raise Exception(f"Error deleting motor: {str(e)}") from e + finally: + self.__del__() diff --git a/lib/repositories/repo.py b/lib/repositories/repo.py index 26aec23..24401c4 100644 --- a/lib/repositories/repo.py +++ b/lib/repositories/repo.py @@ -1,12 +1,13 @@ -import asyncio from motor.motor_asyncio import AsyncIOMotorClient from pymongo.server_api import ServerApi from lib.secrets import secrets_instance + class Repository: """ Base class for all repositories (singleton) """ + _self = None def __new__(cls, *args, **kwargs): @@ -15,8 +16,12 @@ def __new__(cls, *args, **kwargs): return cls._self def __init__(self, collection: str): - self.connection_string = secrets_instance.get_secret("MONGODB_CONNECTION_STRING") - self.client = AsyncIOMotorClient(self.connection_string, server_api=ServerApi('1')) + self.connection_string = secrets_instance.get_secret( + "MONGODB_CONNECTION_STRING" + ) + self.client = AsyncIOMotorClient( + self.connection_string, server_api=ServerApi("1") + ) self.db = self.client.rocketpy self.collection = self.db[collection] diff --git a/lib/repositories/rocket.py b/lib/repositories/rocket.py index ab03807..2d64fce 100644 --- a/lib/repositories/rocket.py +++ b/lib/repositories/rocket.py @@ -4,6 +4,7 @@ from lib.models.rocket import Rocket from lib.repositories.repo import Repository + class RocketRepository(Repository): """ Rocket repository @@ -42,9 +43,12 @@ async def create_rocket(self, rocket_option: str = "Calisto") -> "InsertOneResul rocket_to_dict["rocket_id"] = self.rocket_id rocket_to_dict["rocket_option"] = rocket_option return await self.collection.insert_one(rocket_to_dict) - except: - raise Exception("Error creating rocket") - return InsertOneResult( acknowledged=True, inserted_id=None ) + except Exception as e: + raise Exception(f"Error creating rocket: {str(e)}") from e + finally: + self.__del__() + else: + return InsertOneResult(acknowledged=True, inserted_id=None) async def update_rocket(self, rocket_option: str = "Calisto") -> "Union[int, None]": """ @@ -58,30 +62,31 @@ async def update_rocket(self, rocket_option: str = "Calisto") -> "Union[int, Non rocket_to_dict["rocket_id"] = self.rocket.__hash__() rocket_to_dict["rocket_option"] = rocket_option - updated_rocket = await self.collection.update_one( - { "rocket_id": self.rocket_id }, - { "$set": rocket_to_dict } + await self.collection.update_one( + {"rocket_id": self.rocket_id}, {"$set": rocket_to_dict} ) self.rocket_id = rocket_to_dict["rocket_id"] - return self.rocket_id - except: - raise Exception("Error updating rocket") + return self.rocket_id + except Exception as e: + raise Exception(f"Error updating rocket: {str(e)}") from e + finally: + self.__del__() async def get_rocket(self) -> "Union[Rocket, None]": """ Gets a rocket from the database - + Returns: models.Rocket: Model rocket object """ try: - rocket = await self.collection.find_one({ "rocket_id": self.rocket_id }) + rocket = await self.collection.find_one({"rocket_id": self.rocket_id}) if rocket is not None: return Rocket.parse_obj(rocket) return None - except: - raise Exception("Error getting rocket") + except Exception as e: + raise Exception(f"Error getting rocket: {str(e)}") from e async def delete_rocket(self) -> "DeleteResult": """ @@ -91,6 +96,8 @@ async def delete_rocket(self) -> "DeleteResult": DeleteResult: result of the delete operation """ try: - return await self.collection.delete_one({ "rocket_id": self.rocket_id }) - except: - raise Exception("Error deleting rocket") + return await self.collection.delete_one({"rocket_id": self.rocket_id}) + except Exception as e: + raise Exception(f"Error deleting rocket: {str(e)}") from e + finally: + self.__del__() diff --git a/lib/routes/environment.py b/lib/routes/environment.py new file mode 100644 index 0000000..e2e8f0c --- /dev/null +++ b/lib/routes/environment.py @@ -0,0 +1,93 @@ +""" +Environment routes +""" +from fastapi import APIRouter + +from lib.views.environment import ( + EnvSummary, + EnvCreated, + EnvUpdated, + EnvDeleted, + EnvPickle, +) +from lib.models.environment import Env +from lib.controllers.environment import EnvController + +router = APIRouter( + prefix="/environments", + tags=["ENVIRONMENT"], + responses={ + 404: {"description": "Not found"}, + 422: {"description": "Unprocessable Entity"}, + 500: {"description": "Internal Server Error"}, + }, +) + + +@router.post("/") +async def create_env(env: Env) -> "EnvCreated": + """ + Creates a new environment + + ## Args + ``` Env object as a JSON ``` + """ + return await EnvController(env).create_env() + + +@router.get("/{env_id}") +async def read_env(env_id: int) -> "Env": + """ + Reads an environment + + ## Args + ``` env_id: Environment ID hash ``` + """ + return await EnvController.get_env(env_id) + + +@router.put("/{env_id}") +async def update_env(env_id: int, env: Env) -> "EnvUpdated": + """ + Updates an environment + + ## Args + ``` + env_id: Environment ID hash + env: Env object as JSON + ``` + """ + return await EnvController(env).update_env(env_id) + + +@router.delete("/{env_id}") +async def delete_env(env_id: int) -> "EnvDeleted": + """ + Deletes an environment + + ## Args + ``` env_id: Environment ID hash ``` + """ + return await EnvController.delete_env(env_id) + + +@router.get("/rocketpy/{env_id}") +async def read_rocketpy_env(env_id: int) -> "EnvPickle": + """ + Reads a rocketpy environment + + ## Args + ``` env_id: Environment ID hash ``` + """ + return await EnvController.get_rocketpy_env(env_id) + + +@router.get("/{env_id}/simulate") +async def simulate_env(env_id: int) -> "EnvSummary": + """ + Simulates an environment + + ## Args + ``` env_id: Env ID hash ``` + """ + return await EnvController.simulate(env_id) diff --git a/lib/routes/flight.py b/lib/routes/flight.py new file mode 100644 index 0000000..5e2962b --- /dev/null +++ b/lib/routes/flight.py @@ -0,0 +1,134 @@ +""" +Flight routes +""" +from fastapi import APIRouter + +from lib.views.flight import ( + FlightSummary, + FlightCreated, + FlightUpdated, + FlightDeleted, + FlightPickle, +) +from lib.models.environment import Env +from lib.models.flight import Flight +from lib.models.rocket import Rocket, RocketOptions +from lib.models.motor import MotorKinds +from lib.controllers.flight import FlightController + +router = APIRouter( + prefix="/flights", + tags=["FLIGHT"], + responses={ + 404: {"description": "Not found"}, + 422: {"description": "Unprocessable Entity"}, + 500: {"description": "Internal Server Error"}, + }, +) + + +@router.post("/") +async def create_flight( + flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "FlightCreated": + """ + Creates a new flight + + ## Args + ``` Flight object as JSON ``` + """ + return await FlightController(flight, rocket_option, motor_kind).create_flight() + + +@router.get("/{flight_id}") +async def read_flight(flight_id: int) -> "Flight": + """ + Reads a flight + + ## Args + ``` flight_id: Flight ID hash ``` + """ + return await FlightController.get_flight(flight_id) + + +@router.get("/rocketpy/{flight_id}") +async def read_rocketpy_flight(flight_id: int) -> "FlightPickle": + """ + Reads a rocketpy flight object + + ## Args + ``` flight_id: Flight ID hash. ``` + """ + return await FlightController.get_rocketpy_flight(flight_id) + + +@router.put("/{flight_id}/env") +async def update_flight_env(flight_id: int, env: Env) -> "FlightUpdated": + """ + Updates flight environment + + ## Args + ``` + flight_id: Flight ID hash + env: env object as JSON + ``` + """ + return await FlightController.update_env(flight_id, env) + + +@router.put("/{flight_id}/rocket") +async def update_flight_rocket( + flight_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "FlightUpdated": + """ + Updates flight rocket. + + ## Args + ``` + flight_id: Flight ID hash. + rocket: Rocket object as JSON + ``` + """ + return await FlightController.update_rocket( + flight_id, rocket, rocket_option, motor_kind + ) + + +@router.put("/{flight_id}") +async def update_flight( + flight_id: int, flight: Flight, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "FlightUpdated": + """ + Updates Flight object + + ## Args + ``` + flight_id: Flight ID hash. + flight: Flight object as JSON + ``` + """ + return await FlightController(flight, rocket_option, motor_kind).update_flight( + flight_id + ) + + +@router.delete("/{flight_id}") +async def delete_flight(flight_id: int) -> "FlightDeleted": + """ + Deletes a flight + + ## Args + ``` flight_id: Flight ID hash ``` + """ + return await FlightController.delete_flight(flight_id) + + +@router.get("/{flight_id}/simulate") +async def simulate_flight(flight_id: int) -> "FlightSummary": + """ + Simulates a flight + + ## Args + ``` flight_id: Flight ID hash ``` + """ + return await FlightController.simulate(flight_id) diff --git a/lib/routes/motor.py b/lib/routes/motor.py new file mode 100644 index 0000000..c29d90b --- /dev/null +++ b/lib/routes/motor.py @@ -0,0 +1,95 @@ +""" +Motor routes +""" +from fastapi import APIRouter + +from lib.views.motor import ( + MotorSummary, + MotorCreated, + MotorUpdated, + MotorDeleted, + MotorPickle, +) +from lib.models.motor import Motor, MotorKinds +from lib.controllers.motor import MotorController + +router = APIRouter( + prefix="/motors", + tags=["MOTOR"], + responses={ + 404: {"description": "Not found"}, + 422: {"description": "Unprocessable Entity"}, + 500: {"description": "Internal Server Error"}, + }, +) + + +@router.post("/") +async def create_motor(motor: Motor, motor_kind: MotorKinds) -> "MotorCreated": + """ + Creates a new motor + + ## Args + ``` Motor object as a JSON ``` + """ + return await MotorController(motor, motor_kind).create_motor() + + +@router.get("/{motor_id}") +async def read_motor(motor_id: int) -> "Motor": + """ + Reads a motor + + ## Args + ``` motor_id: Motor ID hash ``` + """ + return await MotorController.get_motor(motor_id) + + +@router.put("/{motor_id}") +async def update_motor( + motor_id: int, motor: Motor, motor_kind: MotorKinds +) -> "MotorUpdated": + """ + Updates a motor + + ## Args + ``` + motor_id: Motor ID hash + motor: Motor object as JSON + ``` + """ + return await MotorController(motor, motor_kind).update_motor(motor_id) + + +@router.delete("/{motor_id}") +async def delete_motor(motor_id: int) -> "MotorDeleted": + """ + Deletes a motor + + ## Args + ``` motor_id: Motor ID hash ``` + """ + return await MotorController.delete_motor(motor_id) + + +@router.get("/rocketpy/{motor_id}") +async def read_rocketpy_motor(motor_id: int) -> "MotorPickle": + """ + Reads a rocketpy motor + + ## Args + ``` motor_id: Motor ID hash ``` + """ + return await MotorController.get_rocketpy_motor(motor_id) + + +@router.get("/{motor_id}/simulate") +async def simulate_motor(motor_id: int) -> "MotorSummary": + """ + Simulates a motor + + ## Args + ``` motor_id: Motor ID hash ``` + """ + return await MotorController.simulate(motor_id) diff --git a/lib/routes/rocket.py b/lib/routes/rocket.py new file mode 100644 index 0000000..fded8cc --- /dev/null +++ b/lib/routes/rocket.py @@ -0,0 +1,100 @@ +""" +Rocket routes +""" +from fastapi import APIRouter + +from lib.views.rocket import ( + RocketSummary, + RocketCreated, + RocketUpdated, + RocketDeleted, + RocketPickle, +) +from lib.models.rocket import Rocket, RocketOptions +from lib.models.motor import MotorKinds +from lib.controllers.rocket import RocketController + +router = APIRouter( + prefix="/rockets", + tags=["ROCKET"], + responses={ + 404: {"description": "Not found"}, + 422: {"description": "Unprocessable Entity"}, + 500: {"description": "Internal Server Error"}, + }, +) + + +@router.post("/") +async def create_rocket( + rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "RocketCreated": + """ + Creates a new rocket + + ## Args + ``` Rocket object as a JSON ``` + """ + return await RocketController(rocket, rocket_option, motor_kind).create_rocket() + + +@router.get("/{rocket_id}") +async def read_rocket(rocket_id: int) -> Rocket: + """ + Reads a rocket + + ## Args + ``` rocket_id: Rocket ID hash ``` + """ + return await RocketController.get_rocket(rocket_id) + + +@router.put("/{rocket_id}") +async def update_rocket( + rocket_id: int, rocket: Rocket, rocket_option: RocketOptions, motor_kind: MotorKinds +) -> "RocketUpdated": + """ + Updates a rocket + + ## Args + ``` + rocket_id: Rocket ID hash + rocket: Rocket object as JSON + ``` + """ + return await RocketController(rocket, rocket_option, motor_kind).update_rocket( + rocket_id + ) + + +@router.delete("/{rocket_id}") +async def delete_rocket(rocket_id: int) -> "RocketDeleted": + """ + Deletes a rocket + + ## Args + ``` rocket_id: Rocket ID hash ``` + """ + return await RocketController.delete_rocket(rocket_id) + + +@router.get("/rocketpy/{rocket_id}") +async def read_rocketpy_rocket(rocket_id: int) -> "RocketPickle": + """ + Reads a rocketpy rocket + + ## Args + ``` rocket_id: Rocket ID hash ``` + """ + return await RocketController.get_rocketpy_rocket(rocket_id) + + +@router.get("/{rocket_id}/simulate") +async def simulate_rocket(rocket_id: int) -> "RocketSummary": + """ + Simulates a rocket + + ## Args + ``` rocket_id: Rocket ID hash ``` + """ + return await RocketController.simulate(rocket_id) diff --git a/lib/secrets.py b/lib/secrets.py index e719203..422a491 100644 --- a/lib/secrets.py +++ b/lib/secrets.py @@ -2,10 +2,12 @@ from dotenv import dotenv_values from pydantic import BaseModel + class Secrets(BaseModel): """ - Secrets class to load secrets from .env file + Secrets class to load secrets from .env file """ + secrets: dict = dotenv_values(".env") @staticmethod @@ -18,5 +20,6 @@ def get_secret(self, key): return self.get_os_secret(key) return dotenv_secret + # global instance secrets_instance = Secrets() diff --git a/lib/views/environment.py b/lib/views/environment.py index de1ace9..51b4216 100644 --- a/lib/views/environment.py +++ b/lib/views/environment.py @@ -1,9 +1,10 @@ from typing import List from pydantic import BaseModel + class EnvData(BaseModel): - #TBD: review grav type - #grav: "Any" + # TBD: review grav type + # grav: "Any" elevation: int model_type: str model_type_max_expected_height: int @@ -18,6 +19,7 @@ class EnvData(BaseModel): lat: float lon: float + class EnvPlots(BaseModel): grid: "List[float]" wind_speed: "List[float]" @@ -29,21 +31,26 @@ class EnvPlots(BaseModel): pressure: "List[float]" temperature: "List[float]" + class EnvSummary(BaseModel): env_data: EnvData env_plots: EnvPlots + class EnvCreated(BaseModel): - env_id: str + env_id: str message: str = "Environment successfully created" + class EnvUpdated(BaseModel): - new_env_id: str + new_env_id: str message: str = "Environment successfully updated" + class EnvDeleted(BaseModel): - deleted_env_id: str + deleted_env_id: str message: str = "Environment successfully deleted" + class EnvPickle(BaseModel): jsonpickle_rocketpy_env: str diff --git a/lib/views/flight.py b/lib/views/flight.py index d038bb5..3d64591 100644 --- a/lib/views/flight.py +++ b/lib/views/flight.py @@ -1,6 +1,7 @@ from typing import Optional, Any from pydantic import BaseModel + class InitialConditions(BaseModel): initial_position: str initial_velocity: str @@ -8,6 +9,7 @@ class InitialConditions(BaseModel): initial_angular_position: str initial_angular_velocity: str + class NumericalIntegrationSettings(BaseModel): max_time: str max_time_step: str @@ -20,15 +22,18 @@ class NumericalIntegrationSettings(BaseModel): function_evaluations_per_time_step: str avg_function_evaluations_per_time_step: str + class SurfaceWindConditions(BaseModel): frontal_surface_wind_speed: str lateral_surface_wind_speed: str + class LaunchRailConditions(BaseModel): rail_length: str flight_inclination: str flight_heading: str + class OutOfRailConditions(BaseModel): out_of_rail_time: str out_of_rail_velocity: str @@ -37,6 +42,7 @@ class OutOfRailConditions(BaseModel): out_of_rail_thrust_weight_ratio: str out_of_rail_reynolds_number: str + class BurnoutConditions(BaseModel): burnout_time: str burnout_rocket_velocity: str @@ -45,11 +51,13 @@ class BurnoutConditions(BaseModel): burnout_mach_number: str burnout_kinetic_energy: str + class ApogeeConditions(BaseModel): apogee_time: str apogee_altitude: str apogee_freestream_speed: str + class MaximumValues(BaseModel): maximum_speed: str maximum_mach_number: str @@ -64,15 +72,18 @@ class MaximumValues(BaseModel): maximum_lower_rail_button_normal_force: str maximum_lower_rail_button_shear_force: str + class ImpactConditions(BaseModel): x_impact_position: "Optional[str]" y_impact_position: "Optional[str]" time_of_impact: "Optional[str]" impact_velocity: "Optional[str]" + class EventsRegistered(BaseModel): events_trace: "Optional[Any]" + class FlightData(BaseModel): initial_conditions: InitialConditions numerical_integration_settings: NumericalIntegrationSettings @@ -85,24 +96,30 @@ class FlightData(BaseModel): impact_conditions: ImpactConditions events_registered: "Optional[EventsRegistered]" + class FlightPlots(BaseModel): pass + class FlightSummary(BaseModel): flight_data: FlightData - #flight_plots: FlightPlots + # flight_plots: FlightPlots + class FlightCreated(BaseModel): - flight_id: str + flight_id: str message: str = "Flight successfully created" + class FlightUpdated(BaseModel): - new_flight_id: str + new_flight_id: str message: str = "Flight successfully updated" + class FlightDeleted(BaseModel): - deleted_flight_id: str + deleted_flight_id: str message: str = "Flight successfully deleted" + class FlightPickle(BaseModel): jsonpickle_rocketpy_flight: str diff --git a/lib/views/motor.py b/lib/views/motor.py index 402477a..0be590e 100644 --- a/lib/views/motor.py +++ b/lib/views/motor.py @@ -1,6 +1,7 @@ from typing import List, Any from pydantic import BaseModel + class MotorData(BaseModel): total_burning_time: str total_propellant_mass: str @@ -9,6 +10,7 @@ class MotorData(BaseModel): maximum_thrust: str total_impulse: str + class MotorPlots(BaseModel): thrust: List[Any] total_mass: List[Any] @@ -20,21 +22,26 @@ class MotorPlots(BaseModel): i_13: List[Any] i_23: List[Any] + class MotorSummary(BaseModel): motor_data: MotorData - #motor_plots: MotorPlots + # motor_plots: MotorPlots + class MotorCreated(BaseModel): - motor_id: str + motor_id: str message: str = "Motor successfully created" + class MotorUpdated(BaseModel): - new_motor_id: str + new_motor_id: str message: str = "Motor successfully updated" + class MotorDeleted(BaseModel): - deleted_motor_id: str + deleted_motor_id: str message: str = "Motor successfully deleted" + class MotorPickle(BaseModel): jsonpickle_rocketpy_motor: str diff --git a/lib/views/rocket.py b/lib/views/rocket.py index f7d184f..07ee848 100644 --- a/lib/views/rocket.py +++ b/lib/views/rocket.py @@ -1,11 +1,13 @@ from typing import List, Any, Optional from pydantic import BaseModel + class InertiaDetails(BaseModel): rocket_mass_without_propellant: str rocket_mass_with_propellant: str rocket_inertia_with_motor_without_propellant: "List[str]" + class RocketGeometricalParameters(BaseModel): rocket_maximum_radius: str rocket_frontal_area: str @@ -13,6 +15,7 @@ class RocketGeometricalParameters(BaseModel): rocket_codm_center_of_propellant_mass: str rocket_codm_loaded_center_of_mass: str + class RocketAerodynamicsQuantities(BaseModel): aerodynamics_lift_coefficient_derivatives: "Any" aerodynamics_center_of_pressure: "Any" @@ -20,36 +23,44 @@ class RocketAerodynamicsQuantities(BaseModel): initial_static_margin: str final_static_margin: str + class ParachuteData(BaseModel): parachute_details: "Any" # parachute_ejection_signal_trigger: "Any" parachute_ejection_system_refresh_rate: "Optional[Any]" parachute_lag: "Any" + class RocketData(BaseModel): inertia_details: InertiaDetails rocket_geometrical_parameters: RocketGeometricalParameters rocket_aerodynamics_quantities: RocketAerodynamicsQuantities parachute_data: ParachuteData + class RocketPlots(BaseModel): pass + class RocketSummary(BaseModel): rocket_data: RocketData - #rocket_plots: RocketPlots + # rocket_plots: RocketPlots + class RocketCreated(BaseModel): rocket_id: str message: str = "Rocket successfully created" + class RocketUpdated(BaseModel): - new_rocket_id: str + new_rocket_id: str message: str = "Rocket successfully updated" + class RocketDeleted(BaseModel): - deleted_rocket_id: str + deleted_rocket_id: str message: str = "Rocket successfully deleted" + class RocketPickle(BaseModel): jsonpickle_rocketpy_rocket: str diff --git a/test/Infinity-API.postman_collection.json b/test/Infinity-API.postman_collection.json index 203e044..4ff23fc 100644 --- a/test/Infinity-API.postman_collection.json +++ b/test/Infinity-API.postman_collection.json @@ -2,7 +2,7 @@ "info": { "_postman_id": "00c970b2-c429-4ecd-a1f7-de23aa286d10", "name": "Infinity-API", - "description": "# About this collection\n\nThe API under this collection includes four artifacts **{Environment, Flight, Motor and Rocket}** with 6 endpoints each covering artifact **creation, reading, editing, deleting, simulating and retrieving artifact as jsonpickle string.**\n\n- POST `api/artifact/{{artifact_id}}` { message, artifact_id }\n \n- GET `api/artifact/{{artifact_id}}` { Artifact }\n \n- GET `api/rocketpy/artifact/{{artifact_id}}` { json_pickle_string_artifact }\n \n- GET `api/simulate/artifact/{{artifact_id}}` { ArtifactSimulationSummary }\n \n- PUT `api/artifact/{{artifact_id}}` { message, new_artifact_id }\n \n- DELETE `api/artifact/{{artifact_id}}` { deleted_artifact_id, message }\n \n\n**Flight artifact** have also additional routes that allows to update its own artifacts.\n\n- POST `api/flight/{{flight_id}}/artifact/` { message, flight_id }\n \n\n## **Disclaimer**\n\nCurrently the API only supports Solid motors and TrapezoidalFins, sorry for that, we are working to expand its capabilities soon.\n\n## **Using this collection**\n\n- Run this collection by clicking on \"Run\".\n \n\n\n\n## Additional resources\n\n[Scripting in Postman](https://learning.postman.com/docs/writing-scripts/intro-to-scripts/)\n\n[Test script examples](https://learning.postman.com/docs/writing-scripts/script-references/test-examples/)\n\n[Postman Sandbox API reference](https://learning.postman.com/docs/sending-requests/grpc/postman-sandbox-api/#writing-assertions)\n\n[Using the Collection Runner](https://learning.postman.com/docs/collections/running-collections/intro-to-collection-runs/)", + "description": "# About this collection\n\nThe API under this collection includes four artifacts **{Environment, Flight, Motor and Rocket}** with 6 endpoints each covering artifact **creation, reading, editing, deleting, simulating and retrieving artifact as jsonpickle string.**\n\n- POST `api/artifact/{{artifact_id}}` { message, artifact_id }\n- GET `api/artifact/{{artifact_id}}` { Artifact }\n- GET `api/rocketpy/artifact/{{artifact_id}}` { json_pickle_string_artifact }\n- GET `api/simulate/artifact/{{artifact_id}}` { ArtifactSimulationSummary }\n- PUT `api/artifact/{{artifact_id}}` { message, new_artifact_id }\n- DELETE `api/artifact/{{artifact_id}}` { deleted_artifact_id, message }\n \n\n**Flight artifact** have also additional routes that allows to update its own artifacts.\n\n- POST `api/flight/{{flight_id}}/artifact/` { message, flight_id }\n \n\n## **Using this collection**\n\n- Run this collection by clicking on \"Run\".\n \n\n\n\n## Additional resources\n\n[Scripting in Postman](https://learning.postman.com/docs/writing-scripts/intro-to-scripts/)\n\n[Test script examples](https://learning.postman.com/docs/writing-scripts/script-references/test-examples/)\n\n[Postman Sandbox API reference](https://learning.postman.com/docs/sending-requests/grpc/postman-sandbox-api/#writing-assertions)\n\n[Using the Collection Runner](https://learning.postman.com/docs/collections/running-collections/intro-to-collection-runs/)", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "29631298", "_collection_link": "https://rocketpy-team.postman.co/workspace/Team-Workspace~d228e0d7-1148-4935-8e58-7db52744ee04/collection/29631298-00c970b2-c429-4ecd-a1f7-de23aa286d10?action=share&source=collection_link&creator=29631298" @@ -398,849 +398,2647 @@ "name": "Flight", "item": [ { - "name": "Create Flight", - "event": [ + "name": "Hybrid", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "var flightRequest = JSON.parse(pm.request.body.raw);", - "", - "// reduce environment date for future assertion", - "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", - "", - "// save flight parameters", - "pm.environment.set('rail_length', flightRequest.rail_length) ", - "pm.environment.set('inclination', flightRequest.inclination)", - "pm.environment.set('heading', flightRequest.heading)", - "", - "// flight environment", - "pm.environment.set('flight_id', apiRspn.flight_id) ", - "pm.environment.set('latitude', flightRequest.environment.latitude)", - "pm.environment.set('longitude', flightRequest.environment.longitude)", - "pm.environment.set('elevation', flightRequest.environment.elevation) ", - "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", - "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", - "pm.environment.set('date', flightRequest.environment.date) ", - "", - "// flight rocket", - "pm.environment.set('radius', flightRequest.rocket.radius)", - "pm.environment.set('mass', flightRequest.rocket.mass)", - "pm.environment.set('inertia', flightRequest.rocket.inertia)", - "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", - "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", - "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", - "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", - "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", - "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", - "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", - "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", - "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", - "", - "// flight rocket motor", - "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", - "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", - "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", - "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", - "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", - "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", - "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", - "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", - "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", - "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", - "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", - "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", - "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", - "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", - "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", - "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", - "", - "// flight rocket nose", - "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", - "pm.environment.set('kind', flightRequest.rocket.nose.kind)", - "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", - "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", - "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", - "", - "// flight rocket fins", - "pm.environment.set('n', flightRequest.rocket.fins.n)", - "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", - "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", - "pm.environment.set('span', flightRequest.rocket.fins.span)", - "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", - "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", - "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", - "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", - "", - "// flight rocket tail", - "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", - "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", - "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", - "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", - "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", - "", - "// flight rocket parachute", - "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", - "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", - "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", - "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", - "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", - "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", - "", - "//TEST", - "bdd = \"Given a valid Flight POST request is made to the API\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully created\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight_id\", function () {", - " pm.expect(apiRspn.flight_id).to.exist; ", - " });" - ], - "type": "text/javascript" - } + "name": "Create Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('flight_id', apiRspn.flight_id) ", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight POST request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully created\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight_id\", function () {", + " pm.expect(apiRspn.flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1400,\n \"latitude\": 0,\n \"longitude\": 0\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/?rocket_option=Calisto&motor_kind=Hybrid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Hybrid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"rail_length\": 5.2,\n \"inclination\": 85,\n \"heading\": 0,\n \"environment\": {\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1405,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n },\n \"rocket\": {\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n }\n } \n}", - "options": { - "raw": { - "language": "json" + "name": "Read Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "var returned_date = apiRspn.environment.date;", + "var reduced_returned_date = returned_date.substring(0, returned_date.length - 7);", + "", + "//TEST", + "bdd = \"Given a valid Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid flight \", function () {", + " pm.expect(apiRspn.inclination).to.eql(pm.environment.get('inclination'), \"flight inclination not matching\");", + " pm.expect(apiRspn.heading).to.eql(pm.environment.get('heading'), \"flight heading not matching\");", + " pm.expect(apiRspn.rail_length).to.eql(pm.environment.get('rail_length'), \"flight rail_length not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight environment\", function () {", + " pm.expect(apiRspn.environment.longitude).to.eql(pm.environment.get('longitude'), \"environment longitude not matching\"); ", + " pm.expect(apiRspn.environment.elevation).to.eql(pm.environment.get('elevation'), \"environment elevation not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_type).to.eql(pm.environment.get('atmospheric_model_type'), \"environment atmospheric_model_type not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_file).to.eql(pm.environment.get('atmospheric_model_file'), \"environment atmospheric_model_file not matching\");", + " pm.expect(reduced_returned_date).to.eql(pm.environment.get('date'), \"date not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket\", function () { ", + " pm.expect(apiRspn.rocket.radius).to.eql(pm.environment.get('radius'), \"rocket radius not matching\");", + " pm.expect(apiRspn.rocket.mass).to.eql(pm.environment.get('mass'), \"rocket mass not matching\");", + " pm.expect(apiRspn.rocket.inertia).to.eql(pm.environment.get('inertia'), \"rocket inertia not matching\");", + " pm.expect(apiRspn.rocket.power_off_drag).to.eql(pm.environment.get('power_off_drag'), \"rocket power_off_drag not matching\");", + " pm.expect(apiRspn.rocket.power_on_drag).to.eql(pm.environment.get('power_on_drag'), \"rocket power_on_drag not matching\");", + " pm.expect(apiRspn.rocket.center_of_mass_without_motor).to.eql(pm.environment.get('center_of_mass_without_motor'), \"rocket center_of_mass_without_motor not matching\");", + " pm.expect(apiRspn.rocket.coordinate_system_orientation).to.eql(pm.environment.get('rocket_coordinate_system_orientation'), \"rocket coordinate_system_orientation not matching\");", + " pm.expect(apiRspn.rocket.motor_position).to.eql(pm.environment.get('motor_position'), \"rocket motor_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons).to.eql(pm.environment.get('rail_buttons'), \"rocket rail_buttons not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.upper_button_position).to.eql(pm.environment.get('upper_button_position'), \"rocket rail_buttons upper_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.lower_button_position).to.eql(pm.environment.get('lower_button_position'), \"rocket rail_buttons lower_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.angular_position).to.eql(pm.environment.get('angular_position'), \"rocket rail_buttons angular_position not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket motor\", function () {", + " pm.expect(apiRspn.rocket.motor.burn_time).to.eql(pm.environment.get('burn_time'), \"rocket motor burn_time not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_mass).to.eql(pm.environment.get('dry_mass'), \"rocket motor dry_mass not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_inertia).to.eql(pm.environment.get('dry_inertia'), \"rocket motor dry_inertia not matching\");", + " pm.expect(apiRspn.rocket.motor.center_of_dry_mass_position).to.eql(pm.environment.get('center_of_dry_mass_position'), \"rocket motor center_of_dry_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_number).to.eql(pm.environment.get('grain_number'), \"rocket motor grain_number not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_density).to.eql(pm.environment.get('grain_density'), \"rocket motor grain_density not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_outer_radius).to.eql(pm.environment.get('grain_outer_radius'), \"rocket motor grain_outer_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_inner_radius).to.eql(pm.environment.get('grain_initial_inner_radius'), \"rocket motor grain_initial_inner_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_height).to.eql(pm.environment.get('grain_initial_height'), \"rocket motor grain_initial_height not matching\");", + " pm.expect(apiRspn.rocket.motor.grains_center_of_mass_position).to.eql(pm.environment.get('grains_center_of_mass_position'), \"rocket motor grains_center_of_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.thrust_source).to.eql(pm.environment.get('thrust_source'), \"rocket motor thrust_source not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_separation).to.eql(pm.environment.get('grain_separation'), \"rocket motor grain_separation not matching\");", + " pm.expect(apiRspn.rocket.motor.nozzle_radius).to.eql(pm.environment.get('nozzle_radius'), \"rocket motor nozzle_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.throat_radius).to.eql(pm.environment.get('throat_radius'), \"rocket motor throat_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.interpolation_method).to.eql(pm.environment.get('interpolation_method'), \"rocket motor interpolation_method not matching\");", + " pm.expect(apiRspn.rocket.motor.coordinate_system_orientation).to.eql(pm.environment.get('motor_coordinate_system_orientation'), \"motor coordinate_system_orientation not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket nose\", function () {", + " pm.expect(apiRspn.rocket.nose.length).to.eql(pm.environment.get('nose_length'), \"rocket nose length not matching\");", + " pm.expect(apiRspn.rocket.nose.kind).to.eql(pm.environment.get('kind'), \"rocket nose kind not matching\");", + " pm.expect(apiRspn.rocket.nose.position).to.eql(pm.environment.get('nose_position'), \"rocket nose position not matching\");", + " pm.expect(apiRspn.rocket.nose.base_radius).to.eql(pm.environment.get('base_radius'), \"rocket nose base_radius not matching\");", + " pm.expect(apiRspn.rocket.nose.rocket_radius).to.eql(pm.environment.get('rocket_radius'), \"rocket nose rocket_radius not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket fins\", function () {", + " pm.expect(apiRspn.rocket.fins.n).to.eql(pm.environment.get('n'), \"rocket fins 'n' not matching\");", + " pm.expect(apiRspn.rocket.fins.root_chord).to.eql(pm.environment.get('root_chord'), \"rocket fins root_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.tip_chord).to.eql(pm.environment.get('tip_chord'), \"rocket fins tip_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.span).to.eql(pm.environment.get('span'), \"rocket fins span not matching\");", + " pm.expect(apiRspn.rocket.fins.position).to.eql(pm.environment.get('fin_position'), \"rocket fins position not matching\");", + " pm.expect(apiRspn.rocket.fins.cant_angle).to.eql(pm.environment.get('cant_angle'), \"rocket fins cant_angle not matching\");", + " pm.expect(apiRspn.rocket.fins.radius).to.eql(pm.environment.get('fin_radius'), \"rocket fins radius not matching\");", + " pm.expect(apiRspn.rocket.fins.airfoil).to.eql(pm.environment.get('airfoil'), \"rocket fins airfoil not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket tail\", function () {", + " pm.expect(apiRspn.rocket.tail.top_radius).to.eql(pm.environment.get('top_radius'), \"rocket tail top_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.bottom_radius).to.eql(pm.environment.get('bottom_radius'), \"rocket tail bottom_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.length).to.eql(pm.environment.get('tail_length'), \"rocket tail length not matching\"); ", + " pm.expect(apiRspn.rocket.tail.position).to.eql(pm.environment.get('tail_position'), \"rocket tail position not matching\"); ", + " pm.expect(apiRspn.rocket.tail.radius).to.eql(pm.environment.get('tail_radius'), \"rocket tail radius not matching\"); ", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket parachutes\", function () {", + " pm.expect(apiRspn.rocket.parachutes.name).to.eql(pm.environment.get('parachutes_names'), \"rocket parachutes names not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.cd_s).to.eql(pm.environment.get('parachutes_cds'), \"rocket parachutes cd_s not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.sampling_rate).to.eql(pm.environment.get('parachutes_sampling_rate'), \"rocket parachutes sampling_rate not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.lag).to.eql(pm.environment.get('parachutes_lags'), \"rocket parachutes lags not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.noise).to.eql(pm.environment.get('parachutes_noises'), \"rocket parachutes noises not matching\");", + " pm.expect(apiRspn.rocket.parachutes.triggers).to.eql(pm.environment.get('parachutes_triggers'), \"rocket parachutes triggers not matching\");", + " });" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "url": { - "raw": "{{endpoint}}/flights/", - "host": [ - "{{endpoint}}" + { + "name": "Read rocketpy Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "//TEST", + "bdd = \"Given a valid rocketpy Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.jsonpickle_rocketpy_flight).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "" - ] + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/rocketpy/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "rocketpy", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." - }, - "response": [] - }, - { - "name": "Read Flight", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "", - "var returned_date = apiRspn.environment.date;", - "var reduced_returned_date = returned_date.substring(0, returned_date.length - 7);", - "", - "//TEST", - "bdd = \"Given a valid Flight GET request is made\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid flight \", function () {", - " pm.expect(apiRspn.inclination).to.eql(pm.environment.get('inclination'), \"flight inclination not matching\");", - " pm.expect(apiRspn.heading).to.eql(pm.environment.get('heading'), \"flight heading not matching\");", - " pm.expect(apiRspn.rail_length).to.eql(pm.environment.get('rail_length'), \"flight rail_length not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight environment\", function () {", - " pm.expect(apiRspn.environment.longitude).to.eql(pm.environment.get('longitude'), \"environment longitude not matching\"); ", - " pm.expect(apiRspn.environment.elevation).to.eql(pm.environment.get('elevation'), \"environment elevation not matching\");", - " pm.expect(apiRspn.environment.atmospheric_model_type).to.eql(pm.environment.get('atmospheric_model_type'), \"environment atmospheric_model_type not matching\");", - " pm.expect(apiRspn.environment.atmospheric_model_file).to.eql(pm.environment.get('atmospheric_model_file'), \"environment atmospheric_model_file not matching\");", - " pm.expect(reduced_returned_date).to.eql(pm.environment.get('date'), \"date not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket\", function () { ", - " pm.expect(apiRspn.rocket.radius).to.eql(pm.environment.get('radius'), \"rocket radius not matching\");", - " pm.expect(apiRspn.rocket.mass).to.eql(pm.environment.get('mass'), \"rocket mass not matching\");", - " pm.expect(apiRspn.rocket.inertia).to.eql(pm.environment.get('inertia'), \"rocket inertia not matching\");", - " pm.expect(apiRspn.rocket.power_off_drag).to.eql(pm.environment.get('power_off_drag'), \"rocket power_off_drag not matching\");", - " pm.expect(apiRspn.rocket.power_on_drag).to.eql(pm.environment.get('power_on_drag'), \"rocket power_on_drag not matching\");", - " pm.expect(apiRspn.rocket.center_of_mass_without_motor).to.eql(pm.environment.get('center_of_mass_without_motor'), \"rocket center_of_mass_without_motor not matching\");", - " pm.expect(apiRspn.rocket.coordinate_system_orientation).to.eql(pm.environment.get('rocket_coordinate_system_orientation'), \"rocket coordinate_system_orientation not matching\");", - " pm.expect(apiRspn.rocket.motor_position).to.eql(pm.environment.get('motor_position'), \"rocket motor_position not matching\");", - " pm.expect(apiRspn.rocket.rail_buttons).to.eql(pm.environment.get('rail_buttons'), \"rocket rail_buttons not matching\");", - " pm.expect(apiRspn.rocket.rail_buttons.upper_button_position).to.eql(pm.environment.get('upper_button_position'), \"rocket rail_buttons upper_button_position not matching\");", - " pm.expect(apiRspn.rocket.rail_buttons.lower_button_position).to.eql(pm.environment.get('lower_button_position'), \"rocket rail_buttons lower_button_position not matching\");", - " pm.expect(apiRspn.rocket.rail_buttons.angular_position).to.eql(pm.environment.get('angular_position'), \"rocket rail_buttons angular_position not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket motor\", function () {", - " pm.expect(apiRspn.rocket.motor.burn_time).to.eql(pm.environment.get('burn_time'), \"rocket motor burn_time not matching\");", - " pm.expect(apiRspn.rocket.motor.dry_mass).to.eql(pm.environment.get('dry_mass'), \"rocket motor dry_mass not matching\");", - " pm.expect(apiRspn.rocket.motor.dry_inertia).to.eql(pm.environment.get('dry_inertia'), \"rocket motor dry_inertia not matching\");", - " pm.expect(apiRspn.rocket.motor.center_of_dry_mass_position).to.eql(pm.environment.get('center_of_dry_mass_position'), \"rocket motor center_of_dry_mass_position not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_number).to.eql(pm.environment.get('grain_number'), \"rocket motor grain_number not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_density).to.eql(pm.environment.get('grain_density'), \"rocket motor grain_density not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_outer_radius).to.eql(pm.environment.get('grain_outer_radius'), \"rocket motor grain_outer_radius not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_initial_inner_radius).to.eql(pm.environment.get('grain_initial_inner_radius'), \"rocket motor grain_initial_inner_radius not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_initial_height).to.eql(pm.environment.get('grain_initial_height'), \"rocket motor grain_initial_height not matching\");", - " pm.expect(apiRspn.rocket.motor.grains_center_of_mass_position).to.eql(pm.environment.get('grains_center_of_mass_position'), \"rocket motor grains_center_of_mass_position not matching\");", - " pm.expect(apiRspn.rocket.motor.thrust_source).to.eql(pm.environment.get('thrust_source'), \"rocket motor thrust_source not matching\");", - " pm.expect(apiRspn.rocket.motor.grain_separation).to.eql(pm.environment.get('grain_separation'), \"rocket motor grain_separation not matching\");", - " pm.expect(apiRspn.rocket.motor.nozzle_radius).to.eql(pm.environment.get('nozzle_radius'), \"rocket motor nozzle_radius not matching\");", - " pm.expect(apiRspn.rocket.motor.throat_radius).to.eql(pm.environment.get('throat_radius'), \"rocket motor throat_radius not matching\");", - " pm.expect(apiRspn.rocket.motor.interpolation_method).to.eql(pm.environment.get('interpolation_method'), \"rocket motor interpolation_method not matching\");", - " pm.expect(apiRspn.rocket.motor.coordinate_system_orientation).to.eql(pm.environment.get('motor_coordinate_system_orientation'), \"motor coordinate_system_orientation not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket nose\", function () {", - " pm.expect(apiRspn.rocket.nose.length).to.eql(pm.environment.get('nose_length'), \"rocket nose length not matching\");", - " pm.expect(apiRspn.rocket.nose.kind).to.eql(pm.environment.get('kind'), \"rocket nose kind not matching\");", - " pm.expect(apiRspn.rocket.nose.position).to.eql(pm.environment.get('nose_position'), \"rocket nose position not matching\");", - " pm.expect(apiRspn.rocket.nose.base_radius).to.eql(pm.environment.get('base_radius'), \"rocket nose base_radius not matching\");", - " pm.expect(apiRspn.rocket.nose.rocket_radius).to.eql(pm.environment.get('rocket_radius'), \"rocket nose rocket_radius not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket fins\", function () {", - " pm.expect(apiRspn.rocket.fins.n).to.eql(pm.environment.get('n'), \"rocket fins 'n' not matching\");", - " pm.expect(apiRspn.rocket.fins.root_chord).to.eql(pm.environment.get('root_chord'), \"rocket fins root_chord not matching\");", - " pm.expect(apiRspn.rocket.fins.tip_chord).to.eql(pm.environment.get('tip_chord'), \"rocket fins tip_chord not matching\");", - " pm.expect(apiRspn.rocket.fins.span).to.eql(pm.environment.get('span'), \"rocket fins span not matching\");", - " pm.expect(apiRspn.rocket.fins.position).to.eql(pm.environment.get('fin_position'), \"rocket fins position not matching\");", - " pm.expect(apiRspn.rocket.fins.cant_angle).to.eql(pm.environment.get('cant_angle'), \"rocket fins cant_angle not matching\");", - " pm.expect(apiRspn.rocket.fins.radius).to.eql(pm.environment.get('fin_radius'), \"rocket fins radius not matching\");", - " pm.expect(apiRspn.rocket.fins.airfoil).to.eql(pm.environment.get('airfoil'), \"rocket fins airfoil not matching\");", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket tail\", function () {", - " pm.expect(apiRspn.rocket.tail.top_radius).to.eql(pm.environment.get('top_radius'), \"rocket tail top_radius not matching\"); ", - " pm.expect(apiRspn.rocket.tail.bottom_radius).to.eql(pm.environment.get('bottom_radius'), \"rocket tail bottom_radius not matching\"); ", - " pm.expect(apiRspn.rocket.tail.length).to.eql(pm.environment.get('tail_length'), \"rocket tail length not matching\"); ", - " pm.expect(apiRspn.rocket.tail.position).to.eql(pm.environment.get('tail_position'), \"rocket tail position not matching\"); ", - " pm.expect(apiRspn.rocket.tail.radius).to.eql(pm.environment.get('tail_radius'), \"rocket tail radius not matching\"); ", - " });", - " pm.test(bdd + \" then response must contain a valid flight rocket parachutes\", function () {", - " pm.expect(apiRspn.rocket.parachutes.name).to.eql(pm.environment.get('parachutes_names'), \"rocket parachutes names not matching\"); ", - " pm.expect(apiRspn.rocket.parachutes.cd_s).to.eql(pm.environment.get('parachutes_cds'), \"rocket parachutes cd_s not matching\"); ", - " pm.expect(apiRspn.rocket.parachutes.sampling_rate).to.eql(pm.environment.get('parachutes_sampling_rate'), \"rocket parachutes sampling_rate not matching\"); ", - " pm.expect(apiRspn.rocket.parachutes.lag).to.eql(pm.environment.get('parachutes_lags'), \"rocket parachutes lags not matching\"); ", - " pm.expect(apiRspn.rocket.parachutes.noise).to.eql(pm.environment.get('parachutes_noises'), \"rocket parachutes noises not matching\");", - " pm.expect(apiRspn.rocket.parachutes.triggers).to.eql(pm.environment.get('parachutes_triggers'), \"rocket parachutes triggers not matching\");", - " });" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}", - "host": [ - "{{endpoint}}" + "name": "Simulate Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid rocketpy Flight simulate GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.flight_data).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.min_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.relative_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.absolute_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.time_overshoot).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.terminate_on_apogee).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.number_of_time_steps).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.avg_function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.rail_length).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_inclination).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_heading).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.frontal_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.lateral_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_time).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_static_margin).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_angle_of_attack).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_thrust_weight_ratio).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_time).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_rocket_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_freestream_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_kinetic_energy).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_time).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_freestream_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_dynamic_pressure).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.x_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.y_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.time_of_impact).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.impact_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Drogue).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Main).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Read rocketpy Flight", - "event": [ + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/simulate", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "simulate" + ] + } + }, + "response": [] + }, { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "", - "//TEST", - "bdd = \"Given a valid rocketpy Flight GET request is made\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.jsonpickle_rocketpy_flight).to.exist; ", - " });" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{endpoint}}/flights/rocketpy/{{flight_id}}", - "host": [ - "{{endpoint}}" + "name": "Update Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "rocketpy", - "{{flight_id}}" - ] - } - }, - "response": [] - }, - { - "name": "Simulate Flight", - "event": [ + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1300,\n \"latitude\": 2,\n \"longitude\": 1\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/?rocket_option=Calisto&motor_kind=Hybrid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Hybrid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "//TEST", - "bdd = \"Given a valid rocketpy Flight simulate GET request is made\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.flight_data).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_position).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_altitude).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_position).to.exist;", - " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time_step).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.min_time_step).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.relative_error_tolerance).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.absolute_error_tolerance).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.time_overshoot).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.terminate_on_apogee).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.number_of_time_steps).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.function_evaluations_per_time_step).to.exist;", - " pm.expect(apiRspn.flight_data.numerical_integration_settings.avg_function_evaluations_per_time_step).to.exist;", - " pm.expect(apiRspn.flight_data.launch_rail_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.launch_rail_conditions.rail_length).to.exist;", - " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_inclination).to.exist;", - " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_heading).to.exist;", - " pm.expect(apiRspn.flight_data.surface_wind_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.surface_wind_conditions.frontal_surface_wind_speed).to.exist;", - " pm.expect(apiRspn.flight_data.surface_wind_conditions.lateral_surface_wind_speed).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_time).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_static_margin).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_angle_of_attack).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_thrust_weight_ratio).to.exist;", - " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_reynolds_number).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_time).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_rocket_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_altitude).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_freestream_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_mach_number).to.exist;", - " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_kinetic_energy).to.exist;", - " pm.expect(apiRspn.flight_data.apogee_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_time).to.exist;", - " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_altitude).to.exist;", - " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_freestream_speed).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_speed).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_mach_number).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_reynolds_number).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_dynamic_pressure).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_during_motor_burn).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_after_motor_burn).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_during_motor_burn).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_after_motor_burn).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_normal_force).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_shear_force).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_normal_force).to.exist;", - " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_shear_force).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions.x_impact_position).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions.y_impact_position).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions.time_of_impact).to.exist;", - " pm.expect(apiRspn.flight_data.impact_conditions.impact_velocity).to.exist;", - " pm.expect(apiRspn.flight_data.events_registered).to.exist;", - " pm.expect(apiRspn.flight_data.events_registered.events_trace).to.exist;", - " pm.expect(apiRspn.flight_data.events_registered.events_trace.Drogue).to.exist;", - " pm.expect(apiRspn.flight_data.events_registered.events_trace.Main).to.exist; ", - " });" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}/simulate", - "host": [ - "{{endpoint}}" + "name": "Update Flight Environment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save environment parameters", + "pm.environment.set('env_id', apiRspn.new_env_id) ", + "pm.environment.set('latitude', flightRequest.latitude)", + "pm.environment.set('longitude', flightRequest.longitude)", + "pm.environment.set('elevation', flightRequest.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.date) ", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}", - "simulate" - ] + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1400,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/env", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "env" + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Update Flight Rocket", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save rocket parameters", + "pm.environment.set('rocket_id', apiRspn.new_rocket_id)", + "pm.environment.set('radius', flightRequest.radius)", + "pm.environment.set('mass', flightRequest.mass)", + "pm.environment.set('inertia', flightRequest.inertia)", + "pm.environment.set('power_off_drag', flightRequest.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.coordinate_system_orientation)", + "", + "// rocket motor", + "pm.environment.set('burn_time', flightRequest.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.motor.coordinate_system_orientation)", + "", + "// rocket nose", + "pm.environment.set('nose_length', flightRequest.nose.length)", + "pm.environment.set('kind', flightRequest.nose.kind)", + "pm.environment.set('nose_position', flightRequest.nose.position)", + "pm.environment.set('base_radius', flightRequest.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.nose.rocket_radius)", + "", + "// rocket fins", + "pm.environment.set('n', flightRequest.fins.n)", + "pm.environment.set('root_chord', flightRequest.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.fins.tip_chord)", + "pm.environment.set('span', flightRequest.fins.span)", + "pm.environment.set('fin_position', flightRequest.fins.position)", + "pm.environment.set('cant_angle', flightRequest.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.fins.radius)", + "pm.environment.set('airfoil', flightRequest.fins.airfoil)", + "", + "// rocket tail", + "pm.environment.set('top_radius', flightRequest.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.tail.length)", + "pm.environment.set('tail_position', flightRequest.tail.position)", + "pm.environment.set('tail_radius', flightRequest.tail.radius)", + "", + "// rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"motor\": {\n \"burn_time\": 6.8,\n \"center_of_dry_mass_position\": 0.512,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 10,\n \"liquid\": {\n \"density\": 10,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/rocket?rocket_option=Calisto&motor_kind=Hybrid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "rocket" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Hybrid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Delete Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid Flight DELETE request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully deleted\", \"message not matching\");", + " pm.expect(apiRspn.deleted_flight_id).to.eql(pm.environment.get('flight_id'), \"flight_id not matching\"); ", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "Update Flight", - "event": [ + "name": "Liquid", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "var flightRequest = JSON.parse(pm.request.body.raw);", - "", - "// reduce environment date for future assertion", - "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", - "", - "// save flight parameters", - "pm.environment.set('flight_id', apiRspn.new_flight_id) ", - "pm.environment.set('rail_length', flightRequest.rail_length) ", - "pm.environment.set('inclination', flightRequest.inclination)", - "pm.environment.set('heading', flightRequest.heading)", - "", - "// flight environment", - "pm.environment.set('latitude', flightRequest.environment.latitude)", - "pm.environment.set('longitude', flightRequest.environment.longitude)", - "pm.environment.set('elevation', flightRequest.environment.elevation) ", - "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", - "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", - "pm.environment.set('date', flightRequest.environment.date) ", - "", - "// flight rocket", - "pm.environment.set('radius', flightRequest.rocket.radius)", - "pm.environment.set('mass', flightRequest.rocket.mass)", - "pm.environment.set('inertia', flightRequest.rocket.inertia)", - "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", - "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", - "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", - "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", - "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", - "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", - "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", - "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", - "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", - "", - "// flight rocket motor", - "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", - "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", - "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", - "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", - "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", - "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", - "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", - "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", - "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", - "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", - "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", - "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", - "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", - "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", - "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", - "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", - "", - "// flight rocket nose", - "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", - "pm.environment.set('kind', flightRequest.rocket.nose.kind)", - "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", - "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", - "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", - "", - "// flight rocket fins", - "pm.environment.set('n', flightRequest.rocket.fins.n)", - "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", - "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", - "pm.environment.set('span', flightRequest.rocket.fins.span)", - "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", - "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", - "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", - "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", - "", - "// flight rocket tail", - "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", - "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", - "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", - "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", - "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", - "", - "// flight rocket parachute", - "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", - "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", - "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", - "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", - "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", - "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", - "", - "//TEST", - "bdd = \"Given a valid Flight PUT request is made to the API\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", - " });", - " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", - " pm.expect(apiRspn.new_flight_id).to.exist; ", - " });" - ], - "type": "text/javascript" - } + "name": "Create Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('flight_id', apiRspn.flight_id) ", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight POST request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully created\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight_id\", function () {", + " pm.expect(apiRspn.flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1400,\n \"latitude\": 0,\n \"longitude\": 0\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/?rocket_option=Calisto&motor_kind=Liquid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Liquid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"rail_length\": 5.2,\n \"inclination\": 85,\n \"heading\": 0,\n \"environment\": {\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1405,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n },\n \"rocket\": {\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n }\n } \n}", - "options": { - "raw": { - "language": "json" + "name": "Read Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "var returned_date = apiRspn.environment.date;", + "var reduced_returned_date = returned_date.substring(0, returned_date.length - 7);", + "", + "//TEST", + "bdd = \"Given a valid Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid flight \", function () {", + " pm.expect(apiRspn.inclination).to.eql(pm.environment.get('inclination'), \"flight inclination not matching\");", + " pm.expect(apiRspn.heading).to.eql(pm.environment.get('heading'), \"flight heading not matching\");", + " pm.expect(apiRspn.rail_length).to.eql(pm.environment.get('rail_length'), \"flight rail_length not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight environment\", function () {", + " pm.expect(apiRspn.environment.longitude).to.eql(pm.environment.get('longitude'), \"environment longitude not matching\"); ", + " pm.expect(apiRspn.environment.elevation).to.eql(pm.environment.get('elevation'), \"environment elevation not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_type).to.eql(pm.environment.get('atmospheric_model_type'), \"environment atmospheric_model_type not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_file).to.eql(pm.environment.get('atmospheric_model_file'), \"environment atmospheric_model_file not matching\");", + " pm.expect(reduced_returned_date).to.eql(pm.environment.get('date'), \"date not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket\", function () { ", + " pm.expect(apiRspn.rocket.radius).to.eql(pm.environment.get('radius'), \"rocket radius not matching\");", + " pm.expect(apiRspn.rocket.mass).to.eql(pm.environment.get('mass'), \"rocket mass not matching\");", + " pm.expect(apiRspn.rocket.inertia).to.eql(pm.environment.get('inertia'), \"rocket inertia not matching\");", + " pm.expect(apiRspn.rocket.power_off_drag).to.eql(pm.environment.get('power_off_drag'), \"rocket power_off_drag not matching\");", + " pm.expect(apiRspn.rocket.power_on_drag).to.eql(pm.environment.get('power_on_drag'), \"rocket power_on_drag not matching\");", + " pm.expect(apiRspn.rocket.center_of_mass_without_motor).to.eql(pm.environment.get('center_of_mass_without_motor'), \"rocket center_of_mass_without_motor not matching\");", + " pm.expect(apiRspn.rocket.coordinate_system_orientation).to.eql(pm.environment.get('rocket_coordinate_system_orientation'), \"rocket coordinate_system_orientation not matching\");", + " pm.expect(apiRspn.rocket.motor_position).to.eql(pm.environment.get('motor_position'), \"rocket motor_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons).to.eql(pm.environment.get('rail_buttons'), \"rocket rail_buttons not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.upper_button_position).to.eql(pm.environment.get('upper_button_position'), \"rocket rail_buttons upper_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.lower_button_position).to.eql(pm.environment.get('lower_button_position'), \"rocket rail_buttons lower_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.angular_position).to.eql(pm.environment.get('angular_position'), \"rocket rail_buttons angular_position not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket motor\", function () {", + " pm.expect(apiRspn.rocket.motor.burn_time).to.eql(pm.environment.get('burn_time'), \"rocket motor burn_time not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_mass).to.eql(pm.environment.get('dry_mass'), \"rocket motor dry_mass not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_inertia).to.eql(pm.environment.get('dry_inertia'), \"rocket motor dry_inertia not matching\");", + " pm.expect(apiRspn.rocket.motor.center_of_dry_mass_position).to.eql(pm.environment.get('center_of_dry_mass_position'), \"rocket motor center_of_dry_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_number).to.eql(pm.environment.get('grain_number'), \"rocket motor grain_number not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_density).to.eql(pm.environment.get('grain_density'), \"rocket motor grain_density not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_outer_radius).to.eql(pm.environment.get('grain_outer_radius'), \"rocket motor grain_outer_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_inner_radius).to.eql(pm.environment.get('grain_initial_inner_radius'), \"rocket motor grain_initial_inner_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_height).to.eql(pm.environment.get('grain_initial_height'), \"rocket motor grain_initial_height not matching\");", + " pm.expect(apiRspn.rocket.motor.grains_center_of_mass_position).to.eql(pm.environment.get('grains_center_of_mass_position'), \"rocket motor grains_center_of_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.thrust_source).to.eql(pm.environment.get('thrust_source'), \"rocket motor thrust_source not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_separation).to.eql(pm.environment.get('grain_separation'), \"rocket motor grain_separation not matching\");", + " pm.expect(apiRspn.rocket.motor.nozzle_radius).to.eql(pm.environment.get('nozzle_radius'), \"rocket motor nozzle_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.throat_radius).to.eql(pm.environment.get('throat_radius'), \"rocket motor throat_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.interpolation_method).to.eql(pm.environment.get('interpolation_method'), \"rocket motor interpolation_method not matching\");", + " pm.expect(apiRspn.rocket.motor.coordinate_system_orientation).to.eql(pm.environment.get('motor_coordinate_system_orientation'), \"motor coordinate_system_orientation not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket nose\", function () {", + " pm.expect(apiRspn.rocket.nose.length).to.eql(pm.environment.get('nose_length'), \"rocket nose length not matching\");", + " pm.expect(apiRspn.rocket.nose.kind).to.eql(pm.environment.get('kind'), \"rocket nose kind not matching\");", + " pm.expect(apiRspn.rocket.nose.position).to.eql(pm.environment.get('nose_position'), \"rocket nose position not matching\");", + " pm.expect(apiRspn.rocket.nose.base_radius).to.eql(pm.environment.get('base_radius'), \"rocket nose base_radius not matching\");", + " pm.expect(apiRspn.rocket.nose.rocket_radius).to.eql(pm.environment.get('rocket_radius'), \"rocket nose rocket_radius not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket fins\", function () {", + " pm.expect(apiRspn.rocket.fins.n).to.eql(pm.environment.get('n'), \"rocket fins 'n' not matching\");", + " pm.expect(apiRspn.rocket.fins.root_chord).to.eql(pm.environment.get('root_chord'), \"rocket fins root_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.tip_chord).to.eql(pm.environment.get('tip_chord'), \"rocket fins tip_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.span).to.eql(pm.environment.get('span'), \"rocket fins span not matching\");", + " pm.expect(apiRspn.rocket.fins.position).to.eql(pm.environment.get('fin_position'), \"rocket fins position not matching\");", + " pm.expect(apiRspn.rocket.fins.cant_angle).to.eql(pm.environment.get('cant_angle'), \"rocket fins cant_angle not matching\");", + " pm.expect(apiRspn.rocket.fins.radius).to.eql(pm.environment.get('fin_radius'), \"rocket fins radius not matching\");", + " pm.expect(apiRspn.rocket.fins.airfoil).to.eql(pm.environment.get('airfoil'), \"rocket fins airfoil not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket tail\", function () {", + " pm.expect(apiRspn.rocket.tail.top_radius).to.eql(pm.environment.get('top_radius'), \"rocket tail top_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.bottom_radius).to.eql(pm.environment.get('bottom_radius'), \"rocket tail bottom_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.length).to.eql(pm.environment.get('tail_length'), \"rocket tail length not matching\"); ", + " pm.expect(apiRspn.rocket.tail.position).to.eql(pm.environment.get('tail_position'), \"rocket tail position not matching\"); ", + " pm.expect(apiRspn.rocket.tail.radius).to.eql(pm.environment.get('tail_radius'), \"rocket tail radius not matching\"); ", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket parachutes\", function () {", + " pm.expect(apiRspn.rocket.parachutes.name).to.eql(pm.environment.get('parachutes_names'), \"rocket parachutes names not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.cd_s).to.eql(pm.environment.get('parachutes_cds'), \"rocket parachutes cd_s not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.sampling_rate).to.eql(pm.environment.get('parachutes_sampling_rate'), \"rocket parachutes sampling_rate not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.lag).to.eql(pm.environment.get('parachutes_lags'), \"rocket parachutes lags not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.noise).to.eql(pm.environment.get('parachutes_noises'), \"rocket parachutes noises not matching\");", + " pm.expect(apiRspn.rocket.parachutes.triggers).to.eql(pm.environment.get('parachutes_triggers'), \"rocket parachutes triggers not matching\");", + " });" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}", - "host": [ - "{{endpoint}}" + { + "name": "Read rocketpy Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "//TEST", + "bdd = \"Given a valid rocketpy Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.jsonpickle_rocketpy_flight).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}" - ] + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/rocketpy/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "rocketpy", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." - }, - "response": [] - }, - { - "name": "Update Flight Environment", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "var flightRequest = JSON.parse(pm.request.body.raw);", - "", - "// save new flight id", - "pm.environment.set('flight_id', apiRspn.new_flight_id) ", - "", - "// save environment parameters", - "pm.environment.set('env_id', apiRspn.new_env_id) ", - "pm.environment.set('latitude', flightRequest.latitude)", - "pm.environment.set('longitude', flightRequest.longitude)", - "pm.environment.set('elevation', flightRequest.elevation) ", - "pm.environment.set('atmospheric_model_type', flightRequest.atmospheric_model_type) ", - "pm.environment.set('atmospheric_model_file', flightRequest.atmospheric_model_file) ", - "pm.environment.set('date', flightRequest.date) ", - "", - "//TEST", - "bdd = \"Given a valid Flight PUT request is made to the API\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", - " });", - " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", - " pm.expect(apiRspn.new_flight_id).to.exist; ", - " });" - ], - "type": "text/javascript" - } + "name": "Simulate Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid rocketpy Flight simulate GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.flight_data).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.min_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.relative_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.absolute_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.time_overshoot).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.terminate_on_apogee).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.number_of_time_steps).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.avg_function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.rail_length).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_inclination).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_heading).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.frontal_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.lateral_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_time).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_static_margin).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_angle_of_attack).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_thrust_weight_ratio).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_time).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_rocket_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_freestream_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_kinetic_energy).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_time).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_freestream_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_dynamic_pressure).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.x_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.y_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.time_of_impact).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.impact_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Drogue).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Main).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/simulate", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "simulate" + ] + } + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1400,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n}", - "options": { - "raw": { - "language": "json" + "name": "Update Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1300,\n \"latitude\": 2,\n \"longitude\": 1\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/?rocket_option=Calisto&motor_kind=Liquid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Liquid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}/env", - "host": [ - "{{endpoint}}" + { + "name": "Update Flight Environment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save environment parameters", + "pm.environment.set('env_id', apiRspn.new_env_id) ", + "pm.environment.set('latitude', flightRequest.latitude)", + "pm.environment.set('longitude', flightRequest.longitude)", + "pm.environment.set('elevation', flightRequest.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.date) ", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}", - "env" - ] + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1400,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/env", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "env" + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, - "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." - }, - "response": [] + { + "name": "Update Flight Rocket", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save rocket parameters", + "pm.environment.set('rocket_id', apiRspn.new_rocket_id)", + "pm.environment.set('radius', flightRequest.radius)", + "pm.environment.set('mass', flightRequest.mass)", + "pm.environment.set('inertia', flightRequest.inertia)", + "pm.environment.set('power_off_drag', flightRequest.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.coordinate_system_orientation)", + "", + "// rocket motor", + "pm.environment.set('burn_time', flightRequest.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.motor.coordinate_system_orientation)", + "", + "// rocket nose", + "pm.environment.set('nose_length', flightRequest.nose.length)", + "pm.environment.set('kind', flightRequest.nose.kind)", + "pm.environment.set('nose_position', flightRequest.nose.position)", + "pm.environment.set('base_radius', flightRequest.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.nose.rocket_radius)", + "", + "// rocket fins", + "pm.environment.set('n', flightRequest.fins.n)", + "pm.environment.set('root_chord', flightRequest.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.fins.tip_chord)", + "pm.environment.set('span', flightRequest.fins.span)", + "pm.environment.set('fin_position', flightRequest.fins.position)", + "pm.environment.set('cant_angle', flightRequest.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.fins.radius)", + "pm.environment.set('airfoil', flightRequest.fins.airfoil)", + "", + "// rocket tail", + "pm.environment.set('top_radius', flightRequest.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.tail.length)", + "pm.environment.set('tail_position', flightRequest.tail.position)", + "pm.environment.set('tail_radius', flightRequest.tail.radius)", + "", + "// rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"motor\": {\n \"burn_time\": 6.8,\n \"center_of_dry_mass_position\": 0.512,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 10,\n \"liquid\": {\n \"density\": 10,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/rocket?rocket_option=Calisto&motor_kind=Liquid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "rocket" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Liquid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Delete Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid Flight DELETE request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully deleted\", \"message not matching\");", + " pm.expect(apiRspn.deleted_flight_id).to.eql(pm.environment.get('flight_id'), \"flight_id not matching\"); ", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] + } + ] }, { - "name": "Update Flight Rocket", - "event": [ + "name": "Solid", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "var flightRequest = JSON.parse(pm.request.body.raw);", - "", - "// save new flight id", - "pm.environment.set('flight_id', apiRspn.new_flight_id) ", - "", - "// save rocket parameters", - "pm.environment.set('rocket_id', apiRspn.new_rocket_id)", - "pm.environment.set('radius', flightRequest.radius)", - "pm.environment.set('mass', flightRequest.mass)", - "pm.environment.set('inertia', flightRequest.inertia)", - "pm.environment.set('power_off_drag', flightRequest.power_off_drag)", - "pm.environment.set('power_on_drag', flightRequest.power_on_drag)", - "pm.environment.set('center_of_mass_without_motor', flightRequest.center_of_mass_without_motor)", - "pm.environment.set('motor_position', flightRequest.motor_position)", - "pm.environment.set('rail_buttons', flightRequest.rail_buttons)", - "pm.environment.set('upper_button_position', flightRequest.rail_buttons.upper_button_position)", - "pm.environment.set('lower_button_position', flightRequest.rail_buttons.lower_button_position)", - "pm.environment.set('angular_position', flightRequest.rail_buttons.angular_position)", - "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.coordinate_system_orientation)", - "", - "// rocket motor", - "pm.environment.set('burn_time', flightRequest.motor.burn_time)", - "pm.environment.set('dry_mass', flightRequest.motor.dry_mass)", - "pm.environment.set('dry_inertia', flightRequest.motor.dry_inertia)", - "pm.environment.set('center_of_dry_mass_position', flightRequest.motor.center_of_dry_mass_position)", - "pm.environment.set('grain_number', flightRequest.motor.grain_number)", - "pm.environment.set('grain_density', flightRequest.motor.grain_density)", - "pm.environment.set('grain_outer_radius', flightRequest.motor.grain_outer_radius)", - "pm.environment.set('grain_initial_inner_radius', flightRequest.motor.grain_initial_inner_radius)", - "pm.environment.set('grain_initial_height', flightRequest.motor.grain_initial_height)", - "pm.environment.set('grains_center_of_mass_position', flightRequest.motor.grains_center_of_mass_position)", - "pm.environment.set('grain_separation', flightRequest.motor.grain_separation)", - "pm.environment.set('thrust_source', flightRequest.motor.thrust_source)", - "pm.environment.set('nozzle_radius', flightRequest.motor.nozzle_radius)", - "pm.environment.set('throat_radius', flightRequest.motor.throat_radius)", - "pm.environment.set('interpolation_method', flightRequest.motor.interpolation_method)", - "pm.environment.set('motor_coordinate_system_orientation', flightRequest.motor.coordinate_system_orientation)", - "", - "// rocket nose", - "pm.environment.set('nose_length', flightRequest.nose.length)", - "pm.environment.set('kind', flightRequest.nose.kind)", - "pm.environment.set('nose_position', flightRequest.nose.position)", - "pm.environment.set('base_radius', flightRequest.nose.base_radius)", - "pm.environment.set('rocket_radius', flightRequest.nose.rocket_radius)", - "", - "// rocket fins", - "pm.environment.set('n', flightRequest.fins.n)", - "pm.environment.set('root_chord', flightRequest.fins.root_chord)", - "pm.environment.set('tip_chord', flightRequest.fins.tip_chord)", - "pm.environment.set('span', flightRequest.fins.span)", - "pm.environment.set('fin_position', flightRequest.fins.position)", - "pm.environment.set('cant_angle', flightRequest.fins.cant_angle)", - "pm.environment.set('fin_radius', flightRequest.fins.radius)", - "pm.environment.set('airfoil', flightRequest.fins.airfoil)", - "", - "// rocket tail", - "pm.environment.set('top_radius', flightRequest.tail.top_radius)", - "pm.environment.set('bottom_radius', flightRequest.tail.bottom_radius)", - "pm.environment.set('tail_length', flightRequest.tail.length)", - "pm.environment.set('tail_position', flightRequest.tail.position)", - "pm.environment.set('tail_radius', flightRequest.tail.radius)", - "", - "// rocket parachute", - "pm.environment.set('parachutes_names', flightRequest.parachutes.name)", - "pm.environment.set('parachutes_cds', flightRequest.parachutes.cd_s)", - "pm.environment.set('parachutes_sampling_rate', flightRequest.parachutes.sampling_rate)", - "pm.environment.set('parachutes_lags', flightRequest.parachutes.lag)", - "pm.environment.set('parachutes_noises', flightRequest.parachutes.noise)", - "pm.environment.set('parachutes_triggers', flightRequest.parachutes.triggers)", - "", - "//TEST", - "bdd = \"Given a valid Flight PUT request is made to the API\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", - " });", - " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", - " pm.expect(apiRspn.new_flight_id).to.exist; ", - " });" - ], - "type": "text/javascript" - } + "name": "Create Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('flight_id', apiRspn.flight_id) ", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight POST request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully created\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight_id\", function () {", + " pm.expect(apiRspn.flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1400,\n \"latitude\": 0,\n \"longitude\": 0\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/?rocket_option=Calisto&motor_kind=Solid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Solid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] }, { - "listen": "prerequest", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PUT", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n } \n}", - "options": { - "raw": { - "language": "json" + "name": "Read Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "var returned_date = apiRspn.environment.date;", + "var reduced_returned_date = returned_date.substring(0, returned_date.length - 7);", + "", + "//TEST", + "bdd = \"Given a valid Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid flight \", function () {", + " pm.expect(apiRspn.inclination).to.eql(pm.environment.get('inclination'), \"flight inclination not matching\");", + " pm.expect(apiRspn.heading).to.eql(pm.environment.get('heading'), \"flight heading not matching\");", + " pm.expect(apiRspn.rail_length).to.eql(pm.environment.get('rail_length'), \"flight rail_length not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight environment\", function () {", + " pm.expect(apiRspn.environment.longitude).to.eql(pm.environment.get('longitude'), \"environment longitude not matching\"); ", + " pm.expect(apiRspn.environment.elevation).to.eql(pm.environment.get('elevation'), \"environment elevation not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_type).to.eql(pm.environment.get('atmospheric_model_type'), \"environment atmospheric_model_type not matching\");", + " pm.expect(apiRspn.environment.atmospheric_model_file).to.eql(pm.environment.get('atmospheric_model_file'), \"environment atmospheric_model_file not matching\");", + " pm.expect(reduced_returned_date).to.eql(pm.environment.get('date'), \"date not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket\", function () { ", + " pm.expect(apiRspn.rocket.radius).to.eql(pm.environment.get('radius'), \"rocket radius not matching\");", + " pm.expect(apiRspn.rocket.mass).to.eql(pm.environment.get('mass'), \"rocket mass not matching\");", + " pm.expect(apiRspn.rocket.inertia).to.eql(pm.environment.get('inertia'), \"rocket inertia not matching\");", + " pm.expect(apiRspn.rocket.power_off_drag).to.eql(pm.environment.get('power_off_drag'), \"rocket power_off_drag not matching\");", + " pm.expect(apiRspn.rocket.power_on_drag).to.eql(pm.environment.get('power_on_drag'), \"rocket power_on_drag not matching\");", + " pm.expect(apiRspn.rocket.center_of_mass_without_motor).to.eql(pm.environment.get('center_of_mass_without_motor'), \"rocket center_of_mass_without_motor not matching\");", + " pm.expect(apiRspn.rocket.coordinate_system_orientation).to.eql(pm.environment.get('rocket_coordinate_system_orientation'), \"rocket coordinate_system_orientation not matching\");", + " pm.expect(apiRspn.rocket.motor_position).to.eql(pm.environment.get('motor_position'), \"rocket motor_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons).to.eql(pm.environment.get('rail_buttons'), \"rocket rail_buttons not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.upper_button_position).to.eql(pm.environment.get('upper_button_position'), \"rocket rail_buttons upper_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.lower_button_position).to.eql(pm.environment.get('lower_button_position'), \"rocket rail_buttons lower_button_position not matching\");", + " pm.expect(apiRspn.rocket.rail_buttons.angular_position).to.eql(pm.environment.get('angular_position'), \"rocket rail_buttons angular_position not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket motor\", function () {", + " pm.expect(apiRspn.rocket.motor.burn_time).to.eql(pm.environment.get('burn_time'), \"rocket motor burn_time not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_mass).to.eql(pm.environment.get('dry_mass'), \"rocket motor dry_mass not matching\");", + " pm.expect(apiRspn.rocket.motor.dry_inertia).to.eql(pm.environment.get('dry_inertia'), \"rocket motor dry_inertia not matching\");", + " pm.expect(apiRspn.rocket.motor.center_of_dry_mass_position).to.eql(pm.environment.get('center_of_dry_mass_position'), \"rocket motor center_of_dry_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_number).to.eql(pm.environment.get('grain_number'), \"rocket motor grain_number not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_density).to.eql(pm.environment.get('grain_density'), \"rocket motor grain_density not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_outer_radius).to.eql(pm.environment.get('grain_outer_radius'), \"rocket motor grain_outer_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_inner_radius).to.eql(pm.environment.get('grain_initial_inner_radius'), \"rocket motor grain_initial_inner_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_initial_height).to.eql(pm.environment.get('grain_initial_height'), \"rocket motor grain_initial_height not matching\");", + " pm.expect(apiRspn.rocket.motor.grains_center_of_mass_position).to.eql(pm.environment.get('grains_center_of_mass_position'), \"rocket motor grains_center_of_mass_position not matching\");", + " pm.expect(apiRspn.rocket.motor.thrust_source).to.eql(pm.environment.get('thrust_source'), \"rocket motor thrust_source not matching\");", + " pm.expect(apiRspn.rocket.motor.grain_separation).to.eql(pm.environment.get('grain_separation'), \"rocket motor grain_separation not matching\");", + " pm.expect(apiRspn.rocket.motor.nozzle_radius).to.eql(pm.environment.get('nozzle_radius'), \"rocket motor nozzle_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.throat_radius).to.eql(pm.environment.get('throat_radius'), \"rocket motor throat_radius not matching\");", + " pm.expect(apiRspn.rocket.motor.interpolation_method).to.eql(pm.environment.get('interpolation_method'), \"rocket motor interpolation_method not matching\");", + " pm.expect(apiRspn.rocket.motor.coordinate_system_orientation).to.eql(pm.environment.get('motor_coordinate_system_orientation'), \"motor coordinate_system_orientation not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket nose\", function () {", + " pm.expect(apiRspn.rocket.nose.length).to.eql(pm.environment.get('nose_length'), \"rocket nose length not matching\");", + " pm.expect(apiRspn.rocket.nose.kind).to.eql(pm.environment.get('kind'), \"rocket nose kind not matching\");", + " pm.expect(apiRspn.rocket.nose.position).to.eql(pm.environment.get('nose_position'), \"rocket nose position not matching\");", + " pm.expect(apiRspn.rocket.nose.base_radius).to.eql(pm.environment.get('base_radius'), \"rocket nose base_radius not matching\");", + " pm.expect(apiRspn.rocket.nose.rocket_radius).to.eql(pm.environment.get('rocket_radius'), \"rocket nose rocket_radius not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket fins\", function () {", + " pm.expect(apiRspn.rocket.fins.n).to.eql(pm.environment.get('n'), \"rocket fins 'n' not matching\");", + " pm.expect(apiRspn.rocket.fins.root_chord).to.eql(pm.environment.get('root_chord'), \"rocket fins root_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.tip_chord).to.eql(pm.environment.get('tip_chord'), \"rocket fins tip_chord not matching\");", + " pm.expect(apiRspn.rocket.fins.span).to.eql(pm.environment.get('span'), \"rocket fins span not matching\");", + " pm.expect(apiRspn.rocket.fins.position).to.eql(pm.environment.get('fin_position'), \"rocket fins position not matching\");", + " pm.expect(apiRspn.rocket.fins.cant_angle).to.eql(pm.environment.get('cant_angle'), \"rocket fins cant_angle not matching\");", + " pm.expect(apiRspn.rocket.fins.radius).to.eql(pm.environment.get('fin_radius'), \"rocket fins radius not matching\");", + " pm.expect(apiRspn.rocket.fins.airfoil).to.eql(pm.environment.get('airfoil'), \"rocket fins airfoil not matching\");", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket tail\", function () {", + " pm.expect(apiRspn.rocket.tail.top_radius).to.eql(pm.environment.get('top_radius'), \"rocket tail top_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.bottom_radius).to.eql(pm.environment.get('bottom_radius'), \"rocket tail bottom_radius not matching\"); ", + " pm.expect(apiRspn.rocket.tail.length).to.eql(pm.environment.get('tail_length'), \"rocket tail length not matching\"); ", + " pm.expect(apiRspn.rocket.tail.position).to.eql(pm.environment.get('tail_position'), \"rocket tail position not matching\"); ", + " pm.expect(apiRspn.rocket.tail.radius).to.eql(pm.environment.get('tail_radius'), \"rocket tail radius not matching\"); ", + " });", + " pm.test(bdd + \" then response must contain a valid flight rocket parachutes\", function () {", + " pm.expect(apiRspn.rocket.parachutes.name).to.eql(pm.environment.get('parachutes_names'), \"rocket parachutes names not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.cd_s).to.eql(pm.environment.get('parachutes_cds'), \"rocket parachutes cd_s not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.sampling_rate).to.eql(pm.environment.get('parachutes_sampling_rate'), \"rocket parachutes sampling_rate not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.lag).to.eql(pm.environment.get('parachutes_lags'), \"rocket parachutes lags not matching\"); ", + " pm.expect(apiRspn.rocket.parachutes.noise).to.eql(pm.environment.get('parachutes_noises'), \"rocket parachutes noises not matching\");", + " pm.expect(apiRspn.rocket.parachutes.triggers).to.eql(pm.environment.get('parachutes_triggers'), \"rocket parachutes triggers not matching\");", + " });" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}/rocket", - "host": [ - "{{endpoint}}" + { + "name": "Read rocketpy Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "", + "//TEST", + "bdd = \"Given a valid rocketpy Flight GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.jsonpickle_rocketpy_flight).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}", - "rocket" - ] + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/rocketpy/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "rocketpy", + "{{flight_id}}" + ] + } + }, + "response": [] }, - "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." - }, - "response": [] - }, - { - "name": "Delete Flight", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "//Fixes the issue of breaking the collection runner whenever an http 500 is received", - "if (responseCode.code == 500) {", - " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " return", - "}", - "", - "var apiRspn = pm.response.json();", - "//TEST", - "bdd = \"Given a valid Flight DELETE request is made\";", - " pm.test(bdd + \" then response must return a 200 status code\", function () {", - " pm.expect(responseCode.code).to.eql(200);", - " });", - " pm.test(bdd + \" then response must contain a valid message\", function () {", - " pm.expect(apiRspn.message).to.eql(\"Flight successfully deleted\", \"message not matching\");", - " pm.expect(apiRspn.deleted_flight_id).to.eql(pm.environment.get('flight_id'), \"flight_id not matching\"); ", - " });" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{endpoint}}/flights/{{flight_id}}", - "host": [ - "{{endpoint}}" + "name": "Simulate Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid rocketpy Flight simulate GET request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.flight_data).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_position).to.exist;", + " pm.expect(apiRspn.flight_data.initial_conditions.initial_angular_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.max_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.min_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.relative_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.absolute_error_tolerance).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.time_overshoot).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.terminate_on_apogee).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.number_of_time_steps).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.numerical_integration_settings.avg_function_evaluations_per_time_step).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.rail_length).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_inclination).to.exist;", + " pm.expect(apiRspn.flight_data.launch_rail_conditions.flight_heading).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.frontal_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.surface_wind_conditions.lateral_surface_wind_speed).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_time).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_static_margin).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_angle_of_attack).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_thrust_weight_ratio).to.exist;", + " pm.expect(apiRspn.flight_data.out_of_rail_conditions.out_of_rail_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_time).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_rocket_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_freestream_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.burnout_conditions.burnout_kinetic_energy).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_time).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_altitude).to.exist;", + " pm.expect(apiRspn.flight_data.apogee_conditions.apogee_freestream_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_speed).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_mach_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_reynolds_number).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_dynamic_pressure).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_acceleration_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_during_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_gs_after_motor_burn).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_upper_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_normal_force).to.exist;", + " pm.expect(apiRspn.flight_data.maximum_values.maximum_lower_rail_button_shear_force).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.x_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.y_impact_position).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.time_of_impact).to.exist;", + " pm.expect(apiRspn.flight_data.impact_conditions.impact_velocity).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Drogue).to.exist;", + " pm.expect(apiRspn.flight_data.events_registered.events_trace.Main).to.exist; ", + " });" + ], + "type": "text/javascript" + } + } ], - "path": [ - "flights", - "{{flight_id}}" - ] + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/simulate", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "simulate" + ] + } + }, + "response": [] + }, + { + "name": "Update Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// reduce environment date for future assertion", + "flightRequest.environment.date = flightRequest.environment.date.substring(0, flightRequest.environment.date.length - 7);", + "", + "// save flight parameters", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "pm.environment.set('rail_length', flightRequest.rail_length) ", + "pm.environment.set('inclination', flightRequest.inclination)", + "pm.environment.set('heading', flightRequest.heading)", + "", + "// flight environment", + "pm.environment.set('latitude', flightRequest.environment.latitude)", + "pm.environment.set('longitude', flightRequest.environment.longitude)", + "pm.environment.set('elevation', flightRequest.environment.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.environment.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.environment.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.environment.date) ", + "", + "// flight rocket", + "pm.environment.set('radius', flightRequest.rocket.radius)", + "pm.environment.set('mass', flightRequest.rocket.mass)", + "pm.environment.set('inertia', flightRequest.rocket.inertia)", + "pm.environment.set('power_off_drag', flightRequest.rocket.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.rocket.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.rocket.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.rocket.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rocket.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rocket.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rocket.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rocket.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.rocket.coordinate_system_orientation)", + "", + "// flight rocket motor", + "pm.environment.set('burn_time', flightRequest.rocket.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.rocket.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.rocket.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.rocket.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.rocket.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.rocket.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.rocket.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.rocket.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.rocket.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.rocket.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.rocket.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.rocket.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.rocket.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.rocket.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.rocket.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.rocket.motor.coordinate_system_orientation)", + "", + "// flight rocket nose", + "pm.environment.set('nose_length', flightRequest.rocket.nose.length)", + "pm.environment.set('kind', flightRequest.rocket.nose.kind)", + "pm.environment.set('nose_position', flightRequest.rocket.nose.position)", + "pm.environment.set('base_radius', flightRequest.rocket.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.rocket.nose.rocket_radius)", + "", + "// flight rocket fins", + "pm.environment.set('n', flightRequest.rocket.fins.n)", + "pm.environment.set('root_chord', flightRequest.rocket.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.rocket.fins.tip_chord)", + "pm.environment.set('span', flightRequest.rocket.fins.span)", + "pm.environment.set('fin_position', flightRequest.rocket.fins.position)", + "pm.environment.set('cant_angle', flightRequest.rocket.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.rocket.fins.radius)", + "pm.environment.set('airfoil', flightRequest.rocket.fins.airfoil)", + "", + "// flight rocket tail", + "pm.environment.set('top_radius', flightRequest.rocket.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.rocket.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.rocket.tail.length)", + "pm.environment.set('tail_position', flightRequest.rocket.tail.position)", + "pm.environment.set('tail_radius', flightRequest.rocket.tail.radius)", + "", + "// flight rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.rocket.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.rocket.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.rocket.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.rocket.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.rocket.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.rocket.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"environment\": {\n \"atmospheric_model_file\": \"GFS\",\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"date\": \"2023-12-29T10:22:00.921396\",\n \"elevation\": 1300,\n \"latitude\": 2,\n \"longitude\": 1\n },\n \"rocket\": {\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"mass\": 16.235,\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"motor_position\": -1.255,\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"radius\": 0.0632,\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n }\n },\n \"inclination\": 85,\n \"heading\": 0,\n \"rail_length\": 5.2\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/?rocket_option=Calisto&motor_kind=Solid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Solid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Update Flight Environment", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save environment parameters", + "pm.environment.set('env_id', apiRspn.new_env_id) ", + "pm.environment.set('latitude', flightRequest.latitude)", + "pm.environment.set('longitude', flightRequest.longitude)", + "pm.environment.set('elevation', flightRequest.elevation) ", + "pm.environment.set('atmospheric_model_type', flightRequest.atmospheric_model_type) ", + "pm.environment.set('atmospheric_model_file', flightRequest.atmospheric_model_file) ", + "pm.environment.set('date', flightRequest.date) ", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"latitude\": 0,\n \"longitude\": 0,\n \"elevation\": 1400,\n \"atmospheric_model_type\": \"standard_atmosphere\",\n \"atmospheric_model_file\": \"GFS\",\n \"date\": \"2023-05-09T16:30:50.065992\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/env", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "env" + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Update Flight Rocket", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "var flightRequest = JSON.parse(pm.request.body.raw);", + "", + "// save new flight id", + "pm.environment.set('flight_id', apiRspn.new_flight_id) ", + "", + "// save rocket parameters", + "pm.environment.set('rocket_id', apiRspn.new_rocket_id)", + "pm.environment.set('radius', flightRequest.radius)", + "pm.environment.set('mass', flightRequest.mass)", + "pm.environment.set('inertia', flightRequest.inertia)", + "pm.environment.set('power_off_drag', flightRequest.power_off_drag)", + "pm.environment.set('power_on_drag', flightRequest.power_on_drag)", + "pm.environment.set('center_of_mass_without_motor', flightRequest.center_of_mass_without_motor)", + "pm.environment.set('motor_position', flightRequest.motor_position)", + "pm.environment.set('rail_buttons', flightRequest.rail_buttons)", + "pm.environment.set('upper_button_position', flightRequest.rail_buttons.upper_button_position)", + "pm.environment.set('lower_button_position', flightRequest.rail_buttons.lower_button_position)", + "pm.environment.set('angular_position', flightRequest.rail_buttons.angular_position)", + "pm.environment.set('rocket_coordinate_system_orientation', flightRequest.coordinate_system_orientation)", + "", + "// rocket motor", + "pm.environment.set('burn_time', flightRequest.motor.burn_time)", + "pm.environment.set('dry_mass', flightRequest.motor.dry_mass)", + "pm.environment.set('dry_inertia', flightRequest.motor.dry_inertia)", + "pm.environment.set('center_of_dry_mass_position', flightRequest.motor.center_of_dry_mass_position)", + "pm.environment.set('grain_number', flightRequest.motor.grain_number)", + "pm.environment.set('grain_density', flightRequest.motor.grain_density)", + "pm.environment.set('grain_outer_radius', flightRequest.motor.grain_outer_radius)", + "pm.environment.set('grain_initial_inner_radius', flightRequest.motor.grain_initial_inner_radius)", + "pm.environment.set('grain_initial_height', flightRequest.motor.grain_initial_height)", + "pm.environment.set('grains_center_of_mass_position', flightRequest.motor.grains_center_of_mass_position)", + "pm.environment.set('grain_separation', flightRequest.motor.grain_separation)", + "pm.environment.set('thrust_source', flightRequest.motor.thrust_source)", + "pm.environment.set('nozzle_radius', flightRequest.motor.nozzle_radius)", + "pm.environment.set('throat_radius', flightRequest.motor.throat_radius)", + "pm.environment.set('interpolation_method', flightRequest.motor.interpolation_method)", + "pm.environment.set('motor_coordinate_system_orientation', flightRequest.motor.coordinate_system_orientation)", + "", + "// rocket nose", + "pm.environment.set('nose_length', flightRequest.nose.length)", + "pm.environment.set('kind', flightRequest.nose.kind)", + "pm.environment.set('nose_position', flightRequest.nose.position)", + "pm.environment.set('base_radius', flightRequest.nose.base_radius)", + "pm.environment.set('rocket_radius', flightRequest.nose.rocket_radius)", + "", + "// rocket fins", + "pm.environment.set('n', flightRequest.fins.n)", + "pm.environment.set('root_chord', flightRequest.fins.root_chord)", + "pm.environment.set('tip_chord', flightRequest.fins.tip_chord)", + "pm.environment.set('span', flightRequest.fins.span)", + "pm.environment.set('fin_position', flightRequest.fins.position)", + "pm.environment.set('cant_angle', flightRequest.fins.cant_angle)", + "pm.environment.set('fin_radius', flightRequest.fins.radius)", + "pm.environment.set('airfoil', flightRequest.fins.airfoil)", + "", + "// rocket tail", + "pm.environment.set('top_radius', flightRequest.tail.top_radius)", + "pm.environment.set('bottom_radius', flightRequest.tail.bottom_radius)", + "pm.environment.set('tail_length', flightRequest.tail.length)", + "pm.environment.set('tail_position', flightRequest.tail.position)", + "pm.environment.set('tail_radius', flightRequest.tail.radius)", + "", + "// rocket parachute", + "pm.environment.set('parachutes_names', flightRequest.parachutes.name)", + "pm.environment.set('parachutes_cds', flightRequest.parachutes.cd_s)", + "pm.environment.set('parachutes_sampling_rate', flightRequest.parachutes.sampling_rate)", + "pm.environment.set('parachutes_lags', flightRequest.parachutes.lag)", + "pm.environment.set('parachutes_noises', flightRequest.parachutes.noise)", + "pm.environment.set('parachutes_triggers', flightRequest.parachutes.triggers)", + "", + "//TEST", + "bdd = \"Given a valid Flight PUT request is made to the API\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully updated\");", + " });", + " pm.test(bdd + \" then response must contain a valid new_flight_id\", function () {", + " pm.expect(apiRspn.new_flight_id).to.exist; ", + " });" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"motor\": {\n \"burn_time\": 6.8,\n \"center_of_dry_mass_position\": 0.512,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 10,\n \"liquid\": {\n \"density\": 10,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}/rocket?rocket_option=Calisto&motor_kind=Solid", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}", + "rocket" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Solid" + } + ] + }, + "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." + }, + "response": [] + }, + { + "name": "Delete Flight", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "//Fixes the issue of breaking the collection runner whenever an http 500 is received", + "if (responseCode.code == 500) {", + " pm.test(\"Given a request is made then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " return", + "}", + "", + "var apiRspn = pm.response.json();", + "//TEST", + "bdd = \"Given a valid Flight DELETE request is made\";", + " pm.test(bdd + \" then response must return a 200 status code\", function () {", + " pm.expect(responseCode.code).to.eql(200);", + " });", + " pm.test(bdd + \" then response must contain a valid message\", function () {", + " pm.expect(apiRspn.message).to.eql(\"Flight successfully deleted\", \"message not matching\");", + " pm.expect(apiRspn.deleted_flight_id).to.eql(pm.environment.get('flight_id'), \"flight_id not matching\"); ", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{endpoint}}/flights/{{flight_id}}", + "host": [ + "{{endpoint}}" + ], + "path": [ + "flights", + "{{flight_id}}" + ] + } + }, + "response": [] } - }, - "response": [] + ] } ] }, @@ -1314,7 +3112,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n}", + "raw": "{\n \"thrust_source\": \"Cesaroni_M1670\",\n \"burn_time\": 3.9,\n \"nozzle_radius\": 0.033,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"center_of_dry_mass_position\": 0.317,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n}", "options": { "raw": { "language": "json" @@ -1322,13 +3120,19 @@ } }, "url": { - "raw": "{{endpoint}}/motors/", + "raw": "{{endpoint}}/motors/?motor_kind=Solid", "host": [ "{{endpoint}}" ], "path": [ "motors", "" + ], + "query": [ + { + "key": "motor_kind", + "value": "Solid" + } ] }, "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." @@ -1562,7 +3366,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n}", + "raw": "{\n \"thrust_source\": \"Cesaroni_M1670\",\n \"burn_time\": 3.9,\n \"nozzle_radius\": 0.033,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"center_of_dry_mass_position\": 0.317,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n}", "options": { "raw": { "language": "json" @@ -1570,13 +3374,20 @@ } }, "url": { - "raw": "{{endpoint}}/motors/{{motor_id}}", + "raw": "{{endpoint}}/motors/{{motor_id}}/?motor_kind=Liquid", "host": [ "{{endpoint}}" ], "path": [ "motors", - "{{motor_id}}" + "{{motor_id}}", + "" + ], + "query": [ + { + "key": "motor_kind", + "value": "Liquid" + } ] }, "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." @@ -1747,7 +3558,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n } \n}", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 45,\n \"lower_button_position\": 0.2,\n \"upper_button_position\": -0.5\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", "options": { "raw": { "language": "json" @@ -1755,13 +3566,23 @@ } }, "url": { - "raw": "{{endpoint}}/rockets/", + "raw": "{{endpoint}}/rockets/?rocket_option=Calisto&motor_kind=Solid", "host": [ "{{endpoint}}" ], "path": [ "rockets", "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Solid" + } ] }, "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection." @@ -2101,7 +3922,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"inertia\": [\n 6.321, \n 6.321, \n 0.0346\n ],\n \"power_off_drag\": \"calisto\",\n \"power_on_drag\": \"calisto\",\n \"center_of_mass_without_motor\": 0,\n \"coordinate_system_orientation\": \"tail_to_nose\",\n \"motor_position\": -1.255,\n \"rail_buttons\": {\n \"upper_button_position\": -0.5,\n \"lower_button_position\": 0.2,\n \"angular_position\": 45\n },\n \"motor\": {\n \"burn_time\": 3.9,\n \"dry_mass\": 1.815,\n \"dry_inertia\": [0.125, 0.125, 0.002],\n \"center_of_dry_mass_position\": 0.317,\n \"grain_number\": 5,\n \"grain_density\": 1815,\n \"grain_outer_radius\": 0.033,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_initial_height\": 0.12,\n \"grains_center_of_mass_position\": -0.85704,\n \"grain_separation\": 0.005,\n \"thrust_source\": \"Cesaroni_M1670\", \n \"nozzle_radius\": 0.033,\n \"throat_radius\": 0.011,\n \"interpolation_method\": \"linear\",\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\"\n },\n \"nose\": {\n \"length\": 0.55829,\n \"kind\": \"vonKarman\",\n \"position\": 1.278,\n \"base_radius\": 0.0635,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"n\": 4,\n \"root_chord\": 0.12,\n \"tip_chord\": 0.04,\n \"span\": 0.1,\n \"position\": -1.04956,\n \"cant_angle\": 0,\n \"radius\": 0.0635,\n \"airfoil\": \"\"\n },\n \"tail\": {\n \"top_radius\": 0.0635,\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635\n },\n \"parachutes\": {\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"cd_s\": [\n 10,\n 1\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n } \n}", + "raw": "{\n \"rail_buttons\": {\n \"angular_position\": 47,\n \"lower_button_position\": 0.1,\n \"upper_button_position\": -0.2\n },\n \"motor\": {\n \"burn_time\": 5.9,\n \"center_of_dry_mass_position\": 0.317,\n \"coordinate_system_orientation\": \"nozzle_to_combustion_chamber\",\n \"dry_inertia\": [\n 0.125,\n 0.125,\n 0.002\n ],\n \"dry_mass\": 1.815,\n \"grain_density\": 1815,\n \"grain_initial_height\": 0.12,\n \"grain_initial_inner_radius\": 0.015,\n \"grain_number\": 5,\n \"grain_outer_radius\": 0.033,\n \"grain_separation\": 0.005,\n \"grains_center_of_mass_position\": -0.85704,\n \"interpolation_method\": \"linear\",\n \"nozzle_radius\": 0.033,\n \"tanks\": [\n {\n \"discretize\": 100,\n \"flux_time\": [\n 0,\n 8\n ],\n \"gas\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"gas_mass\": 0.1,\n \"gas_mass_flow_rate_in\": 0.1,\n \"gas_mass_flow_rate_out\": 0.1,\n \"geometry\": [\n [\n [\n 0,\n 5\n ],\n 1\n ],\n [\n [\n 5,\n 10\n ],\n 2\n ]\n ],\n \"initial_gas_mass\": 0.1,\n \"initial_liquid_mass\": 5,\n \"liquid\": {\n \"density\": 100,\n \"name\": \"FluidName\"\n },\n \"liquid_height\": 0.5,\n \"liquid_mass\": 5,\n \"liquid_mass_flow_rate_in\": 0.1,\n \"liquid_mass_flow_rate_out\": 0.1,\n \"name\": \"Tank\",\n \"position\": 1,\n \"tank_kind\": \"MassFlow\",\n \"ullage\": 0.1\n }\n ],\n \"throat_radius\": 0.011,\n \"thrust_source\": \"Cesaroni_M1670\"\n },\n \"nose\": {\n \"base_radius\": 0.0635,\n \"kind\": \"vonKarman\",\n \"length\": 0.55829,\n \"position\": 1.278,\n \"rocket_radius\": 0.0635\n },\n \"fins\": {\n \"airfoil\": \"\",\n \"cant_angle\": 0,\n \"n\": 4,\n \"position\": -1.04956,\n \"radius\": 0.0635,\n \"root_chord\": 0.12,\n \"span\": 0.1,\n \"tip_chord\": 0.04\n },\n \"tail\": {\n \"bottom_radius\": 0.0435,\n \"length\": 0.06,\n \"position\": -1.194656,\n \"radius\": 0.0635,\n \"top_radius\": 0.0635\n },\n \"parachutes\": {\n \"cd_s\": [\n 10,\n 1\n ],\n \"lag\": [\n 1.5,\n 1.5\n ],\n \"name\": [\n \"Main\",\n \"Drogue\"\n ],\n \"noise\": [\n [\n 0,\n 8.3,\n 0.5\n ],\n [\n 0,\n 8.3,\n 0.5\n ]\n ],\n \"sampling_rate\": [\n 105,\n 105\n ],\n \"triggers\": [\n \"lambda p, h, y: y[5] < 0 and h < 800\",\n \"lambda p, h, y: y[5] < 0\"\n ]\n },\n \"inertia\": [\n 6.321,\n 6.321,\n 0.0346\n ],\n \"center_of_mass_without_motor\": 0,\n \"radius\": 0.0632,\n \"mass\": 16.235,\n \"motor_position\": -1.255,\n \"power_off_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"power_on_drag\": [\n [\n 0.01,\n 0.333865758\n ],\n [\n 0.02,\n 0.394981721\n ],\n [\n 0.03,\n 0.407756063\n ]\n ],\n \"coordinate_system_orientation\": \"tail_to_nose\"\n}", "options": { "raw": { "language": "json" @@ -2109,13 +3930,24 @@ } }, "url": { - "raw": "{{endpoint}}/rockets/{{rocket_id}}", + "raw": "{{endpoint}}/rockets/{{rocket_id}}/?rocket_option=Calisto&motor_kind=Hybrid", "host": [ "{{endpoint}}" ], "path": [ "rockets", - "{{rocket_id}}" + "{{rocket_id}}", + "" + ], + "query": [ + { + "key": "rocket_option", + "value": "Calisto" + }, + { + "key": "motor_kind", + "value": "Hybrid" + } ] }, "description": "This returns a `token` that you can use to retrieve information later on.\n\nWe have included a test to confirm if a token is returned. We have also added test scripts to copy the token to the `token` collection variable. This makes it easy for us to reuse this token in other requests in the collection."