Browse Source

migrate to sync connection

tripeur 2 years ago
parent
commit
ef94fddfdb

+ 9 - 11
alembic/env.py

@@ -2,7 +2,6 @@ import asyncio
 from logging.config import fileConfig
 
 from sqlalchemy import engine_from_config, pool
-from sqlalchemy.ext.asyncio import AsyncEngine
 
 from alembic import context
 from app.core import config as app_config
@@ -78,16 +77,15 @@ async def run_migrations_online():
     configuration = config.get_section(config.config_ini_section)
     assert configuration
     configuration["sqlalchemy.url"] = get_database_uri()
-    connectable = AsyncEngine(
-        engine_from_config(
-            configuration,
-            prefix="sqlalchemy.",
-            poolclass=pool.NullPool,
-            future=True,
-        )  # type: ignore
-    )
-    async with connectable.connect() as connection:
-        await connection.run_sync(do_run_migrations)
+    connectable = engine_from_config(
+        configuration,
+        prefix="sqlalchemy.",
+        poolclass=pool.NullPool,
+        future=True,
+    )  # type: ignore
+
+    with connectable.connect() as connection:
+        do_run_migrations(connection)
 
 
 if context.is_offline_mode():

+ 8 - 7
app/api/deps.py

@@ -1,26 +1,27 @@
 import time
 from collections.abc import AsyncGenerator
+from typing import Generator
 
 import jwt
 from fastapi import Depends, HTTPException, status
 from fastapi.security import OAuth2PasswordBearer
 from sqlalchemy import select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 
 from app.core import config, security
-from app.core.session import async_session
+from app.core.session import session
 from app.models import User
 
 reusable_oauth2 = OAuth2PasswordBearer(tokenUrl="auth/access-token")
 
 
-async def get_session() -> AsyncGenerator[AsyncSession, None]:
-    async with async_session() as session:
-        yield session
+def get_session() -> Generator[Session, None, None]:
+    with session() as db:
+        yield db
 
 
 async def get_current_user(
-    session: AsyncSession = Depends(get_session), token: str = Depends(reusable_oauth2)
+    session: Session = Depends(get_session), token: str = Depends(reusable_oauth2)
 ) -> User:
     try:
         payload = jwt.decode(
@@ -46,7 +47,7 @@ async def get_current_user(
             detail="Could not validate credentials, token expired or not yet valid",
         )
 
-    result = await session.execute(select(User).where(User.id == token_data.sub))
+    result = session.execute(select(User).where(User.id == token_data.sub))
     user = result.scalars().first()
 
     if not user:

+ 5 - 5
app/api/endpoints/auth.py

@@ -5,7 +5,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
 from fastapi.security import OAuth2PasswordRequestForm
 from pydantic import ValidationError
 from sqlalchemy import select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 
 from app.api import deps
 from app.core import config, security
@@ -18,12 +18,12 @@ router = APIRouter()
 
 @router.post("/access-token", response_model=AccessTokenResponse)
 async def login_access_token(
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
     form_data: OAuth2PasswordRequestForm = Depends(),
 ):
     """OAuth2 compatible token, get an access token for future requests using username and password"""
 
-    result = await session.execute(select(User).where(User.email == form_data.username))
+    result = session.execute(select(User).where(User.email == form_data.username))
     user = result.scalars().first()
 
     if user is None:
@@ -38,7 +38,7 @@ async def login_access_token(
 @router.post("/refresh-token", response_model=AccessTokenResponse)
 async def refresh_token(
     input: RefreshTokenRequest,
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """OAuth2 compatible token, get an access token for future requests using refresh token"""
     try:
@@ -68,7 +68,7 @@ async def refresh_token(
             detail="Could not validate credentials, token expired or not yet valid",
         )
 
-    result = await session.execute(select(User).where(User.id == token_data.sub))
+    result = session.execute(select(User).where(User.id == token_data.sub))
     user = result.scalars().first()
 
     if user is None:

+ 40 - 43
app/api/endpoints/project.py

@@ -4,7 +4,7 @@ from uuid import UUID
 from fastapi import APIRouter, Depends, HTTPException
 from sqlalchemy import delete, select
 from sqlalchemy.exc import IntegrityError
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 
 from app.api import deps
 from app.models import Project, Slot, Sms, User
@@ -21,19 +21,19 @@ router = APIRouter()
 @router.get("/projects", response_model=list[ProjectListResponse])
 async def list_project(
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Get project_list"""
-    results = await session.execute(select(Project))
+    results = session.execute(select(Project))
     return results.scalars().all()
 
 
 @router.get("/public-projects", response_model=list[ProjectListResponse])
 async def list_public_project(
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Get the list of public projects"""
-    results = await session.execute(select(Project).where(Project.is_public == True))
+    results = session.execute(select(Project).where(Project.is_public == True))
     return results.scalars().all()
 
 
@@ -41,75 +41,72 @@ async def list_public_project(
 async def create_project(
     new_project: ProjectRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Create a new project"""
     project = Project(**new_project.dict())
     session.add(project)
     try:
-        await session.commit()
+        session.commit()
     except IntegrityError as e:
         raise HTTPException(422, "Project name already exist")
 
-    await session.refresh(project)
+    session.refresh(project)
 
     return ProjectResponse.from_orm(project)
 
 
-@router.get("/public-project/{id}", response_model=ProjectResponse)
+@router.get("/public-project/{project_id}", response_model=ProjectResponse)
 async def get_public_project(
-    id: UUID,
-    session: AsyncSession = Depends(deps.get_session),
+    project_id: UUID,
+    session: Session = Depends(deps.get_session),
 ):
     """Get a project that is public"""
-    result = await session.get(Project, id)
+    result = session.get(Project, project_id)
     if (result is None) or not result.is_public:
-        return HTTPException(status_code=404, detail="Project not found")
+        raise HTTPException(status_code=404, detail="Project not found")
     return result
 
 
-@router.get("/project/{id}", response_model=ProjectResponse)
+@router.get("/project/{project_id}", response_model=ProjectResponse)
 async def get_project(
-    id: UUID,
-    session: AsyncSession = Depends(deps.get_session),
+    project_id: UUID,
+    current_user: User = Depends(deps.get_current_user),
+    session: Session = Depends(deps.get_session),
 ):
     """Get a project"""
-    project = await session.get(Project, id)
+    project = session.get(Project, project_id)
     if project is None:
-        return HTTPException(status_code=404, detail="Project not found")
-    project.sms
-    project.volunteers
-    project.slots
-    response = ProjectResponse.from_orm(project)
-    return response
+        raise HTTPException(status_code=404, detail="Project not found")
+    return ProjectResponse.from_orm(project)
 
 
-@router.post("/project/{id}", response_model=ProjectListResponse)
+@router.post("/project/{project_id}", response_model=ProjectListResponse)
 async def update_project(
-    id: UUID,
+    project_id: UUID,
     edit_project: ProjectRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Edit project"""
-    p = await session.get(Project, id)
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
     p.name = edit_project.name
     p.is_public = edit_project.is_public
-    await session.commit()
+    session.commit()
     return p
 
 
-@router.post("/project/{id}/import-gsheet", response_model=ProjectResponse)
+@router.post("/project/{project_id}/import-gsheet", response_model=ProjectResponse)
 async def update_project_from_gsheet(
-    id: UUID,
+    project_id: UUID,
     gsheet: ProjectImportGsheetRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Edit project name"""
-    p = await session.get(Project, id)
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
     url = gsheet.sheet_url
@@ -117,12 +114,12 @@ async def update_project_from_gsheet(
     return p
 
 
-@router.post("/project/{id}/create-all-sms", response_model=list[SMSResponse])
+@router.post("/project/{project_id}/create-all-sms", response_model=list[SMSResponse])
 async def create_sms_batch(
-    id: UUID,
+    project_id: UUID,
     sms_batch: ProjectSMSBatchRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Create SMS based on a template and the list of slots and volunteer associated to the project
 
@@ -135,11 +132,11 @@ async def create_sms_batch(
      - {name} volunteer.surname
 
     """
-    p = await session.get(Project, id)
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
     # Get all slots
-    slots = await session.execute(select(Slot).where(Slot.project_id == id))
+    slots = session.execute(select(Slot).where(Slot.project_id == project_id))
     sms_list = []
     for slot in slots.scalars():
         # Replace the slot placeholder by their value
@@ -156,7 +153,7 @@ async def create_sms_batch(
                 "{prenom}", volunteer.name
             ).replace("{nom}", volunteer.surname)
             sms = Sms(
-                project_id=id,
+                project_id=project_id,
                 volunteer_id=volunteer.id,
                 content=personalized_content,
                 phone_number=volunteer.phone_number,
@@ -164,17 +161,17 @@ async def create_sms_batch(
             )
             sms_list.append(sms)
     session.add_all(sms_list)
-    await session.commit()
+    session.commit()
 
     return sms_list
 
 
-@router.delete("/project/{id}")
+@router.delete("/project/{project_id}")
 async def delete_project(
     id: UUID,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Delete project"""
-    await session.execute(delete(Project).where(Project.id == id))
-    await session.commit()
+    session.execute(delete(Project).where(Project.id == project_id))
+    session.commit()

+ 40 - 38
app/api/endpoints/slots.py

@@ -2,7 +2,7 @@ from uuid import UUID
 
 from fastapi import APIRouter, Depends, HTTPException
 from sqlalchemy import delete, select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 
 from app.api import deps
 from app.api.utils import update_object_from_payload, verify_id_list
@@ -22,30 +22,30 @@ from app.schemas.responses import SlotResponse
 router = APIRouter()
 
 
-@router.get("/project/{id}/slots", response_model=list[SlotResponse])
+@router.get("/project/{project_id}/slots", response_model=list[SlotResponse])
 async def list_project_slots(
-    id: UUID,
+    project_id: UUID,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """List slots from project"""
-    p = await session.get(Project, id)
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
 
-    results = await session.execute(select(Slot).where(Slot.project_id == id))
-    return results.scalars()
+    results = session.execute(select(Slot).where(Slot.project_id == project_id))
+    return results.scalars().all()
 
 
-@router.post("/project/{id}/slot", response_model=SlotResponse)
+@router.post("/project/{project_id}/slot", response_model=SlotResponse)
 async def create_slot(
-    id: UUID,
+    project_id: UUID,
     new_slot: SlotCreateRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Create a new slot to the project"""
-    p = await session.get(Project, id)
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
 
@@ -55,70 +55,72 @@ async def create_slot(
     if input_dict["volunteers"] is not None:
         volunteers = input_dict["volunteers"]
         await verify_id_list(
-            session, volunteers, id, Volunteer, "Invalid volunteer list"
+            session, volunteers, project_id, Volunteer, "Invalid volunteer list"
         )
     del input_dict["volunteers"]
 
-    slot = Slot(project_id=id, **input_dict)
+    slot = Slot(project_id=project_id, **input_dict)
     session.add(slot)
-    await session.commit()
+    session.commit()
     # Add the slot to the list of volunteer
-    await session.execute(
-        association_table_volunteer_slot.insert().values(
-            [(volunteer_id, slot.id) for volunteer_id in volunteers]
+    if len(volunteers) > 0:
+        session.execute(
+            association_table_volunteer_slot.insert().values(
+                [(volunteer_id, slot.id) for volunteer_id in volunteers]
+            )
         )
-    )
-    await session.commit()
+    session.commit()
     return slot
 
 
-@router.post("/project/{id}/slot/{slot_id}", response_model=SlotResponse)
+@router.post("/project/{project_id}/slot/{slot_id}", response_model=SlotResponse)
 async def update_slot(
-    id: UUID,
+    project_id: UUID,
     slot_id: UUID,
     new_slot: SlotUpdateRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Update a slot from the project"""
-    slot = await session.get(Slot, slot_id)
-    if (slot is None) or slot.project_id != id:
+    slot = session.get(Slot, slot_id)
+    if (slot is None) or (slot.project_id != str(project_id)):
         raise HTTPException(status_code=404, detail="Slot not found")
 
     input_dict = new_slot.dict()
     if input_dict["volunteers"] is not None:
         volunteers: list[UUID] = input_dict["volunteers"]
         await verify_id_list(
-            session, volunteers, id, Volunteer, "Invalid volunteer list"
+            session, volunteers, project_id, Volunteer, "Invalid volunteer list"
         )
-        await session.execute(
+        session.execute(
             association_table_volunteer_slot.delete().where(
                 association_table_volunteer_slot.c.slot_id == slot.id
             )
         )
-        await session.execute(
-            association_table_volunteer_slot.insert().values(
-                [(volunteer_id, slot.id) for volunteer_id in volunteers]
+        if len(volunteers) > 0:
+            session.execute(
+                association_table_volunteer_slot.insert().values(
+                    [(volunteer_id, slot.id) for volunteer_id in volunteers]
+                )
             )
-        )
     del input_dict["volunteers"]
 
     update_object_from_payload(slot, input_dict)
-    await session.commit()
-    await session.refresh(slot)
+    session.commit()
+    session.refresh(slot)
 
     return slot
 
 
-@router.delete("/project/{id}/slot/{slot_id}")
+@router.delete("/project/{project_id}/slot/{slot_id}")
 async def delete_slot(
-    id: UUID,
+    project_id: UUID,
     slot_id: UUID,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Delete a slot from the project"""
-    await session.execute(
-        delete(Slot).where((Slot.id == slot_id) & (Slot.project_id == id))
+    session.execute(
+        delete(Slot).where((Slot.id == slot_id) & (Slot.project_id == project_id))
     )
-    await session.commit()
+    session.commit()

+ 35 - 36
app/api/endpoints/sms.py

@@ -3,7 +3,7 @@ from uuid import UUID
 
 from fastapi import APIRouter, Depends, HTTPException
 from sqlalchemy import delete, select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 
 from app.api import deps
 from app.api.utils import update_object_from_payload
@@ -18,35 +18,35 @@ from app.schemas.responses import SMSResponse
 router = APIRouter()
 
 
-@router.get("/project/{id}/sms", response_model=list[SMSResponse])
+@router.get("/project/{project_id}/sms", response_model=list[SMSResponse])
 async def list_project_sms(
-    id: UUID,
+    project_id: UUID,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """List sms from project"""
-    p = await session.get(Project, id)
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
 
-    results = await session.execute(select(Sms).where(Sms.project_id == id))
-    return results.scalars()
+    results = session.execute(select(Sms).where(Sms.project_id == project_id))
+    return results.scalars().all()
 
 
-@router.post("/project/{id}/sms", response_model=list[SMSResponse])
+@router.post("/project/{project_id}/sms", response_model=SMSResponse)
 async def create_sms(
-    id: UUID,
+    project_id: UUID,
     new_sms: SmsCreateRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
-    """create a new to the project"""
-    p = await session.get(Project, id)
+    """Create a new to the project"""
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
-    sms = Sms(project_id=id, **new_sms.dict())
+    sms = Sms(project_id=project_id, **new_sms.dict())
     session.add(sms)
-    await session.commit()
+    session.commit()
     return sms
 
 
@@ -56,64 +56,63 @@ async def update_sms(
     sms_id: UUID,
     new_sms: SmsUpdateRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Create a new to the project"""
-    sms = await session.get(Sms, sms_id)
+    sms = session.get(Sms, sms_id)
     if (sms is None) or sms.project_id != project_id:
         raise HTTPException(status_code=404, detail="Sms not found")
     update_object_from_payload(sms, new_sms.dict())
-    await session.commit()
-    await session.refresh(sms)
+    session.commit()
+    session.refresh(sms)
     return sms
 
 
-@router.delete("/project/{id}/sms/{sms_id}")
+@router.delete("/project/{project_id}/sms/{sms_id}")
 async def delete_sms(
-    id: UUID,
+    project_id: UUID,
     sms_id: UUID,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Delete a sms from the project"""
-    await session.execute(
-        delete(Sms).where((Sms.id == sms_id) & (Sms.project_id == id))
+    session.execute(
+        delete(Sms).where((Sms.id == sms_id) & (Sms.project_id == str(project_id)))
     )
-    await session.commit()
+    session.commit()
 
 
 @router.get("/sms/to-send", response_model=list[SMSResponse])
 async def list_sms_to_send(
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
-    """List sms to"""
-    results = await session.execute(
+    """List sms that should be send by now"""
+    results = session.execute(
         select(Sms).where(
-            (Sms.sending_time < datetime.datetime.now()) & Sms.send_time.is_not(None)
+            (Sms.sending_time < datetime.datetime.now()) & (Sms.send_time == None)
         )
     )
-    return results.scalars()
+    return results.scalars().all()
 
 
 @router.get("/sms/not-send", response_model=list[SMSResponse])
 async def list_not_sent(
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """List sms that are not sent"""
-
-    results = await session.execute(select(Sms).where(Sms.send_time.is_not(None)))
-    return results.scalars()
+    results = session.execute(select(Sms).where((Sms.send_time == None)))
+    return results.scalars().all()
 
 
 @router.get("/sms/future", response_model=list[SMSResponse])
 async def list_future_sms(
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """List sms that should be sent in the future"""
-    results = await session.execute(
+    results = session.execute(
         select(Sms).where(Sms.sending_time > datetime.datetime.now())
     )
-    return results.scalars()
+    return results.scalars().all()

+ 9 - 9
app/api/endpoints/users.py

@@ -1,6 +1,6 @@
 from fastapi import APIRouter, Depends, HTTPException
 from sqlalchemy import delete, select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 
 from app.api import deps
 from app.core.security import get_password_hash
@@ -22,33 +22,33 @@ async def read_current_user(
 @router.delete("/me", status_code=204)
 async def delete_current_user(
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Delete current user"""
-    await session.execute(delete(User).where(User.id == current_user.id))
-    await session.commit()
+    session.execute(delete(User).where(User.id == current_user.id))
+    session.commit()
 
 
 @router.post("/reset-password", response_model=UserResponse)
 async def reset_current_user_password(
     user_update_password: UserUpdatePasswordRequest,
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
     current_user: User = Depends(deps.get_current_user),
 ):
     """Update current user password"""
     current_user.hashed_password = get_password_hash(user_update_password.password)
     session.add(current_user)
-    await session.commit()
+    session.commit()
     return current_user
 
 
 @router.post("/register", response_model=UserResponse)
 async def register_new_user(
     new_user: UserCreateRequest,
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Create new user"""
-    result = await session.execute(select(User).where(User.email == new_user.email))
+    result = session.execute(select(User).where(User.email == new_user.email))
     if result.scalars().first() is not None:
         raise HTTPException(status_code=400, detail="Cannot use this email address")
     user = User(
@@ -56,5 +56,5 @@ async def register_new_user(
         hashed_password=get_password_hash(new_user.password),
     )
     session.add(user)
-    await session.commit()
+    session.commit()
     return user

+ 37 - 33
app/api/endpoints/volunteers.py

@@ -2,7 +2,7 @@ from uuid import UUID
 
 from fastapi import APIRouter, Depends, HTTPException
 from sqlalchemy import delete, select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 from sqlalchemy.sql import func
 
 from app.api import deps
@@ -14,30 +14,32 @@ from app.schemas.responses import VolunteerResponse
 router = APIRouter()
 
 
-@router.get("/project/{id}/volunteers", response_model=list[VolunteerResponse])
+@router.get("/project/{project_id}/volunteers", response_model=list[VolunteerResponse])
 async def list_project_volunteers(
-    id: UUID,
+    project_id: UUID,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """List volunteers from project"""
-    p = await session.get(Project, id)
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
 
-    results = await session.execute(select(Volunteer).where(Volunteer.project_id == id))
-    return results.scalars()
+    results = session.execute(
+        select(Volunteer).where(Volunteer.project_id == project_id)
+    )
+    return results.scalars().all()
 
 
-@router.post("/project/{id}/volunteer", response_model=VolunteerResponse)
+@router.post("/project/{project_id}/volunteer", response_model=VolunteerResponse)
 async def create_volunteer(
-    id: UUID,
+    project_id: UUID,
     new_volunteer: VolunteerCreateRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Create a new volunteer to the project"""
-    p = await session.get(Project, id)
+    p = session.get(Project, project_id)
     if p is None:
         raise HTTPException(status_code=404, detail="Project not found")
     input_dict = new_volunteer.dict()
@@ -46,20 +48,21 @@ async def create_volunteer(
     slots: list[UUID] = []
     if input_dict["slots"] is not None:
         slots = input_dict["slots"]
-        await verify_id_list(session, slots, id, Slot, "Invalid slot list")
+        await verify_id_list(session, slots, project_id, Slot, "Invalid slot list")
     del input_dict["slots"]
 
-    volunteer = Volunteer(project_id=id, **input_dict)
+    volunteer = Volunteer(project_id=project_id, **input_dict)
     session.add(volunteer)
     # commit to optain an id for the volunteer
-    await session.commit()
+    session.commit()
 
-    await session.execute(
-        association_table_volunteer_slot.insert().values(
-            [(volunteer.id, slot_id) for slot_id in slots]
+    if len(slots) > 0:
+        session.execute(
+            association_table_volunteer_slot.insert().values(
+                [(volunteer.id, slot_id) for slot_id in slots]
+            )
         )
-    )
-    await session.commit()
+    session.commit()
     return volunteer
 
 
@@ -71,11 +74,11 @@ async def update_volunteer(
     volunteer_id: UUID,
     new_volunteer: VolunteerUpdateRequest,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Update a volunteer from the project"""
-    volunteer = await session.get(Volunteer, volunteer_id)
-    if (volunteer is None) or (volunteer.project_id != project_id):
+    volunteer = session.get(Volunteer, volunteer_id)
+    if (volunteer is None) or (volunteer.project_id != str(project_id)):
         raise HTTPException(status_code=404, detail="Volunteer not found")
 
     input_dict = new_volunteer.dict()
@@ -84,22 +87,23 @@ async def update_volunteer(
         slots: list[UUID] = input_dict["slots"]
         await verify_id_list(session, slots, project_id, Slot, "Invalid slot list")
         # Remove previous values
-        await session.execute(
+        session.execute(
             association_table_volunteer_slot.delete().where(
                 association_table_volunteer_slot.c.volunteer_id == volunteer.id
             )
         )
-        # Add the new
-        await session.execute(
-            association_table_volunteer_slot.insert().values(
-                [(volunteer.id, slot_id) for slot_id in slots]
+        # Add the new slots
+        if len(slots) > 0:
+            session.execute(
+                association_table_volunteer_slot.insert().values(
+                    [(volunteer.id, slot_id) for slot_id in slots]
+                )
             )
-        )
     del input_dict["slots"]
 
     update_object_from_payload(volunteer, input_dict)
-    await session.commit()
-    await session.refresh(volunteer)
+    session.commit()
+    session.refresh(volunteer)
     return volunteer
 
 
@@ -108,12 +112,12 @@ async def delete_volunteer(
     project_id: UUID,
     volunteer_id: UUID,
     current_user: User = Depends(deps.get_current_user),
-    session: AsyncSession = Depends(deps.get_session),
+    session: Session = Depends(deps.get_session),
 ):
     """Delete a volunteer from the project"""
-    await session.execute(
+    session.execute(
         delete(Volunteer).where(
             (Volunteer.id == volunteer_id) & (Volunteer.project_id == project_id)
         )
     )
-    await session.commit()
+    session.commit()

+ 3 - 3
app/api/utils.py

@@ -2,12 +2,12 @@ from uuid import UUID
 
 from fastapi import HTTPException
 from sqlalchemy import select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 from sqlalchemy.sql import func
 
 
 async def verify_id_list(
-    session: AsyncSession,
+    session: Session,
     id_list: list[UUID],
     project_id: UUID,
     ObjectClass,
@@ -29,7 +29,7 @@ async def verify_id_list(
     statement = select(func.count(ObjectClass.id)).where(
         ObjectClass.id.in_(id_list) & (ObjectClass.project_id == project_id)
     )
-    results = await session.execute(statement)
+    results = session.execute(statement)
     if results.scalar() != len(id_list):
         raise HTTPException(status_code=400, detail=error_message)
 

+ 2 - 2
app/core/config.py

@@ -70,7 +70,7 @@ class Settings(BaseSettings):
     @validator("DEFAULT_SQLALCHEMY_DATABASE_URI")
     def _assemble_default_db_connection(cls, v: str, values: dict[str, str]) -> str:
         return PostgresDsn.build(
-            scheme="postgresql+asyncpg",
+            scheme="postgresql",
             user=values["DEFAULT_DATABASE_USER"],
             password=values["DEFAULT_DATABASE_PASSWORD"],
             host=values["DEFAULT_DATABASE_HOSTNAME"],
@@ -81,7 +81,7 @@ class Settings(BaseSettings):
     @validator("TEST_SQLALCHEMY_DATABASE_URI")
     def _assemble_test_db_connection(cls, v: str, values: dict[str, str]) -> str:
         return PostgresDsn.build(
-            scheme="postgresql+asyncpg",
+            scheme="postgresql",
             user=values["TEST_DATABASE_USER"],
             password=values["TEST_DATABASE_PASSWORD"],
             host=values["TEST_DATABASE_HOSTNAME"],

+ 5 - 8
app/core/session.py

@@ -1,10 +1,8 @@
 """
-SQLAlchemy async engine and sessions tools
-
-https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html
+SQLAlchemy  engine and sessions tools
 """
-
-from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
 
 from app.core import config
 
@@ -13,6 +11,5 @@ if config.settings.ENVIRONMENT == "PYTEST":
 else:
     sqlalchemy_database_uri = config.settings.DEFAULT_SQLALCHEMY_DATABASE_URI
 
-
-async_engine = create_async_engine(sqlalchemy_database_uri, pool_pre_ping=True)
-async_session = async_sessionmaker(async_engine, expire_on_commit=False)
+engine = create_engine(sqlalchemy_database_uri)
+session = sessionmaker(autocommit=False, autoflush=False, bind=engine)

+ 5 - 5
app/initial_data.py

@@ -10,14 +10,14 @@ import asyncio
 from sqlalchemy import select
 
 from app.core import config, security
-from app.core.session import async_session
+from app.core.session import session
 from app.models import User
 
 
 async def main() -> None:
     print("Start initial data")
-    async with async_session() as session:
-        result = await session.execute(
+    with session() as db:
+        result = db.execute(
             select(User).where(User.email == config.settings.FIRST_SUPERUSER_EMAIL)
         )
         user = result.scalars().first()
@@ -29,8 +29,8 @@ async def main() -> None:
                     config.settings.FIRST_SUPERUSER_PASSWORD
                 ),
             )
-            session.add(new_superuser)
-            await session.commit()
+            db.add(new_superuser)
+            db.commit()
             print("Superuser was created")
         else:
             print("Superuser already exists in database")

+ 17 - 17
app/tests/conftest.py

@@ -1,14 +1,17 @@
 import asyncio
 from collections.abc import AsyncGenerator
+from typing import Generator
 
 import pytest
 import pytest_asyncio
 from httpx import AsyncClient
 from sqlalchemy import delete, select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 
 from app.core import config, security
-from app.core.session import async_engine, async_session
+from app.core.session import engine
+from app.core.session import session as session_maker
+
 from app.main import app
 from app.models import Base, User
 
@@ -35,20 +38,19 @@ async def test_db_setup_sessionmaker():
     assert config.settings.ENVIRONMENT == "PYTEST"
 
     # always drop and create test db tables between tests session
-    async with async_engine.begin() as conn:
-        await conn.run_sync(Base.metadata.drop_all)
-        await conn.run_sync(Base.metadata.create_all)
+    Base.metadata.drop_all(engine)
+    Base.metadata.create_all(engine)
 
 
 @pytest_asyncio.fixture(autouse=True)
-async def session(test_db_setup_sessionmaker) -> AsyncGenerator[AsyncSession, None]:
-    async with async_session() as session:
-        yield session
+async def session(test_db_setup_sessionmaker) -> AsyncGenerator[Session, None]:
+    with session_maker() as db:
+        yield db
 
         # delete all data from all tables after test
         for name, table in Base.metadata.tables.items():
-            await session.execute(delete(table))
-        await session.commit()
+            db.execute(delete(table))
+        db.commit()
 
 
 @pytest_asyncio.fixture(scope="session")
@@ -60,10 +62,8 @@ async def client() -> AsyncGenerator[AsyncClient, None]:
 
 @pytest_asyncio.fixture
 async def default_user(test_db_setup_sessionmaker) -> User:
-    async with async_session() as session:
-        result = await session.execute(
-            select(User).where(User.email == default_user_email)
-        )
+    with session_maker() as db:
+        result = db.execute(select(User).where(User.email == default_user_email))
         user = result.scalars().first()
         if user is None:
             new_user = User(
@@ -71,9 +71,9 @@ async def default_user(test_db_setup_sessionmaker) -> User:
                 hashed_password=default_user_password_hash,
             )
             new_user.id = default_user_id
-            session.add(new_user)
-            await session.commit()
-            await session.refresh(new_user)
+            db.add(new_user)
+            db.commit()
+            db.refresh(new_user)
             return new_user
         return user
 

+ 7 - 7
app/tests/test_users.py

@@ -1,6 +1,6 @@
 from httpx import AsyncClient
 from sqlalchemy import select
-from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.orm import Session
 
 from app.main import app
 from app.models import User
@@ -23,19 +23,19 @@ async def test_read_current_user(client: AsyncClient, default_user_headers):
 
 
 async def test_delete_current_user(
-    client: AsyncClient, default_user_headers, session: AsyncSession
+    client: AsyncClient, default_user_headers, session: Session
 ):
     response = await client.delete(
         app.url_path_for("delete_current_user"), headers=default_user_headers
     )
     assert response.status_code == 204
-    result = await session.execute(select(User).where(User.id == default_user_id))
+    result = session.execute(select(User).where(User.id == default_user_id))
     user = result.scalars().first()
     assert user is None
 
 
 async def test_reset_current_user_password(
-    client: AsyncClient, default_user_headers, session: AsyncSession
+    client: AsyncClient, default_user_headers, session: Session
 ):
     response = await client.post(
         app.url_path_for("reset_current_user_password"),
@@ -43,14 +43,14 @@ async def test_reset_current_user_password(
         json={"password": "testxxxxxx"},
     )
     assert response.status_code == 200
-    result = await session.execute(select(User).where(User.id == default_user_id))
+    result = session.execute(select(User).where(User.id == default_user_id))
     user = result.scalars().first()
     assert user is not None
     assert user.hashed_password != default_user_password_hash
 
 
 async def test_register_new_user(
-    client: AsyncClient, default_user_headers, session: AsyncSession
+    client: AsyncClient, default_user_headers, session: Session
 ):
     response = await client.post(
         app.url_path_for("register_new_user"),
@@ -61,6 +61,6 @@ async def test_register_new_user(
         },
     )
     assert response.status_code == 200
-    result = await session.execute(select(User).where(User.email == "qwe@example.com"))
+    result = session.execute(select(User).where(User.email == "qwe@example.com"))
     user = result.scalars().first()
     assert user is not None