async def get_room_by_id_minimal(room_id: int, user_id: int, config: Config) -> Dict: """ Get data for a room by id, redacting secret information like the room code """ try: async with config.get_database_connection() as db: room_persistence = rooms.RoomPersistence(db) room = await room_persistence.get_room(room_id) if not room or not room.get("active"): raise err.not_found(f"No active room with id {room_id}") user_in_room = await is_user_in_room(user_id, room_id, db, room=room) return { "room_id": room.get("room_id"), "owner_id": room.get("owner_id"), "room_name": room.get("room_name"), "has_code": bool(room.get("room_code")), "user_in_room": user_in_room } except Exception as e: logger.exception("Failed to retrieve minimal data for room %s: %s", room_id, e) raise e
async def find_room(query: Dict, config: Config) -> Dict: """get a room's ID using either the owner's ID or the room ID in the case of supplying the latter, this simply verifies that a room with that ID exists""" if "owner_id" in query: resource_name = "owner_id" query_method = rooms.RoomPersistence.get_room_by_owner elif "room_id" in query: resource_name = "room_id" query_method = rooms.RoomPersistence.get_room else: raise err.bad_request( "Supply either a room_id or an owner_id to search for") try: resource_id = int(query[resource_name]) except ValueError: raise err.bad_request( f"'{query[resource_name]}' is not a valid {resource_name}") try: async with config.get_database_connection() as db: room_persistence = rooms.RoomPersistence(db) room = await query_method(room_persistence, resource_id) if not room or not room.get("active"): raise err.not_found( f"No active rooms found for {resource_name} {resource_id}") else: return {"room_id": room["room_id"]} except HTTPException: raise except Exception as e: logger.exception("Failed to find room with query %s: %s", query, e) raise e
async def deactivate_room(user_id: int, room_id: int, config: Config) -> Dict: """Process a request from an owner to deactivate an owned room""" try: async with config.get_database_connection() as db: room_persistence = rooms.RoomPersistence(db) room = await room_persistence.get_room(room_id) if not room or not room.get("active"): raise err.not_found(f"Active room with id {room_id} not found") if room["owner_id"] != user_id: raise err.forbidden( "User is not permitted to deactivate this room") await room_persistence.deactivate_room(room_id) return { "success": True, "message": "Successfully deactivated the room" } except HTTPException: raise except Exception as e: logger.exception("Failed to deactivate rooms for user %s: %s", user_id, e) raise e
async def join_room(user_id: int, room_id: int, room_code: Optional[str], config: Config) -> Dict: """Process a request from a user to join a room""" try: async with config.get_database_connection() as db: room_persistence = rooms.RoomPersistence(db) room = await room_persistence.get_room(room_id) if not room or not room.get("active"): raise err.not_found(f"Active room with id {room_id} not found") if await is_user_in_room(user_id, room_id, db, room=room): return {"success": True, "message": "User is already in room"} if room.get("room_code"): if room["room_code"] != room_code: return {"success": False, "message": "Invalid room code"} await room_persistence.add_user_to_room(room_id, user_id) return {"success": True, "message": "Successfully joined the room"} except HTTPException: raise except Exception as e: logger.exception("Failed to get rooms for user %s: %s", user_id, e) raise e
async def get_owned_room_for_user(user_id: int, config: Config) -> Dict: try: async with config.get_database_connection() as db: room_persistence = rooms.RoomPersistence(db) room = await room_persistence.get_room_by_owner(user_id) return room except Exception as e: logger.exception("Failed to create room for user %s: %s", user_id, e) raise e
async def get_joined_rooms_for_user(user_id: int, config: Config) -> Dict: """Get rooms which the user is a member of""" try: async with config.get_database_connection() as db: room_persistence = rooms.RoomPersistence(db) joined_rooms = await room_persistence.get_joined_rooms_by_user( user_id) return {"rooms": joined_rooms} except Exception as e: logger.exception("Failed to get rooms for user %s: %s", user_id, e) raise e
async def test_deactivate_removes_room_from_joined_for_owner(self): """test that deactivating a room stops it from being returned in rooms for the owner""" user_id = (await self.get_or_create_user())["user_id"] async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) new_room_id = await room_model.create_room(user_id, "test_room_code_2", "test_room_name_2") await room_model.deactivate_room(new_room_id) joined_rooms = await room_model.get_joined_rooms_by_user(user_id) self.assertFalse(any(room["room_id"] == new_room_id for room in joined_rooms))
async def test_deactivate_sets_active_false(self): """test that deactivating a room stops it from being active""" user_id = (await self.get_or_create_user())["user_id"] async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) new_room_id = await room_model.create_room(user_id, "test_room_code_2", "test_room_name_2") await room_model.deactivate_room(new_room_id) room = await room_model.get_room(new_room_id) self.assertFalse(room["active"])
async def test_user_not_in_room(self): """tests check_user_in_room does not assume new users are in rooms""" user_id = (await self.get_or_create_user())["user_id"] other_user = await self.random_new_user() async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) room_id = await room_model.create_room(user_id, "test_room_code", "test_room_name") is_user_in_room = await room_model.check_user_in_room(other_user["user_id"], room_id) self.assertFalse(is_user_in_room)
async def test_add_user_to_room(self): """tests adding a user to a room they do not own""" user_id = (await self.get_or_create_user())["user_id"] other_user = await self.random_new_user() async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) room_id = await room_model.create_room(user_id, "test_room_code", "test_room_name") await room_model.add_user_to_room(room_id, other_user["user_id"]) is_user_in_room = await room_model.check_user_in_room(other_user["user_id"], room_id) self.assertTrue(is_user_in_room)
async def test_get_room_by_owner_returns_active_room(self): """tests that getting a room by owner_id returns the most recent active room""" user_id = (await self.get_or_create_user())["user_id"] async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) existing_room_id = await room_model.create_room(user_id, "test_room_code", "test_room_name") new_room_id = await room_model.create_room(user_id, "test_room_code_2", "test_room_name_2") room = await room_model.get_room_by_owner(user_id) self.assertTrue(room["active"]) self.assertEqual(room["room_id"], new_room_id)
async def is_user_in_room(user_id: int, room_id: int, db: Connection, *, room: Optional[Dict] = None): """ Check if a user is in a room: the user is a room member or is the owner """ room_persistence = rooms.RoomPersistence(db) if room is None: room = room_persistence.get_room(room_id) if room is not None and room.get("owner_id") == user_id: return True return await room_persistence.check_user_in_room(user_id, room_id)
async def test_get_owned_rooms_multiple_members(self): """tests that getting a room for a user only returns each room once""" user_id = (await self.get_or_create_user())["user_id"] other_user = await self.random_new_user() other_user_2 = await self.random_new_user() async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) new_room_id = await room_model.create_room(user_id, "test_room_code_2", "test_room_name_2") await room_model.add_user_to_room(new_room_id, other_user["user_id"]) await room_model.add_user_to_room(new_room_id, other_user_2["user_id"]) await room_model.add_user_to_room(new_room_id, user_id) joined_rooms = await room_model.get_joined_rooms_by_user(user_id) self.assertEqual(len(joined_rooms), 1) self.assertEqual(joined_rooms[0]["owner_id"], user_id)
async def create_room(user_id: int, room_code: Optional[str], room_name: str, config: Config) -> Dict: try: # only users that have auth'd with Spotify may create rooms token_result = await spotify.get_valid_token_for_user(user_id, config) token = _handle_token_result(token_result) async with config.get_database_connection() as db: room_persistence = rooms.RoomPersistence(db) room_id = await room_persistence.create_room( user_id, room_code, room_name) logger.debug("Created room(id=%s) for user(id=%s)", room_id, user_id) created_room = await room_persistence.get_room(room_id) return created_room except Exception as e: logger.exception("Failed to create room for user %s: %s", user_id, e) raise e
async def test_remove_user_from_room(self): """tests that removing a user from a room results in the user not being in the room""" user_id = (await self.get_or_create_user())["user_id"] other_user = await self.random_new_user() async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) room_id = await room_model.create_room(user_id, "test_room_code", "test_room_name") await room_model.add_user_to_room(room_id, other_user["user_id"]) is_user_in_room = await room_model.check_user_in_room(other_user["user_id"], room_id) self.assertTrue(is_user_in_room) # remove the user from the room and recheck await room_model.remove_user_from_room(room_id, other_user["user_id"]) is_user_in_room = await room_model.check_user_in_room(other_user["user_id"], room_id) self.assertFalse(is_user_in_room)
async def me(user_id: int, config: Config) -> Dict: try: async with config.get_database_connection() as db: get_user = users.UsersPersistence(db).get_user_by_id(user_id) get_joined_rooms = rooms.RoomPersistence( db).get_joined_rooms_by_user(user_id) get_token = spotify.get_valid_token_for_user(user_id, config) (user, joined_rooms, token) = await asyncio.gather(get_user, get_joined_rooms, get_token, return_exceptions=False) except Exception as e: logger.exception("Failed to get user(id=%s) from db: %s", user_id, e) raise err.internal_server_error() del user["password_hash"] user["rooms"] = joined_rooms user["authed_with_spotify"] = isinstance(token, str) return user
async def test_create_room_no_existing_rooms(self): """test creating a room when the user has no existing rooms""" user_id = (await self.get_or_create_user())["user_id"] async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) room_id = await room_model.create_room(user_id, "test_room_code", "test_room_name") room_data = await room_model.get_room(room_id) expected_room_data = { "room_id": room_id, "active": True, "owner_id": user_id, "room_code": "test_room_code", "room_name": "test_room_name" } for key in expected_room_data: self.assertIn(key, room_data) self.assertEqual(expected_room_data[key], room_data[key])
async def get_room_for_user_assertive(room_id: int, user_id: int, db: Connection): """ helper method to get a room if the room exists, is active, and the user is a member of it raises HTTPException if any condition fails """ room_persistence = rooms.RoomPersistence(db) room = await room_persistence.get_room(room_id) if not room: raise err.not_found(f"No room with id {room_id}") user_in_room = await is_user_in_room(user_id, room_id, db, room=room) if not user_in_room: raise err.forbidden( f"User {user_id} is not a member of room {room_id}") elif not room.get("active"): raise err.not_found(f"No active room with id {room_id}") else: return room
async def test_create_room_deactivates_old_room(self): """test creating a room when the user has an existing rooms - the existing room should be deactivated""" user_id = (await self.get_or_create_user())["user_id"] async with aiosqlite.connect(self.db_name) as db: db.row_factory = aiosqlite.Row room_model = rooms.RoomPersistence(db) existing_room_id = await room_model.create_room(user_id, "test_room_code", "test_room_name_1") new_room_id = await room_model.create_room(user_id, "test_room_code_2","test_room_name_2") existing_room_data = await room_model.get_room(existing_room_id) self.assertFalse(existing_room_data["active"], "Existing room should have been deactivated") room_data = await room_model.get_room(new_room_id) expected_room_data = { "room_id": new_room_id, "active": True, "owner_id": user_id, "room_code": "test_room_code_2", "room_name": "test_room_name_2" } for key in expected_room_data: self.assertIn(key, room_data) self.assertEqual(expected_room_data[key], room_data[key])