From e9ec10809401f161d5ea7bd2ec2f40e762670c57 Mon Sep 17 00:00:00 2001 From: Will Sams Date: Fri, 22 May 2026 13:01:46 -0400 Subject: [PATCH 1/4] Fix backend bugs and dead code from follow-up review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix NameError in get_reservation_resolver when DbSession raises - Fix fetch_reservation and delete_reservation type hints (id builtin → int) - Remove unreachable else branch in delete_reservation - Fix getReservation schema argument (roomId → id) - Replace deprecated datetime.utcnow() with datetime.now(timezone.utc) - Remove unused create_refresh_token - Add default for ENV and startup validation for required secrets - Fix convert_to_local_date_from_str dropping timezone conversion on naive datetime - Remove unused find_by_id spec helper and normalize test method naming --- backend/schema.graphql | 2 +- .../resolvers/when_cancelling_reservations.py | 9 +-------- backend/src/api/resolvers/data.py | 14 +++++--------- backend/src/api/resolvers/queries.py | 3 +-- backend/src/api/utils.py | 3 +-- backend/src/auth.py | 19 ++++++------------- backend/src/settings.py | 13 ++++++++++++- 7 files changed, 27 insertions(+), 36 deletions(-) diff --git a/backend/schema.graphql b/backend/schema.graphql index d058613..c66c0da 100644 --- a/backend/schema.graphql +++ b/backend/schema.graphql @@ -49,7 +49,7 @@ type RoomsResult { } type Query { - getReservation(roomId: String!): ReservationResult! + getReservation(id: ID!): ReservationResult! getAllReservations: ReservationsResult! getAllRooms: RoomsResult! getAvailableRooms(input: AvailableRoomInput!): RoomsResult! diff --git a/backend/specs/resolvers/when_cancelling_reservations.py b/backend/specs/resolvers/when_cancelling_reservations.py index 262b6b7..45dd1e3 100644 --- a/backend/specs/resolvers/when_cancelling_reservations.py +++ b/backend/specs/resolvers/when_cancelling_reservations.py @@ -10,13 +10,6 @@ class DescribeDeleteReservationResolver: - def find_by_id(self, reservation_id): - return [ - reservation - for reservation in self.reservations - if reservation.id != reservation_id - ] - @pytest.mark.asyncio async def should_delete_existing_reservation(self, mocker): original_reservations = [ @@ -60,7 +53,7 @@ async def should_delete_existing_reservation(self, mocker): assert result["reservations"] == expected_reservations @pytest.mark.asyncio - async def test_should_give_reservation_not_found(self, mocker): + async def should_give_reservation_not_found(self, mocker): result = await delete_reservation_resolver( None, MOCK_EXECUTION_CONTEXT, reservationId=999 ) diff --git a/backend/src/api/resolvers/data.py b/backend/src/api/resolvers/data.py index 93b3a76..fc563d5 100644 --- a/backend/src/api/resolvers/data.py +++ b/backend/src/api/resolvers/data.py @@ -36,14 +36,10 @@ async def create_reservation( return reservations -async def delete_reservation(db, reservation_id: str) -> Dict[str, Any]: - reservation = await fetch_reservation(db, reservation_id) - if reservation: - await db.execute("DELETE FROM reservations WHERE id = $1", reservation_id) - return await fetch_all_rows(db, Reservation) - else: - errors = ["Reservation not found"] - return {"success": False, "errors": errors, "reservations": None} +async def delete_reservation(db, reservation_id: int) -> Dict[str, Any]: + await fetch_reservation(db, reservation_id) + await db.execute("DELETE FROM reservations WHERE id = $1", reservation_id) + return await fetch_all_rows(db, Reservation) async def is_room_available( @@ -116,7 +112,7 @@ async def fetch_room(db, room_id: str) -> Room: raise ValueError(f"Room with id {room_id} not found") -async def fetch_reservation(db, reservation_id: id): +async def fetch_reservation(db, reservation_id: int): result = await fetch_by_id(db, Reservation, id=reservation_id) if result["success"]: return result diff --git a/backend/src/api/resolvers/queries.py b/backend/src/api/resolvers/queries.py index e9c0435..c073aef 100644 --- a/backend/src/api/resolvers/queries.py +++ b/backend/src/api/resolvers/queries.py @@ -7,9 +7,8 @@ async def get_reservation_resolver(obj, info, id) -> Dict[str, Any]: + db = await DbSession() try: - db = await DbSession() - result = await fetch_reservation(db, id) return result except ValueError as error: diff --git a/backend/src/api/utils.py b/backend/src/api/utils.py index 81c650f..f857121 100644 --- a/backend/src/api/utils.py +++ b/backend/src/api/utils.py @@ -32,8 +32,7 @@ def convert_to_local_date(dt_utc: datetime) -> datetime: def convert_to_local_date_from_str(date_str: str) -> datetime: - dt_utc = datetime.strptime(date_str, "%Y-%m-%d") - return dt_utc.astimezone(timezone.utc).replace(tzinfo=None) + return datetime.strptime(date_str, "%Y-%m-%d") def get_calling_function_name() -> str: diff --git a/backend/src/auth.py b/backend/src/auth.py index b1967fb..35c7d7c 100644 --- a/backend/src/auth.py +++ b/backend/src/auth.py @@ -1,20 +1,17 @@ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from jose import jwt -from settings import ( - ACCESS_TOKEN_EXPIRE_MINUTES, - ALGORITHM, - REFRESH_SECRET_KEY, - SECRET_KEY, -) +from settings import ACCESS_TOKEN_EXPIRE_MINUTES, ALGORITHM, SECRET_KEY def create_token(subject: str, secret_key: str, expires_delta: timedelta) -> str: if expires_delta is not None: - expires_at = datetime.utcnow() + expires_delta + expires_at = datetime.now(timezone.utc) + expires_delta else: - expires_at = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + expires_at = datetime.now(timezone.utc) + timedelta( + minutes=ACCESS_TOKEN_EXPIRE_MINUTES + ) to_encode = {"exp": expires_at, "sub": subject} encoded_jwt = jwt.encode(to_encode, secret_key, ALGORITHM) @@ -25,10 +22,6 @@ def create_access_token(subject: str, expires_delta: timedelta) -> str: return create_token(subject, str(SECRET_KEY), expires_delta) -def create_refresh_token(subject: str, expires_delta: timedelta) -> str: - return create_token(subject, str(REFRESH_SECRET_KEY), expires_delta) - - def verify_user(username: str, password: str): # for this example application, we are just going to hard-code this return username == "example-user" and password == "example-user" diff --git a/backend/src/settings.py b/backend/src/settings.py index 2217e14..b3fedc0 100644 --- a/backend/src/settings.py +++ b/backend/src/settings.py @@ -2,7 +2,7 @@ API_NAME = "Acme Hotel Reservation - Graphql API" API_PORT = getenv("RESERVATION_PORT") or 80 -ENV = getenv("ENV") +ENV = getenv("ENV", "development") DB_URL = getenv("PG_URL") IS_DEBUG = bool(int(getenv("IS_DEBUG", "0"))) or False @@ -17,3 +17,14 @@ SECRET_KEY = getenv("SECRET_KEY") REFRESH_SECRET_KEY = getenv("REFRESH_SECRET_KEY") + +_missing = [ + name + for name, val in [ + ("SECRET_KEY", SECRET_KEY), + ("REFRESH_SECRET_KEY", REFRESH_SECRET_KEY), + ] + if not val +] +if _missing: + raise RuntimeError(f"Missing required environment variables: {', '.join(_missing)}") From 883a5f4ae260af5eb05e6a2b6468445ff1c42c65 Mon Sep 17 00:00:00 2001 From: Will Sams Date: Fri, 22 May 2026 13:04:02 -0400 Subject: [PATCH 2/4] Fix test due to removed code --- backend/specs/when_authorizing.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/backend/specs/when_authorizing.py b/backend/specs/when_authorizing.py index 0d04bbd..4783d4a 100644 --- a/backend/specs/when_authorizing.py +++ b/backend/specs/when_authorizing.py @@ -3,8 +3,8 @@ import pytest from jose import jwt -from auth import create_access_token, create_refresh_token -from settings import ALGORITHM, REFRESH_SECRET_KEY, SECRET_KEY +from auth import create_access_token +from settings import ALGORITHM, SECRET_KEY class DescribeAuthorization: @@ -22,12 +22,3 @@ def should_create_access_token(self, subject, expires_delta): decoded_token = jwt.decode(generated_token, SECRET_KEY, algorithms=[ALGORITHM]) assert decoded_token["sub"] == subject - - def should_create_refresh_token(self, subject, expires_delta): - generated_token = create_refresh_token(subject, expires_delta) - assert generated_token is not None - - decoded_token = jwt.decode( - generated_token, REFRESH_SECRET_KEY, algorithms=[ALGORITHM] - ) - assert decoded_token["sub"] == subject From 0c330c3619783dbd53c138b2c34e99608b078fca Mon Sep 17 00:00:00 2001 From: Will Sams Date: Fri, 22 May 2026 13:11:53 -0400 Subject: [PATCH 3/4] Resolve changes to data.py --- backend/src/api/resolvers/data.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/api/resolvers/data.py b/backend/src/api/resolvers/data.py index fc563d5..221bf6c 100644 --- a/backend/src/api/resolvers/data.py +++ b/backend/src/api/resolvers/data.py @@ -76,11 +76,8 @@ async def fetch_all_rows(db, entity_type) -> Dict[str, Any]: table_name = entity_type.__name__.lower() + "s" query = f"SELECT * FROM {table_name}" rows = await db.fetch(query) - if rows: - entities = [entity_type(**dict(row)) for row in rows] - return {"success": True, f"{table_name}": entities} - else: - raise ValueError("No reserved rooms found") + entities = [entity_type(**dict(row)) for row in rows] if rows else [] + return {"success": True, f"{table_name}": entities} async def fetch_by_id(db, entity_type, id) -> Dict[str, Any]: @@ -95,11 +92,14 @@ async def fetch_by_id(db, entity_type, id) -> Dict[str, Any]: async def fetch_available_rooms(db, checkin_date, checkout_date) -> Dict[str, Any]: - rooms = await fetch_all_rows(db, Room) + result = await fetch_all_rows(db, Room) + rooms = result.get("rooms", []) available_rooms = [ room for room in rooms - if await is_room_available(db, room.id, checkin_date, checkout_date) + if (await is_room_available(db, room.id, checkin_date, checkout_date))[ + "success" + ] ] return {"success": True, "rooms": available_rooms} From 7369b6c639ee7c7c84f66a4316d4ac7b99476a27 Mon Sep 17 00:00:00 2001 From: Will Sams Date: Fri, 22 May 2026 13:22:05 -0400 Subject: [PATCH 4/4] Omit need for SECRET_KEY and REFRESH_SECRET_KEY for tests --- backend/src/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/settings.py b/backend/src/settings.py index c8d2ed4..0bcce5a 100644 --- a/backend/src/settings.py +++ b/backend/src/settings.py @@ -23,5 +23,5 @@ ] if not val ] -if _missing: +if _missing and ENV != "test": raise RuntimeError(f"Missing required environment variables: {', '.join(_missing)}")