stub out v1 api

This commit is contained in:
2024-05-23 22:51:14 -06:00
parent f56de15674
commit 2dbb0a8416
12 changed files with 797 additions and 41 deletions

View File

@@ -1,7 +1,10 @@
from flask import Flask
from flask import Flask, g, request
from sqlalchemy import select
from . import db as dbm
from .config import Config
from .ext import db
from .v1_api import bp as v1_bp
def create_app():
@@ -10,4 +13,20 @@ def create_app():
db.init_app(app)
@app.before_request
def before_request():
if not hasattr(g, "tenant"):
hostname = request.host.split(":")[0]
g.tenant = db.session.scalars(
select(dbm.Tenant).filter_by(hostname=hostname).limit(1)
).first()
@app.teardown_request
def teardown_request(exc):
if exc:
db.session.rollback()
db.session.remove()
app.register_blueprint(v1_bp)
return app

41
teufa/dao.py Normal file
View File

@@ -0,0 +1,41 @@
from pydantic import BaseModel
empty = object()
class Error(BaseModel):
message: str
class Flight(BaseModel):
id: int | None = None
departure_icao: str
arrival_icao: str
aircraft_id: int
class PartialFlight(BaseModel):
id: int | object = empty
departure_icao: str | object = empty
arrival_icao: str | object = empty
aircraft_id: int | object = empty
class CreateFlightRequest(BaseModel):
flight: Flight
class CreateFlightResponse(BaseModel):
flight: Flight
class UpdateFlightRequest(BaseModel):
flight: PartialFlight
class UpdateFlightResponse(BaseModel):
flight: Flight
class GetFlightResponse(BaseModel):
flight: Flight

View File

@@ -1,13 +1,120 @@
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from datetime import datetime
from sqlalchemy import ForeignKey, UniqueConstraint, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
class Base(DeclarativeBase):
pass
class Tenant(Base):
__tablename__ = "tenants"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
hostname: Mapped[str]
created_at: Mapped[datetime] = mapped_column(default=func.now())
users: Mapped[list["User"]] = relationship(
back_populates="tenant", cascade="all, delete-orphan"
)
airlines: Mapped[list["Airline"]] = relationship(
back_populates="tenant", cascade="all, delete-orphan"
)
airports: Mapped[list["Airport"]] = relationship(
back_populates="tenant", cascade="all, delete-orphan"
)
fleet: Mapped[list["Aircraft"]] = relationship(
back_populates="tenant", cascade="all, delete-orphan"
)
liveries: Mapped[list["Livery"]] = relationship(
back_populates="tenant", cascade="all, delete-orphan"
)
flights: Mapped[list["Flight"]] = relationship(
back_populates="tenant", cascade="all, delete-orphan"
)
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
name: Mapped[str]
email: Mapped[str]
created_at: Mapped[datetime] = mapped_column(default=func.now())
tenant: Mapped["Tenant"] = relationship(back_populates="users")
class Airline(Base):
__tablename__ = "airlines"
id: Mapped[int] = mapped_column(primary_key=True)
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
name: Mapped[str]
iata: Mapped[str]
icao: Mapped[str]
created_at: Mapped[datetime] = mapped_column(default=func.now())
tenant: Mapped["Tenant"] = relationship(back_populates="airlines")
class Airport(Base):
__tablename__ = "airports"
icao: Mapped[str] = mapped_column(primary_key=True)
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
iata: Mapped[str]
name: Mapped[str]
city: Mapped[str]
country: Mapped[str]
latitude: Mapped[float]
longitude: Mapped[float]
created_at: Mapped[datetime] = mapped_column(default=func.now())
tenant: Mapped["Tenant"] = relationship(back_populates="airports")
class Aircraft(Base):
__tablename__ = "aircraft"
__table_args__ = (UniqueConstraint("icao", "tail_number"),)
id: Mapped[int] = mapped_column(primary_key=True)
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
icao: Mapped[str]
tail_number: Mapped[str | None]
range_nm: Mapped[int]
created_at: Mapped[datetime] = mapped_column(default=func.now())
tenant: Mapped["Tenant"] = relationship(back_populates="fleet")
liveries: Mapped[list["Livery"]] = relationship(back_populates="aircraft")
flights: Mapped[list["Flight"]] = relationship(back_populates="aircraft")
class Livery(Base):
__tablename__ = "livery"
id: Mapped[int] = mapped_column(primary_key=True)
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
name: Mapped[str]
aircraft_id: Mapped[int] = mapped_column(ForeignKey("aircraft.id"))
created_at: Mapped[datetime] = mapped_column(default=func.now())
tenant: Mapped["Tenant"] = relationship(back_populates="liveries")
aircraft: Mapped["Aircraft"] = relationship(back_populates="liveries")
class Flight(Base):
__tablename__ = "flights"
id: Mapped[int] = mapped_column(primary_key=True)
tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id"))
departure_icao: Mapped[str]
arrival_icao: Mapped[str]
created_at: Mapped[datetime] = mapped_column(default=func.now())
aircraft_id: Mapped["Aircraft"] = mapped_column(ForeignKey("aircraft.id"))
tenant: Mapped["Tenant"] = relationship(back_populates="flights")
aircraft: Mapped["Aircraft"] = relationship(back_populates="flights")

10
teufa/v1_api/__init__.py Normal file
View File

@@ -0,0 +1,10 @@
from flask import Blueprint
from flask_restful import Api
from .flights import FlightCollectionResource, FlightResource
bp = Blueprint("api", __name__, url_prefix="/api")
api = Api(bp)
api.add_resource(FlightCollectionResource, "/v1/flights")
api.add_resource(FlightResource, "/v1/flights/<int:flight_id>")

102
teufa/v1_api/flights.py Normal file
View File

@@ -0,0 +1,102 @@
from flask import g, request
from flask_restful import Resource
from .. import dao
from .. import db as dbm
from ..ext import db
class FlightCollectionResource(Resource):
def post(self):
data = request.get_json()
req = dao.CreateFlightRequest(**data)
flight = dbm.Flight(
tenant_id=g.tenant.id,
departure_icao=req.flight.departure_icao,
arrival_icao=req.flight.arrival_icao,
aircraft_id=req.flight.aircraft_id,
)
db.session.add(flight)
db.session.commit()
res = dao.CreateFlightResponse(
**{
"flight": {
"id": flight.id,
"departure_icao": flight.departure_icao,
"arrival_icao": flight.arrival_icao,
"aircraft_id": flight.aircraft_id,
}
}
)
return res.model_dump_json(), 201
class FlightResource(Resource):
def get(self, flight_id):
flight = db.session.get(dbm.Flight, flight_id)
if not flight:
return dao.Error(message="Flight not found").model_dump_json(), 404
res = dao.GetFlightResponse(
**{
"flight": {
"id": flight.id,
"departure_icao": flight.departure_icao,
"arrival_icao": flight.arrival_icao,
"aircraft_id": flight.aircraft_id,
}
}
)
return res.model_dump_json()
def put(self, flight_id):
flight = db.session.get(dbm.Flight, flight_id)
if not flight:
return dao.Error(message="Flight not found").model_dump_json(), 404
data = request.get_json()
req = dao.UpdateFlightRequest(**data)
if req.flight.id is not dao.empty:
flight.id = req.flight.id
if req.flight.departure_icao is not dao.empty:
flight.departure_icao = req.flight.departure_icao
if req.flight.arrival_icao is not dao.empty:
flight.arrival_icao = req.flight.arrival_icao
if req.flight.aircraft_id is not dao.empty:
flight.aircraft_id = req.flight.aircraft_id
db.session.commit()
res = dao.UpdateFlightResponse(
**{
"flight": {
"id": flight.id,
"departure_icao": flight.departure_icao,
"arrival_icao": flight.arrival_icao,
"aircraft_id": flight.aircraft_id,
}
}
)
return res.model_dump_json()
def delete(self, flight_id):
flight = db.session.get(dbm.Flight, flight_id)
if not flight:
return dao.Error(message="Flight not found").model_dump_json(), 404
db.session.delete(flight)
db.session.commit()
return "", 204