From accfb6201a55d45d3fa5cfa468519b38ca492cac Mon Sep 17 00:00:00 2001 From: MarkParker5 Date: Tue, 21 Jun 2022 00:04:46 +0200 Subject: [PATCH] refactor and test rooms, init house with hub --- SmartHome/API/__init__.py | 0 SmartHome/API/endpoints/house/HouseManager.py | 24 +++++-- SmartHome/API/endpoints/house/router.py | 2 +- SmartHome/API/endpoints/hub/HubManager.py | 7 +- SmartHome/API/endpoints/room/RoomsManager.py | 71 ++++++++++++------- SmartHome/API/endpoints/room/router.py | 28 +++----- SmartHome/API/endpoints/room/schemas.py | 5 +- SmartHome/API/exceptions.py | 5 ++ SmartHome/tests/test_endpoints/test_rooms.py | 51 +++++++++++++ 9 files changed, 141 insertions(+), 52 deletions(-) create mode 100644 SmartHome/API/__init__.py create mode 100644 SmartHome/tests/test_endpoints/test_rooms.py diff --git a/SmartHome/API/__init__.py b/SmartHome/API/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/SmartHome/API/endpoints/house/HouseManager.py b/SmartHome/API/endpoints/house/HouseManager.py index 741ca33..f2491df 100644 --- a/SmartHome/API/endpoints/house/HouseManager.py +++ b/SmartHome/API/endpoints/house/HouseManager.py @@ -1,6 +1,8 @@ from uuid import UUID from fastapi import Depends +from sqlalchemy import select, delete +from sqlalchemy.ext.asyncio import AsyncSession from SmartHome.API.models import House from SmartHome.API.dependencies import database @@ -8,9 +10,23 @@ from . import schemas class HouseManager: - def __init__(self, session = Depends(database.get_session)): + session: AsyncSession + + def __init__(self, session = Depends(database.get_async_session)): self.session = session - def get(self) -> House: - db = self.session - return db.query(House).one() + async def get(self) -> House: + db: AsyncSession = self.session + result = await db.scalars(select(House)) + return result.first() + + async def create(self, house_id: UUID) -> House: + db: AsyncSession = self.session + + if house := await self.get(): + await db.delete(house) + + house = House(id = house_id) + db.add(house) + await db.commit() + return house diff --git a/SmartHome/API/endpoints/house/router.py b/SmartHome/API/endpoints/house/router.py index 408a857..20661fe 100644 --- a/SmartHome/API/endpoints/house/router.py +++ b/SmartHome/API/endpoints/house/router.py @@ -11,5 +11,5 @@ router = APIRouter( ) @router.get('', response_model = House) -async def house_get(manager: HouseManager = Depends()): +async def get_house(manager: HouseManager = Depends()): return manager.get() diff --git a/SmartHome/API/endpoints/hub/HubManager.py b/SmartHome/API/endpoints/hub/HubManager.py index 8bd52a3..f69f74d 100644 --- a/SmartHome/API/endpoints/hub/HubManager.py +++ b/SmartHome/API/endpoints/hub/HubManager.py @@ -2,7 +2,7 @@ from __future__ import annotations from uuid import UUID from fastapi import Depends -from sqlalchemy import select, update, delete +from sqlalchemy import select, update from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.exc import NoResultFound @@ -11,6 +11,7 @@ import config from API import exceptions from API.models import Hub from API.dependencies import database +from API import endpoints from . import schemas @@ -22,6 +23,9 @@ class HubManager: async def init(self, create_hub: schemas.HubInit) -> Hub: db: AsyncSession = self.session + house_manager = endpoints.house.HouseManager(db) + + await house_manager.create(create_hub.house_id) if hub := await self.get(): await db.delete(hub) @@ -30,7 +34,6 @@ class HubManager: db.add(hub) await db.commit() - await db.refresh(hub) return hub diff --git a/SmartHome/API/endpoints/room/RoomsManager.py b/SmartHome/API/endpoints/room/RoomsManager.py index eab7a34..3fe6a0b 100644 --- a/SmartHome/API/endpoints/room/RoomsManager.py +++ b/SmartHome/API/endpoints/room/RoomsManager.py @@ -1,44 +1,61 @@ from uuid import UUID from fastapi import Depends -from sqlalchemy import delete +from sqlalchemy import select, update, delete +from sqlalchemy.orm import selectinload +from sqlalchemy.ext.asyncio import AsyncSession -from SmartHome.API.models import Room -from SmartHome.API.dependencies import database +from API import exceptions +from API import endpoints +from API.models import Room +from API.dependencies import database from . import schemas class RoomsManager: - def __init__(self, session = Depends(database.get_session)): + session: AsyncSession + + def __init__(self, session = Depends(database.get_async_session)): self.session = session - def get(self, id: UUID) -> Room | None: - db = self.session - return db.get(Room, id) + # TODO: separate shallow get and deep fetch + async def get(self, id: UUID) -> Room | None: + db: AsyncSession = self.session + response = await db.scalars( + select(Room).where(Room.id == id).options(selectinload(Room.devices)) + ) + return response.first() - def create(self, create_room: schemas.PatchRoom) -> Room: - db = self.session - room = Room(name = create_room.name, house_id = create_room.house_id) + async def create(self, create_room: schemas.CreateRoom) -> Room: + db: AsyncSession = self.session + house = await endpoints.house.HouseManager(db).get() + + if not house: + raise exceptions.not_initialized + + room = Room(name = create_room.name, house_id = house.id, devices = []) db.add(room) - db.commit() - db.refresh(room) + await db.commit() + return room - def update(self, room: schemas.Room): - db = self.session + async def update(self, room: schemas.Room): + db: AsyncSession = self.session values = {key: value for key, value in room.dict().items() if key != 'id'} - db.execute(Room.__table__.update().values(values).filter_by(id = room.id)) - db.commit() + await db.execute(update(Room).values(values).where(Room.id == room.id)) + await db.commit() - def patch(self, id: UUID, room: schemas.PatchRoom): - db = self.session - values = {key: value for key, value in room.dict().items() if key != 'id' and value != None} - db.execute(Room.__table__.update().values(values).filter_by(id = id)) - db.commit() + async def patch(self, id: UUID, room: schemas.PatchRoom): + db: AsyncSession = self.session + values = {key: value for key, value in room.dict().items() if value != None} + await db.execute(update(Room).values(**values)) + await db.commit() - def delete(self, room_id: UUID): - db = self.session - room = self.get(room_id) - if room and room.house.owner_id == self.owner_id: - db.delete(room) - db.commit() + async def delete(self, room_id: UUID): + db: AsyncSession = self.session + room = await self.get(room_id) + if room: #TODO: and room.house.owner_id == self.user_id: + await db.delete(room) + await db.commit() + else: + raise exceptions.not_found diff --git a/SmartHome/API/endpoints/room/router.py b/SmartHome/API/endpoints/room/router.py index 70e3f10..708e23d 100644 --- a/SmartHome/API/endpoints/room/router.py +++ b/SmartHome/API/endpoints/room/router.py @@ -2,7 +2,7 @@ from uuid import UUID from fastapi import APIRouter, Depends from SmartHome.API import exceptions from .RoomsManager import RoomsManager -from .schemas import Room, PatchRoom, PatchRoom +from .schemas import Room, CreateRoom, PatchRoom router = APIRouter( @@ -10,27 +10,21 @@ router = APIRouter( tags = ['room'], ) -@router.get('/{id}', response_model = Room) -async def room_get(id: UUID, manager: RoomsManager = Depends()): - room = manager.get(id) +@router.post('', response_model = Room) +async def create_room(room: CreateRoom, manager: RoomsManager = Depends()): + return await manager.create(room) +@router.get('/{id}', response_model = Room) +async def get_room(id: UUID, manager: RoomsManager = Depends()): + room = await manager.get(id) if not room: raise exceptions.not_found - return room -@router.post('', response_model = Room) -async def room_create(room: PatchRoom, manager: RoomsManager = Depends()): - return manager.create(room) - -@router.put('') -async def room_put(room: Room, manager: RoomsManager = Depends()): - manager.update(room) - @router.patch('/{id}') -async def room_patch(id: UUID, room: PatchRoom, manager: RoomsManager = Depends()): - manager.patch(id, room) +async def patch_room(id: UUID, room: PatchRoom, manager: RoomsManager = Depends()): + await manager.patch(id, room) @router.delete('/{id}') -async def room_delete(id: UUID, manager: RoomsManager = Depends()): - manager.delete(id) +async def delete_room(id: UUID, manager: RoomsManager = Depends()): + await manager.delete(id) diff --git a/SmartHome/API/endpoints/room/schemas.py b/SmartHome/API/endpoints/room/schemas.py index f0e7202..e08f22e 100644 --- a/SmartHome/API/endpoints/room/schemas.py +++ b/SmartHome/API/endpoints/room/schemas.py @@ -3,9 +3,12 @@ from pydantic import BaseModel from ..device.schemas import Device -class PatchRoom(BaseModel): +class CreateRoom(BaseModel): name: str +class PatchRoom(CreateRoom): + pass + class Room(BaseModel): id: UUID name: str diff --git a/SmartHome/API/exceptions.py b/SmartHome/API/exceptions.py index e31ee3a..4836ae5 100644 --- a/SmartHome/API/exceptions.py +++ b/SmartHome/API/exceptions.py @@ -27,3 +27,8 @@ already_exist = HTTPException( status_code = 400, detail = 'Resource already exist' ) + +not_initialized = HTTPException( + status_code = 400, + detail = 'Hub not initialized' +) diff --git a/SmartHome/tests/test_endpoints/test_rooms.py b/SmartHome/tests/test_endpoints/test_rooms.py new file mode 100644 index 0000000..32d8ad0 --- /dev/null +++ b/SmartHome/tests/test_endpoints/test_rooms.py @@ -0,0 +1,51 @@ +from tests.setup import * + + +id = uuid1() +room = { + 'name': 'New Room', + 'devices': [] +} + +def test_get_room_null(): + response = client.get(f'/api/room/') + assert response.status_code == 405 + assert response.json() == {'detail': 'Method Not Allowed'} + +def test_get_room_404(): + response = client.get(f'/api/room/{id}') + assert response.status_code == 404 + assert response.json() == {'detail': 'Not found'} + +def test_delete_room_404(): + response = client.delete(f'/api/room/{id}') + assert response.status_code == 404 + assert response.json() == {'detail': 'Not found'} + +def test_create_room(): + response = client.post(f'/api/room', json = room) + new_room = response.json() + assert response.status_code == 200 + global id + id = new_room.get('id') + assert id + room['id'] = id + assert new_room == room + +def test_get_room(): + response = client.get(f'/api/room/{id}') + assert response.status_code == 200, id + assert response.json() == room + +def test_patch_room(): + room['name'] = 'Patched Room' + response = client.patch(f'/api/room/{id}', json = room) + assert response.status_code == 200 + assert client.get(f'/api/room/{id}').json() == room + +def test_delete_room(): + response = client.delete(f'/api/room/{id}', json = room) + assert response.status_code == 200 + +def test_get_deleted_room(): + test_get_room_404()