diff --git a/tests/test_airline.py b/tests/test_airline.py new file mode 100644 index 0000000..aa9c588 --- /dev/null +++ b/tests/test_airline.py @@ -0,0 +1,132 @@ +from flask.testing import FlaskClient +from sqlalchemy.sql import func, select + +from teufa import db as dbm +from teufa.ext import db + + +def test_create_airline(client: FlaskClient, tenant: dbm.Tenant): + response = client.post( + "/api/v1/airlines", + json={ + "airline": { + "name": "Test Airline", + "iata": "TA", + "icao": "TST", + } + }, + ) + + assert response.status_code == 201 + assert response.json == { + "airline": { + "id": 1, + "name": "Test Airline", + "iata": "TA", + "icao": "TST", + } + } + + +def test_get_airline(client: FlaskClient, tenant: dbm.Tenant): + airline = dbm.Airline( + tenant_id=tenant.id, + name="Test Airline", + iata="TA", + icao="TST", + ) + db.session.add(airline) + db.session.commit() + + response = client.get(f"/api/v1/airlines/{airline.id}") + + assert response.status_code == 200 + assert response.json == { + "airline": { + "id": airline.id, + "name": "Test Airline", + "iata": "TA", + "icao": "TST", + } + } + + +def test_get_airline_not_found(client: FlaskClient): + response = client.get("/api/v1/airlines/1") + + assert response.status_code == 404 + assert response.json == {"message": "Airline not found"} + + +def test_update_airline(client: FlaskClient, tenant: dbm.Tenant): + airline = dbm.Airline( + tenant_id=tenant.id, + name="Test Airline", + iata="TA", + icao="TST", + ) + db.session.add(airline) + db.session.commit() + + response = client.put( + f"/api/v1/airlines/{airline.id}", + json={ + "airline": { + "name": "Updated Airline", + "iata": "UA", + "icao": "UPD", + } + }, + ) + + assert response.status_code == 200 + assert response.json == { + "airline": { + "id": airline.id, + "name": "Updated Airline", + "iata": "UA", + "icao": "UPD", + } + } + + +def test_update_airline_not_found(client: FlaskClient): + response = client.put( + "/api/v1/airlines/1", + json={ + "airline": { + "name": "Updated Airline", + "iata": "UA", + "icao": "UPD", + } + }, + ) + + assert response.status_code == 404 + assert response.json == {"message": "Airline not found"} + + +def test_delete_airline(client: FlaskClient, tenant: dbm.Tenant): + airline = dbm.Airline( + tenant_id=tenant.id, + name="Test Airline", + iata="TA", + icao="TST", + ) + db.session.add(airline) + db.session.commit() + + response = client.delete(f"/api/v1/airlines/{airline.id}") + + assert response.status_code == 204 + assert response.data == b"" + + with db.session.begin(): + assert db.session.scalar(select(func.count(dbm.Airline.id))) == 0 + + +def test_delete_airline_not_found(client: FlaskClient): + response = client.delete("/api/v1/airlines/1") + + assert response.status_code == 404 + assert response.json == {"message": "Airline not found"} diff --git a/teufa/dao.py b/teufa/dao.py index ceac8db..7f27828 100644 --- a/teufa/dao.py +++ b/teufa/dao.py @@ -1,3 +1,5 @@ +from datetime import datetime + from pydantic import BaseModel empty = object() @@ -71,3 +73,37 @@ class UpdateFlightResponse(BaseModel): class GetFlightResponse(BaseModel): flight: Flight + + +class Airline(BaseModel): + id: int | None = None + name: str + iata: str + icao: str + + +class PartialAirline(BaseModel): + id: int | object = empty + name: str | object = empty + iata: str | object = empty + icao: str | object = empty + + +class CreateAirlineRequest(BaseModel): + airline: Airline + + +class CreateAirlineResponse(BaseModel): + airline: Airline + + +class UpdateAirlineRequest(BaseModel): + airline: PartialAirline + + +class UpdateAirlineResponse(BaseModel): + airline: Airline + + +class GetAirlineResponse(BaseModel): + airline: Airline diff --git a/teufa/v1_api/__init__.py b/teufa/v1_api/__init__.py index 18246fa..258313f 100644 --- a/teufa/v1_api/__init__.py +++ b/teufa/v1_api/__init__.py @@ -3,6 +3,7 @@ from sqlalchemy import select from .. import db as dbm from ..ext import db +from .airlines import bp as airlines_bp from .flights import bp as flights_bp from .tenants import bp as tenants_bp @@ -25,5 +26,6 @@ def before_request(): ).first() +api_bp.register_blueprint(airlines_bp) api_bp.register_blueprint(flights_bp) v1_bp.register_blueprint(api_bp) diff --git a/teufa/v1_api/airlines.py b/teufa/v1_api/airlines.py new file mode 100644 index 0000000..92829dc --- /dev/null +++ b/teufa/v1_api/airlines.py @@ -0,0 +1,103 @@ +from flask import Blueprint, g, jsonify, request + +from .. import dao +from .. import db as dbm +from ..ext import db + +bp = Blueprint("airlines", __name__, url_prefix="/airlines") + + +@bp.route("", methods=["POST"]) +def create_airline(): + data = request.get_json() + req = dao.CreateAirlineRequest(**data) + + airline = dbm.Airline( + tenant_id=g.tenant.id, + name=req.airline.name, + iata=req.airline.iata, + icao=req.airline.icao, + ) + + db.session.add(airline) + db.session.commit() + + res = dao.CreateAirlineResponse( + **{ + "airline": { + "id": airline.id, + "name": airline.name, + "iata": airline.iata, + "icao": airline.icao, + } + } + ) + + return jsonify(res.model_dump()), 201 + + +@bp.route("/", methods=["GET"]) +def get_airline(airline_id): + airline = db.session.get(dbm.Airline, airline_id) + + if not airline: + return jsonify(dao.Error(message="Airline not found").model_dump()), 404 + + res = dao.GetAirlineResponse( + **{ + "airline": { + "id": airline.id, + "name": airline.name, + "iata": airline.iata, + "icao": airline.icao, + } + } + ) + + return jsonify(res.model_dump()) + + +@bp.route("/", methods=["PUT"]) +def update_airline(airline_id): + airline = db.session.get(dbm.Airline, airline_id) + + if not airline: + return jsonify(dao.Error(message="Airline not found").model_dump()), 404 + + data = request.get_json() + req = dao.UpdateAirlineRequest(**data) + + if req.airline.name is not dao.empty: + airline.name = req.airline.name + if req.airline.iata is not dao.empty: + airline.iata = req.airline.iata + if req.airline.icao is not dao.empty: + airline.icao = req.airline.icao + + db.session.commit() + + res = dao.UpdateAirlineResponse( + **{ + "airline": { + "id": airline.id, + "name": airline.name, + "iata": airline.iata, + "icao": airline.icao, + } + } + ) + + return jsonify(res.model_dump()) + + +@bp.route("/", methods=["DELETE"]) +def delete_airline(airline_id): + airline = db.session.get(dbm.Airline, airline_id) + + if not airline: + return jsonify(dao.Error(message="Airline not found").model_dump()), 404 + + db.session.delete(airline) + db.session.commit() + + return "", 204