Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 27 additions & 23 deletions backend/app/api/routes/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
from app.api.deps import AuthContextDep, SessionDep
from app.api.permissions import Permission, require_permission
from app.core.exception_handlers import HTTPException
from app.core.providers import mask_credential_fields, validate_provider
from app.core.providers import (
CredentialPayload,
mask_credential_fields,
validate_provider,
)
from app.crud.credentials import (
get_creds_by_org,
get_provider_credential,
Expand All @@ -14,7 +18,7 @@
set_creds_for_org,
update_creds_for_org,
)
from app.models import CredsCreate, CredsPublic, CredsUpdate
from app.models import CredsCreate, CredsPublic, CredsUpdate, Message
from app.utils import APIResponse, load_description

logger = logging.getLogger(__name__)
Expand All @@ -32,7 +36,7 @@ def create_new_credential(
session: SessionDep,
creds_in: CredsCreate,
_current_user: AuthContextDep,
):
) -> APIResponse[list[CredsPublic]]:
# Project comes from API key context; no cross-org check needed here
# Database unique constraint ensures no duplicate credentials per provider-org-project combination

Expand Down Expand Up @@ -61,7 +65,7 @@ def read_credential(
*,
session: SessionDep,
_current_user: AuthContextDep,
):
) -> APIResponse[list[CredsPublic]]:
creds = get_creds_by_org(
session=session,
org_id=_current_user.organization_.id,
Expand All @@ -73,7 +77,7 @@ def read_credential(

@router.get(
"/provider/{provider}",
response_model=APIResponse[dict | None],
response_model=APIResponse[CredentialPayload | None],
description=load_description("credentials/get_provider.md"),
dependencies=[Depends(require_permission(Permission.REQUIRE_PROJECT))],
)
Expand All @@ -82,7 +86,7 @@ def read_provider_credential(
session: SessionDep,
provider: str,
_current_user: AuthContextDep,
):
) -> APIResponse[CredentialPayload | None]:
try:
provider_enum = validate_provider(provider)
except ValueError as e:
Expand Down Expand Up @@ -113,7 +117,7 @@ def update_credential(
session: SessionDep,
creds_in: CredsUpdate,
_current_user: AuthContextDep,
):
) -> APIResponse[list[CredsPublic]]:
if not creds_in or not creds_in.provider or not creds_in.credential:
logger.warning(
f"[update_credential] Invalid input | organization_id: {_current_user.organization_.id}, project_id: {_current_user.project_.id}"
Expand All @@ -137,7 +141,7 @@ def update_credential(

@router.delete(
"/provider/{provider}",
response_model=APIResponse[dict],
response_model=APIResponse[Message],
description=load_description("credentials/delete_provider.md"),
dependencies=[Depends(require_permission(Permission.REQUIRE_PROJECT))],
)
Expand All @@ -146,7 +150,7 @@ def delete_provider_credential(
session: SessionDep,
provider: str,
_current_user: AuthContextDep,
):
) -> APIResponse[Message]:
try:
provider_enum = validate_provider(provider)
except ValueError as e:
Expand All @@ -160,29 +164,29 @@ def delete_provider_credential(
)

return APIResponse.success_response(
{"message": "Provider credentials removed successfully"}
Message(message="Provider credentials removed successfully")
)


@router.delete(
"",
response_model=APIResponse[dict],
response_model=APIResponse[Message],
description=load_description("credentials/delete_all.md"),
dependencies=[Depends(require_permission(Permission.REQUIRE_PROJECT))],
)
def delete_all_credentials(
*,
session: SessionDep,
_current_user: AuthContextDep,
):
) -> APIResponse[Message]:
remove_creds_for_org(
session=session,
org_id=_current_user.organization_.id,
project_id=_current_user.project_.id,
)

return APIResponse.success_response(
{"message": "All credentials deleted successfully"}
Message(message="All credentials deleted successfully")
)


Expand All @@ -198,7 +202,7 @@ def read_credentials_by_org_project(
org_id: int,
project_id: int,
_current_user: AuthContextDep,
):
) -> APIResponse[list[CredsPublic]]:
creds = get_creds_by_org(
session=session,
org_id=org_id,
Expand All @@ -210,7 +214,7 @@ def read_credentials_by_org_project(

@router.get(
"/{org_id}/{project_id}/provider/{provider}",
response_model=APIResponse[dict | None],
response_model=APIResponse[CredentialPayload | None],
description=load_description("credentials/get_provider_by_org_project.md"),
dependencies=[Depends(require_permission(Permission.SUPERUSER))],
)
Expand All @@ -221,7 +225,7 @@ def read_provider_credential_by_org_project(
project_id: int,
provider: str,
_current_user: AuthContextDep,
):
) -> APIResponse[CredentialPayload | None]:
try:
provider_enum = validate_provider(provider)
except ValueError as e:
Expand Down Expand Up @@ -254,7 +258,7 @@ def update_credential_by_org_project(
project_id: int,
creds_in: CredsUpdate,
_current_user: AuthContextDep,
):
) -> APIResponse[list[CredsPublic]]:
if not creds_in or not creds_in.provider or not creds_in.credential:
logger.warning(
f"[update_credential_by_org_project] Invalid input | organization_id: {org_id}, project_id: {project_id}"
Expand All @@ -277,7 +281,7 @@ def update_credential_by_org_project(

@router.delete(
"/{org_id}/{project_id}/provider/{provider}",
response_model=APIResponse[dict],
response_model=APIResponse[Message],
description=load_description("credentials/delete_provider_by_org_project.md"),
dependencies=[Depends(require_permission(Permission.SUPERUSER))],
)
Expand All @@ -288,7 +292,7 @@ def delete_provider_credential_by_org_project(
project_id: int,
provider: str,
_current_user: AuthContextDep,
):
) -> APIResponse[Message]:
try:
provider_enum = validate_provider(provider)
except ValueError as e:
Expand All @@ -302,13 +306,13 @@ def delete_provider_credential_by_org_project(
)

return APIResponse.success_response(
{"message": "Provider credentials removed successfully"}
Message(message="Provider credentials removed successfully")
)


@router.delete(
"/{org_id}/{project_id}",
response_model=APIResponse[dict],
response_model=APIResponse[Message],
description=load_description("credentials/delete_all_by_org_project.md"),
dependencies=[Depends(require_permission(Permission.SUPERUSER))],
)
Expand All @@ -318,13 +322,13 @@ def delete_all_credentials_by_org_project(
org_id: int,
project_id: int,
_current_user: AuthContextDep,
):
) -> APIResponse[Message]:
remove_creds_for_org(
session=session,
org_id=org_id,
project_id=project_id,
)

return APIResponse.success_response(
{"message": "All credentials deleted successfully"}
Message(message="All credentials deleted successfully")
)
2 changes: 1 addition & 1 deletion backend/app/api/routes/onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
def onboard_project_route(
onboard_in: OnboardingRequest,
session: SessionDep,
):
) -> APIResponse[OnboardingResponse]:
response = onboard_project(session=session, onboard_in=onboard_in)

metadata = None
Expand Down
15 changes: 9 additions & 6 deletions backend/app/api/routes/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
router = APIRouter(prefix="/projects", tags=["Projects"])


# Retrieve projects
@router.get(
"",
dependencies=[Depends(require_permission(Permission.SUPERUSER))],
Expand All @@ -54,7 +53,7 @@ def read_projects(
True,
description="Filter by active status. Pass false to list soft-deleted projects.",
),
):
) -> APIResponse[list[ProjectPublic]]:
filters = [Project.is_active.is_(is_active)]
if search and search.strip():
filters.append(Project.name.ilike(f"%{search.strip()}%"))
Expand All @@ -76,7 +75,9 @@ def read_projects(
response_model=APIResponse[ProjectPublic],
description=load_description("projects/create.md"),
)
def create_new_project(*, session: SessionDep, project_in: ProjectCreate):
def create_new_project(
*, session: SessionDep, project_in: ProjectCreate
) -> APIResponse[ProjectPublic]:
project = create_project(session=session, project_create=project_in)
return APIResponse.success_response(project)

Expand Down Expand Up @@ -111,7 +112,7 @@ def update_project_settings_route(
response_model=APIResponse[ProjectPublic],
description=load_description("projects/get.md"),
)
def read_project(*, session: SessionDep, project_id: int):
def read_project(*, session: SessionDep, project_id: int) -> APIResponse[ProjectPublic]:
"""
Retrieve a project by ID.
"""
Expand All @@ -128,7 +129,9 @@ def read_project(*, session: SessionDep, project_id: int):
response_model=APIResponse[ProjectPublic],
description=load_description("projects/update.md"),
)
def update_project(*, session: SessionDep, project_id: int, project_in: ProjectUpdate):
def update_project(
*, session: SessionDep, project_id: int, project_in: ProjectUpdate
) -> APIResponse[ProjectPublic]:
project = get_project_by_id(session=session, project_id=project_id)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
Expand Down Expand Up @@ -193,7 +196,7 @@ def delete_project_endpoint(
session: SessionDep,
project_id: int,
body: DeleteRequest | None = None,
):
) -> APIResponse[None]:
project = get_project_by_id(session=session, project_id=project_id)
if project is None:
raise HTTPException(status_code=404, detail="Project not found")
Expand Down
19 changes: 9 additions & 10 deletions backend/app/api/routes/users.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import logging
from typing import Any

from fastapi import APIRouter, Depends
from sqlmodel import func, select
Expand Down Expand Up @@ -37,7 +36,7 @@
response_model=UsersPublic,
include_in_schema=False,
)
def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> UsersPublic:
count = session.exec(select(func.count()).select_from(User)).one()
users = session.exec(select(User).offset(skip).limit(limit)).all()
return UsersPublic(data=users, count=count)
Expand All @@ -49,7 +48,7 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
response_model=UserPublic,
include_in_schema=False,
)
def create_user_endpoint(*, session: SessionDep, user_in: UserCreate) -> Any:
def create_user_endpoint(*, session: SessionDep, user_in: UserCreate) -> User:
if get_user_by_email(session=session, email=user_in.email):
logger.warning(
f"[create_user_endpoint] Attempting to create user with existing email | email: {user_in.email}"
Expand All @@ -76,7 +75,7 @@ def create_user_endpoint(*, session: SessionDep, user_in: UserCreate) -> Any:
@router.patch("/me", response_model=UserPublic)
def update_user_me(
*, session: SessionDep, user_in: UserUpdateMe, current_user_dep: AuthContextDep
) -> Any:
) -> User:
current_user = current_user_dep.user
if user_in.email:
existing_user = get_user_by_email(session=session, email=user_in.email)
Expand All @@ -99,7 +98,7 @@ def update_user_me(
@router.patch("/me/password", response_model=Message)
def update_password_me(
*, session: SessionDep, body: UpdatePassword, current_user_dep: AuthContextDep
) -> Any:
) -> Message:
current_user = current_user_dep.user
if not verify_password(body.current_password, current_user.hashed_password):
raise HTTPException(status_code=400, detail="Incorrect password")
Expand All @@ -123,7 +122,7 @@ def update_password_me(
def read_user_me(
session: SessionDep,
current_user_dep: AuthContextDep,
) -> Any:
) -> UserPublic:
user = current_user_dep.user
features: list[str] = []
org = current_user_dep.organization
Expand All @@ -139,7 +138,7 @@ def read_user_me(


@router.delete("/me", response_model=Message)
def delete_user_me(session: SessionDep, current_user_dep: AuthContextDep) -> Any:
def delete_user_me(session: SessionDep, current_user_dep: AuthContextDep) -> Message:
current_user = current_user_dep.user
if current_user.is_superuser:
logger.warning(
Expand All @@ -159,7 +158,7 @@ def delete_user_me(session: SessionDep, current_user_dep: AuthContextDep) -> Any
dependencies=[Depends(require_permission(Permission.SUPERUSER))],
response_model=UserPublic,
)
def register_user(session: SessionDep, user_in: UserRegister) -> Any:
def register_user(session: SessionDep, user_in: UserRegister) -> User:
"""
This endpoint allows the registration of a new user and is accessible only by a superuser.
"""
Expand All @@ -179,7 +178,7 @@ def register_user(session: SessionDep, user_in: UserRegister) -> Any:
@router.get("/{user_id}", response_model=UserPublic, include_in_schema=False)
def read_user_by_id(
user_id: int, session: SessionDep, current_user: AuthContextDep
) -> Any:
) -> User | None:
user = session.get(User, user_id)
if user == current_user.user:
return user
Expand All @@ -204,7 +203,7 @@ def update_user_endpoint(
session: SessionDep,
user_id: int,
user_in: UserUpdate,
) -> Any:
) -> User:
db_user = session.get(User, user_id)
if not db_user:
raise HTTPException(
Expand Down
Loading
Loading