personal-lifecycle-tracker / backend / app / routers / groups.py
groups.py
Raw
from __future__ import annotations

from typing import List
from fastapi import APIRouter, Depends, HTTPException, Response, status

from app.auth import get_current_user_optional
from app.models import (
    AddMemberRequest,
    CreateGroupRequest,
    Group,
    MessageResponse,
    User,
)
from app.store import DatabaseStore, get_store
from app.telemetry import record_group_created, record_group_member_invited

router = APIRouter()


@router.get("", response_model=List[Group], status_code=status.HTTP_200_OK)
async def list_groups(
    current_user: User = Depends(get_current_user_optional),
    store: DatabaseStore = Depends(get_store),
) -> List[Group]:
    return store.get_groups(user=current_user)


@router.post("", response_model=Group, status_code=status.HTTP_201_CREATED)
async def create_group(
    payload: CreateGroupRequest,
    current_user: User = Depends(get_current_user_optional),
    store: DatabaseStore = Depends(get_store),
) -> Group:
    group = store.create_group(user=current_user, name=payload.name)
    record_group_created()
    return group


@router.get("/{id}", response_model=Group, status_code=status.HTTP_200_OK)
async def get_group(
    id: str,
    current_user: User = Depends(get_current_user_optional),
    store: DatabaseStore = Depends(get_store),
) -> Group:
    group = store.get_group_by_id(id)
    if not group:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Group not found",
        )
    if not store.is_group_accessible(group, current_user):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not authorized to access this group",
        )
    return group


@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_group(
    id: str,
    current_user: User = Depends(get_current_user_optional),
    store: DatabaseStore = Depends(get_store),
) -> Response:
    group = store.get_group_by_id(id)
    if not group:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Group not found",
        )
    deleted, err = store.delete_group(requester=current_user, group_id=id)
    if not deleted:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=err or "Could not delete group",
        )
    return Response(status_code=status.HTTP_204_NO_CONTENT)


@router.post("/{groupId}/members", response_model=Group, status_code=status.HTTP_200_OK)
async def add_member(
    groupId: str,
    payload: AddMemberRequest,
    current_user: User = Depends(get_current_user_optional),
    store: DatabaseStore = Depends(get_store),
) -> Group:
    group = store.get_group_by_id(groupId)
    if not group:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Group not found",
        )
    try:
        updated_group = store.add_member(
            user=current_user, group_id=groupId, email=payload.email
        )
        if not updated_group:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Group not found")
        record_group_member_invited()
        return updated_group
    except PermissionError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))


@router.delete("/{groupId}/members/{memberEmail}", response_model=Group, status_code=status.HTTP_200_OK)
async def remove_member(
    groupId: str,
    memberEmail: str,
    current_user: User = Depends(get_current_user_optional),
    store: DatabaseStore = Depends(get_store),
) -> Group:
    group = store.get_group_by_id(groupId)
    if not group:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Group not found",
        )
    updated_group, err = store.remove_member(
        requester=current_user, group_id=groupId, member_email=memberEmail
    )
    if not updated_group:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=err or "Could not remove member",
        )
    return updated_group


@router.post("/{groupId}/leave", response_model=MessageResponse, status_code=status.HTTP_200_OK)
async def leave_group(
    groupId: str,
    current_user: User = Depends(get_current_user_optional),
    store: DatabaseStore = Depends(get_store),
) -> MessageResponse:
    group = store.get_group_by_id(groupId)
    if not group:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Group not found",
        )
    success, err = store.leave_group(user=current_user, group_id=groupId)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=err or "Could not leave group",
        )
    return MessageResponse(message="Left group successfully")