Everything is locked down for dependencies in the backend.

Signed-off-by: Cliff Hill <xlorep@darkhelm.org>
This commit is contained in:
2025-10-04 15:43:36 -04:00
parent 41b215eafc
commit f6718820ff
6 changed files with 691 additions and 1323 deletions

1897
backend/poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,30 +18,29 @@ Changelog = "https://github.com/xlorepdarkhelm/backend/releases"
[tool.poetry.dependencies]
python = ">=3.13,<4.0"
click = "8.1.7"
fastapi = {extras = ["standard"], version = "0.116.1"}
fastapi = {extras = ["standard"], version = "0.111.0"}
asyncpg = "0.30.0"
sqlalchemy = "2.0.43"
alembic = "1.16.5"
alembic = "1.13.1"
python-dateutil = "2.9.0.post0"
[tool.poetry.group.dev.dependencies]
pyyaml = "6.0.1"
Pygments = "2.17.2"
black = "25.9.0"
coverage = {extras = ["toml"], version = "7.5.3"}
black = "24.3.0"
coverage = {extras = ["toml"], version = "7.4.4"}
darglint = "1.8.1"
flake8 = "7.0.0"
flake8-bandit = "4.1.0"
flake8-bugbear = "24.4.27"
flake8 = "6.1.0"
flake8-bugbear = "23.9.16"
flake8-docstrings = "1.7.0"
flake8-rst-docstrings = "0.3.0"
furo = "2024.8.6"
isort = "5.13.2"
mypy = "1.10.0"
mypy = "1.8.0"
pep8-naming = "0.13.3"
pre-commit = "3.7.0"
pre-commit = "3.5.0"
pre-commit-hooks = "4.6.0"
pytest = "8.2.2"
pytest = "8.2.0"
pyupgrade = "3.15.2"
safety = "3.0.1"
sphinx = "7.3.7"
@@ -51,10 +50,10 @@ typeguard = "4.2.1"
xdoctest = {extras = ["colors"], version = "1.1.3"}
myst-parser = {version = "2.0.0"}
pytest-asyncio = "1.1.0"
cryptography = "45.0.6" # For safety
httpx = "0.28.1"
python-dotenv = "1.1.1"
nox = "2025.5.1"
cryptography = "42.0.0" # For safety
httpx = "0.27.0"
python-dotenv = "1.0.0"
nox = "2024.4.15"
nox-poetry = "1.2.0"
asyncpg-stubs = "0.30.2"
types-python-dateutil = "2.9.0.20250822"

View File

@@ -175,4 +175,3 @@ async def create_db_and_tables() -> None:
type UserList = list[User]
type RoomList = list[Room]
type BookingList = list[Booking]
type InviteeList = list[Invitee]

View File

@@ -16,13 +16,14 @@ from backend.dependencies.bookings import NewBookingService
from backend.dependencies.bookings import UpdateBookingService
from backend.dependencies.db import DBSession
from backend.dependencies.rooms import GetRoomService
from backend.models import Booking
from backend.models import BookingList
# Import the SSE publisher from rooms router
from backend.routers.rooms import publish_room_availability_event
from backend.schemas.bookings import BookingCreate
from backend.schemas.bookings import BookingResponse
from backend.schemas.bookings import BookingUpdate
from backend.schemas.rooms import RoomResponse
logger = logging.getLogger(__name__)
@@ -37,7 +38,7 @@ async def read_booking(
session: DBSession,
get_booking_func: GetBookingService,
get_room_func: GetRoomService,
) -> BookingResponse:
) -> Booking:
"""Retrieve a booking by ID.
Args:
@@ -50,25 +51,14 @@ async def read_booking(
HTTPException: If the booking is not found or a database error occurs.
Returns:
The booking response model.
The booking model.
"""
logger.debug(f"Received request to fetch booking with id: {booking_id}")
try:
booking = await get_booking_func(session, booking_id)
logger.info(f"Successfully retrieved booking with id: {booking_id}")
invitees = getattr(booking, "invitees", [])
if invitees and hasattr(invitees[0], "user_email"):
invitees = [i.user_email for i in invitees]
room = await get_room_func(session, booking.room_id)
room_response = RoomResponse.model_validate(room) if room else None
booking_response = BookingResponse.model_validate(
{
**booking.__dict__,
"invitees": invitees,
"room": room_response,
}
)
return booking_response
# Optionally, enrich booking object here if needed, but return the model object
return booking
except NoResultFound as err:
logger.warning(f"Booking with id {booking_id} not found")
raise HTTPException(
@@ -100,7 +90,7 @@ async def create_booking(
session: DBSession,
new_booking_func: NewBookingService,
get_room_func: GetRoomService,
) -> BookingResponse:
) -> Booking:
"""Create a new booking.
Args:
@@ -113,16 +103,16 @@ async def create_booking(
HTTPException: If a validation error, database error, or unexpected error occurs.
Returns:
The created booking response model.
The created booking model.
"""
logger.debug("Received request to create a new booking")
try:
# Service handles all DB/session logic and returns a ready-to-serialize response
booking_response = await new_booking_func(
# Service handles all DB/session logic and returns the model object
created_booking = await new_booking_func(
session, booking, publish_room_availability_event
)
return BookingResponse.model_validate(booking_response)
return created_booking
except ValueError as err:
logger.warning(f"Validation error while creating booking: {str(err)}")
# Detect overlap/conflict error
@@ -158,8 +148,8 @@ async def update_existing_booking(
session: DBSession,
update_booking_func: UpdateBookingService,
get_room_func: GetRoomService,
) -> BookingResponse:
"""Update an existing booking and return the updated BookingResponse.
) -> Booking:
"""Update an existing booking and return the updated Booking.
Args:
booking_id: The ID of the booking to update.
@@ -172,7 +162,7 @@ async def update_existing_booking(
HTTPException: If a validation error, database error, or unexpected error occurs.
Returns:
The updated booking response model.
The updated booking model.
"""
logger.debug(f"Received request to update booking with id: {booking_id}")
try:
@@ -186,20 +176,8 @@ async def update_existing_booking(
if v is not None
},
)
room = await get_room_func(session, updated_booking.room_id)
room_response = RoomResponse.model_validate(room) if room else None
invitees = getattr(updated_booking, "invitees", [])
if invitees and hasattr(invitees[0], "user_email"):
invitees = [i.user_email for i in invitees]
booking_response = BookingResponse.model_validate(
{
**updated_booking.__dict__,
"invitees": invitees,
"room": room_response,
}
)
logger.info(f"Successfully updated booking with id: {booking_id}")
return booking_response
return updated_booking
except NoResultFound as err:
logger.error(f"Booking with id {booking_id} not found: {err}")
raise HTTPException(status_code=404, detail="Booking not found") from err
@@ -285,7 +263,7 @@ async def read_bookings_for_month(
session: DBSession,
get_bookings_for_month_func: GetBookingsForMonthService,
get_room_func: GetRoomService,
) -> list[BookingResponse]:
) -> BookingList:
"""Retrieve all bookings for all rooms for a given month.
Args:
@@ -298,27 +276,14 @@ async def read_bookings_for_month(
HTTPException: If a database error or unexpected error occurs.
Returns:
List of booking response models for the specified month.
List of booking models for the specified month.
"""
try:
bookings = await get_bookings_for_month_func(session, month)
booking_responses: list[BookingResponse] = []
for booking in bookings:
room = await get_room_func(session, booking.room_id)
room_response = RoomResponse.model_validate(room) if room else None
invitees = getattr(booking, "invitees", [])
booking_response = BookingResponse.model_validate(
{
**booking.__dict__,
"invitees": invitees,
"room": room_response,
}
)
booking_responses.append(booking_response)
logger.info(
f"Successfully retrieved {len(booking_responses)} bookings for month: {month}"
f"Successfully retrieved {len(bookings)} bookings for month: {month}"
)
return booking_responses
return bookings
except Exception as err:
logger.exception(f"Error fetching bookings for month {month}: {str(err)}")
raise HTTPException(

View File

@@ -129,7 +129,7 @@ async def read_room(
*,
session: DBSession,
get_room_func: GetRoomService,
) -> RoomResponse:
) -> Room:
"""Retrieve a single room by ID.
Args:
@@ -138,7 +138,7 @@ async def read_room(
get_room_func: Service to get a room.
Returns:
RoomResponse for the requested room.
Room for the requested room.
Raises:
HTTPException: If the room is not found or a database error occurs.
@@ -147,7 +147,7 @@ async def read_room(
try:
room = await get_room_func(session, room_id)
logger.info(f"Successfully retrieved room with id: {room_id}")
return RoomResponse.model_validate(room)
return room
except NoResultFound as err:
logger.warning(f"Room not found: {str(err)}")
raise HTTPException(status_code=404, detail="Room not found") from err
@@ -211,7 +211,7 @@ async def create_room(
*,
session: DBSession,
new_room_func: NewRoomService,
) -> RoomResponse:
) -> Room:
"""Create a new room.
Args:
@@ -230,7 +230,7 @@ async def create_room(
db_room = Room(**room.model_dump())
created_room = await new_room_func(session, db_room)
logger.info(f"Successfully created room with id: {created_room.id}")
return RoomResponse.model_validate(created_room)
return created_room
except NoResultFound as err:
logger.warning(f"Room not found: {str(err)}")
raise HTTPException(status_code=404, detail="Room not found") from err
@@ -255,7 +255,7 @@ async def patch_room(
*,
session: DBSession,
update_room_func: UpdateRoomService,
) -> RoomResponse:
) -> Room:
"""Update an existing room by ID.
Args:
@@ -277,7 +277,7 @@ async def patch_room(
update_data.pop("id")
updated_room = await update_room_func(session, room_id, **update_data)
logger.info(f"Successfully updated room with id: {room_id}")
return RoomResponse.model_validate(updated_room)
return updated_room
except ValueError as err:
logger.warning(str(err))
raise HTTPException(status_code=400, detail=str(err)) from err

View File

@@ -45,7 +45,7 @@ async def read_users(
try:
users = await get_users_func(session)
logger.info(f"Successfully retrieved {len(users)} users")
return list(users)
return users
except NoResultFound as err:
logger.warning(f"User not found: {str(err)}")
raise HTTPException(status_code=404, detail="User not found") from err