stub out v1 api
This commit is contained in:
21
teufa/app.py
21
teufa/app.py
@@ -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
41
teufa/dao.py
Normal 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
|
109
teufa/db.py
109
teufa/db.py
@@ -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
10
teufa/v1_api/__init__.py
Normal 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
102
teufa/v1_api/flights.py
Normal 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
|
Reference in New Issue
Block a user