Compare commits

...

1 Commits

Author SHA1 Message Date
Nik
649a2af6be refactor(be): replace HTTPException with OnyxError in small API files 2026-03-09 15:48:08 -07:00
12 changed files with 65 additions and 56 deletions

View File

@@ -4,7 +4,6 @@ from typing import List
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from pydantic import BaseModel
from sqlalchemy.orm import Session
@@ -22,6 +21,8 @@ from onyx.auth.users import current_user
from onyx.configs.constants import PUBLIC_API_TAGS
from onyx.db.engine.sql_engine import get_session
from onyx.db.models import User
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
router = APIRouter(prefix="/analytics", tags=PUBLIC_API_TAGS)
@@ -231,8 +232,9 @@ def get_assistant_stats(
end = end or datetime.datetime.utcnow()
if not user_can_view_assistant_stats(db_session, user, assistant_id):
raise HTTPException(
status_code=403, detail="Not allowed to access this assistant's stats."
raise OnyxError(
OnyxErrorCode.INSUFFICIENT_PERMISSIONS,
"Not allowed to access this assistant's stats.",
)
# Pull daily usage from the DB calls

View File

@@ -4,7 +4,6 @@ from typing import Any
from typing import cast
from typing import IO
from fastapi import HTTPException
from fastapi import UploadFile
from ee.onyx.server.enterprise_settings.models import AnalyticsScriptUpload
@@ -13,6 +12,8 @@ from onyx.configs.constants import FileOrigin
from onyx.configs.constants import KV_CUSTOM_ANALYTICS_SCRIPT_KEY
from onyx.configs.constants import KV_ENTERPRISE_SETTINGS_KEY
from onyx.configs.constants import ONYX_DEFAULT_APPLICATION_NAME
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.file_store.file_store import get_default_file_store
from onyx.key_value_store.factory import get_kv_store
from onyx.key_value_store.interface import KvKeyNotFoundError
@@ -118,9 +119,9 @@ def upload_logo(file: UploadFile | str, is_logotype: bool = False) -> bool:
else:
logger.notice("Uploading logo from uploaded file")
if not file.filename or not is_valid_file_type(file.filename):
raise HTTPException(
status_code=400,
detail="Invalid file type- only .png, .jpg, and .jpeg files are allowed",
raise OnyxError(
OnyxErrorCode.VALIDATION_ERROR,
"Invalid file type- only .png, .jpg, and .jpeg files are allowed",
)
content = file.file
display_name = file.filename

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from ee.onyx.db.standard_answer import fetch_standard_answer
@@ -15,6 +14,8 @@ from ee.onyx.db.standard_answer import update_standard_answer_category
from onyx.auth.users import current_admin_user
from onyx.db.engine.sql_engine import get_session
from onyx.db.models import User
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.manage.models import StandardAnswer
from onyx.server.manage.models import StandardAnswerCategory
from onyx.server.manage.models import StandardAnswerCategoryCreationRequest
@@ -65,7 +66,7 @@ def patch_standard_answer(
)
if existing_standard_answer is None:
raise HTTPException(status_code=404, detail="Standard answer not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Standard answer not found")
standard_answer_model = update_standard_answer(
standard_answer_id=standard_answer_id,
@@ -131,9 +132,7 @@ def patch_standard_answer_category(
)
if existing_standard_answer_category is None:
raise HTTPException(
status_code=404, detail="Standard answer category not found"
)
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Standard answer category not found")
standard_answer_category_model = update_standard_answer_category(
standard_answer_category_id=standard_answer_category_id,

View File

@@ -2,7 +2,6 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.users import current_admin_user
@@ -10,6 +9,8 @@ from onyx.db.engine.sql_engine import get_session
from onyx.db.models import User
from onyx.db.persona import get_default_assistant
from onyx.db.persona import update_default_assistant_configuration
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.prompts.chat_prompts import DEFAULT_SYSTEM_PROMPT
from onyx.server.features.default_assistant.models import DefaultAssistantConfiguration
from onyx.server.features.default_assistant.models import DefaultAssistantUpdateRequest
@@ -32,7 +33,7 @@ def get_default_assistant_configuration(
"""
persona = get_default_assistant(db_session)
if not persona:
raise HTTPException(status_code=404, detail="Default assistant not found")
raise OnyxError(OnyxErrorCode.PERSONA_NOT_FOUND, "Default assistant not found")
# Extract DB tool IDs from the persona's tools
tool_ids = [tool.id for tool in persona.tools]
@@ -86,5 +87,5 @@ def update_default_assistant(
except ValueError as e:
if "Default assistant not found" in str(e):
raise HTTPException(status_code=404, detail=str(e))
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.PERSONA_NOT_FOUND, str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.access.hierarchy_access import get_user_external_group_ids
@@ -12,6 +11,8 @@ from onyx.db.engine.sql_engine import get_session
from onyx.db.hierarchy import get_accessible_hierarchy_nodes_for_source
from onyx.db.models import User
from onyx.db.opensearch_migration import get_opensearch_retrieval_state
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.hierarchy.constants import DOCUMENT_PAGE_SIZE
from onyx.server.features.hierarchy.constants import HIERARCHY_NODE_DOCUMENTS_PATH
from onyx.server.features.hierarchy.constants import HIERARCHY_NODES_LIST_PATH
@@ -43,14 +44,14 @@ router = APIRouter(prefix=HIERARCHY_NODES_PREFIX)
def _require_opensearch(db_session: Session) -> None:
if not ENABLE_OPENSEARCH_INDEXING_FOR_ONYX:
raise HTTPException(
status_code=403,
detail=OPENSEARCH_NOT_ENABLED_MESSAGE,
raise OnyxError(
OnyxErrorCode.NOT_IMPLEMENTED,
OPENSEARCH_NOT_ENABLED_MESSAGE,
)
if not get_opensearch_retrieval_state(db_session):
raise HTTPException(
status_code=403,
detail=MIGRATION_STATUS_MESSAGE,
raise OnyxError(
OnyxErrorCode.SERVICE_UNAVAILABLE,
MIGRATION_STATUS_MESSAGE,
)

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.users import current_admin_user
@@ -15,6 +14,8 @@ from onyx.db.input_prompt import remove_public_input_prompt
from onyx.db.input_prompt import update_input_prompt
from onyx.db.models import InputPrompt__User
from onyx.db.models import User
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.input_prompt.models import CreateInputPromptRequest
from onyx.server.features.input_prompt.models import InputPromptSnapshot
from onyx.server.features.input_prompt.models import UpdateInputPromptRequest
@@ -97,7 +98,7 @@ def patch_input_prompt(
except ValueError as e:
error_msg = "Error occurred while updated input prompt"
logger.warn(f"{error_msg}. Stack trace: {e}")
raise HTTPException(status_code=404, detail=error_msg)
raise OnyxError(OnyxErrorCode.NOT_FOUND, error_msg)
return InputPromptSnapshot.from_model(updated_input_prompt)
@@ -117,7 +118,7 @@ def delete_input_prompt(
except ValueError as e:
error_msg = "Error occurred while deleting input prompt"
logger.warn(f"{error_msg}. Stack trace: {e}")
raise HTTPException(status_code=404, detail=error_msg)
raise OnyxError(OnyxErrorCode.NOT_FOUND, error_msg)
@admin_router.delete("/{input_prompt_id}")
@@ -132,7 +133,7 @@ def delete_public_input_prompt(
except ValueError as e:
error_msg = "Error occurred while deleting input prompt"
logger.warn(f"{error_msg}. Stack trace: {e}")
raise HTTPException(status_code=404, detail=error_msg)
raise OnyxError(OnyxErrorCode.NOT_FOUND, error_msg)
@basic_router.post("/{input_prompt_id}/hide")

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.users import current_user
@@ -9,6 +8,8 @@ from onyx.db.models import User
from onyx.db.notification import dismiss_notification
from onyx.db.notification import get_notification_by_id
from onyx.db.notification import get_notifications
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.build.utils import ensure_build_mode_intro_notification
from onyx.server.features.release_notes.utils import (
ensure_release_notes_fresh_and_notify,
@@ -64,10 +65,11 @@ def dismiss_notification_endpoint(
try:
notification = get_notification_by_id(notification_id, user, db_session)
except PermissionError:
raise HTTPException(
status_code=403, detail="Not authorized to dismiss this notification"
raise OnyxError(
OnyxErrorCode.INSUFFICIENT_PERMISSIONS,
"Not authorized to dismiss this notification",
)
except ValueError:
raise HTTPException(status_code=404, detail="Notification not found")
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Notification not found")
dismiss_notification(notification, db_session)

View File

@@ -1,6 +1,5 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from fastapi_users.exceptions import InvalidPasswordException
from sqlalchemy.orm import Session
@@ -11,6 +10,8 @@ from onyx.auth.users import User
from onyx.auth.users import UserManager
from onyx.db.engine.sql_engine import get_session
from onyx.db.users import get_user_by_email
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.features.password.models import ChangePasswordRequest
from onyx.server.features.password.models import UserResetRequest
from onyx.server.features.password.models import UserResetResponse
@@ -34,10 +35,11 @@ async def change_my_password(
new_password=form_data.new_password,
)
except InvalidPasswordException as e:
raise HTTPException(status_code=400, detail=str(e.reason))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e.reason))
except Exception as e:
raise HTTPException(
status_code=500, detail=f"An unexpected error occurred: {str(e)}"
raise OnyxError(
OnyxErrorCode.INTERNAL_ERROR,
f"An unexpected error occurred: {str(e)}",
)
@@ -53,7 +55,7 @@ async def admin_reset_user_password(
"""
user = get_user_by_email(user_reset_request.user_email, db_session)
if not user:
raise HTTPException(status_code=404, detail="User not found")
raise OnyxError(OnyxErrorCode.USER_NOT_FOUND, "User not found")
new_password = await user_manager.reset_password_as_admin(user.id)
return UserResetResponse(
user_id=str(user.id),

View File

@@ -3,7 +3,6 @@ import re
import requests
from fastapi import APIRouter
from fastapi import HTTPException
from onyx import __version__
from onyx.auth.users import anonymous_user_enabled
@@ -16,6 +15,8 @@ from onyx.configs.constants import DEV_VERSION_PATTERN
from onyx.configs.constants import PUBLIC_API_TAGS
from onyx.configs.constants import STABLE_VERSION_PATTERN
from onyx.db.auth import get_user_count
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.manage.models import AllVersions
from onyx.server.manage.models import AuthTypeResponse
from onyx.server.manage.models import ContainerVersions
@@ -104,14 +105,14 @@ def get_versions() -> AllVersions:
# Ensure we have at least one tag of each type
if not dev_tags:
raise HTTPException(
status_code=500,
detail="No valid dev versions found matching pattern v(number).(number).(number)-beta.(number)",
raise OnyxError(
OnyxErrorCode.INTERNAL_ERROR,
"No valid dev versions found matching pattern v(number).(number).(number)-beta.(number)",
)
if not stable_tags:
raise HTTPException(
status_code=500,
detail="No valid stable versions found matching pattern v(number).(number).(number)",
raise OnyxError(
OnyxErrorCode.INTERNAL_ERROR,
"No valid stable versions found matching pattern v(number).(number).(number)",
)
# Sort common tags and get the latest one

View File

@@ -2,7 +2,6 @@
from fastapi import APIRouter
from fastapi import Depends
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.auth.users import current_user
@@ -11,6 +10,8 @@ from onyx.db.models import User
from onyx.db.pat import create_pat
from onyx.db.pat import list_user_pats
from onyx.db.pat import revoke_pat
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.pat.models import CreatedTokenResponse
from onyx.server.pat.models import CreateTokenRequest
from onyx.server.pat.models import TokenResponse
@@ -57,7 +58,7 @@ def create_token(
expiration_days=request.expiration_days,
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
raise OnyxError(OnyxErrorCode.VALIDATION_ERROR, str(e))
logger.info(f"User {user.email} created PAT '{request.name}'")
@@ -81,9 +82,7 @@ def delete_token(
"""Delete (revoke) personal access token. Only owner can revoke their own tokens."""
success = revoke_pat(db_session, token_id, user.id)
if not success:
raise HTTPException(
status_code=404, detail="Token not found or not owned by user"
)
raise OnyxError(OnyxErrorCode.NOT_FOUND, "Token not found or not owned by user")
logger.info(f"User {user.email} revoked token {token_id}")
return {"message": "Token deleted successfully"}

View File

@@ -2,7 +2,6 @@
from collections.abc import Callable
from fastapi import HTTPException
from sqlalchemy.orm import Session
from onyx.configs.app_configs import ANTHROPIC_DEFAULT_API_KEY
@@ -12,6 +11,8 @@ from onyx.configs.app_configs import OPENROUTER_DEFAULT_API_KEY
from onyx.db.usage import check_usage_limit
from onyx.db.usage import UsageLimitExceededError
from onyx.db.usage import UsageType
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
from onyx.server.tenant_usage_limits import TenantUsageLimitKeys
from onyx.server.tenant_usage_limits import TenantUsageLimitOverrides
from onyx.utils.logger import setup_logger
@@ -267,4 +268,4 @@ def check_usage_and_raise(
"Please upgrade your plan or wait for the next billing period."
)
raise HTTPException(status_code=429, detail=detail)
raise OnyxError(OnyxErrorCode.RATE_LIMITED, detail)

View File

@@ -1,15 +1,14 @@
"""Utilities for gating endpoints that require a vector database."""
from fastapi import HTTPException
from starlette.status import HTTP_501_NOT_IMPLEMENTED
from onyx.configs.app_configs import DISABLE_VECTOR_DB
from onyx.error_handling.error_codes import OnyxErrorCode
from onyx.error_handling.exceptions import OnyxError
def require_vector_db() -> None:
"""FastAPI dependency — raises 501 when the vector DB is disabled."""
if DISABLE_VECTOR_DB:
raise HTTPException(
status_code=HTTP_501_NOT_IMPLEMENTED,
detail="This feature requires a vector database (DISABLE_VECTOR_DB is set).",
raise OnyxError(
OnyxErrorCode.NOT_IMPLEMENTED,
"This feature requires a vector database (DISABLE_VECTOR_DB is set).",
)