🐛 fix(store.py): replace generic exception handling with custom exceptions to provide more specific error messages and status codes

🔀 merge(exceptions.py): add custom exceptions with status codes to handle specific errors in the store service
🔀 merge(service.py): replace ValueError with custom exceptions in the store service to provide more specific error messages and status codes
This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-11-19 19:35:23 -03:00
commit bbba05b49b
3 changed files with 42 additions and 21 deletions

View file

@ -4,9 +4,11 @@ from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from httpx import HTTPStatusError from httpx import HTTPStatusError
from langflow.services.auth import utils as auth_utils from langflow.services.auth import utils as auth_utils
from langflow.services.database.models.user.user import User from langflow.services.database.models.user.user import User
from langflow.services.deps import get_settings_service, get_store_service from langflow.services.deps import get_settings_service, get_store_service
from langflow.services.store.exceptions import CustomException
from langflow.services.store.schema import ( from langflow.services.store.schema import (
CreateComponentResponse, CreateComponentResponse,
DownloadComponentResponse, DownloadComponentResponse,
@ -127,17 +129,11 @@ async def get_components(
limit=limit, limit=limit,
store_api_Key=store_api_Key, store_api_Key=store_api_Key,
) )
except Exception as exc: except CustomException as exc:
if isinstance(exc, HTTPStatusError): if isinstance(exc, ValueError):
if exc.response.status_code == 403: raise HTTPException(status_code=500, detail=str(exc)) from exc
raise HTTPException(status_code=403, detail="Forbidden")
elif isinstance(exc, ValueError):
if "Check your API key" in str(exc):
raise HTTPException(status_code=401, detail=str(exc))
elif "filter by likes" in str(exc) or "filter your components" in str(exc):
raise HTTPException(status_code=400, detail=str(exc))
raise HTTPException(status_code=500, detail=str(exc)) raise HTTPException(status_code=exc.status_code, detail=str(exc)) from exc
@router.get("/components/{component_id}", response_model=DownloadComponentResponse) @router.get("/components/{component_id}", response_model=DownloadComponentResponse)

View file

@ -0,0 +1,25 @@
class CustomException(Exception):
def __init__(self, detail, status_code):
super().__init__(detail)
self.status_code = status_code
# Define custom exceptions with status codes
class UnauthorizedError(CustomException):
def __init__(self, detail="Unauthorized access"):
super().__init__(detail, 401)
class ForbiddenError(CustomException):
def __init__(self, detail="Forbidden"):
super().__init__(detail, 403)
class APIKeyError(CustomException):
def __init__(self, detail="API key error"):
super().__init__(detail, 401)
class FilterError(CustomException):
def __init__(self, detail="Filter error"):
super().__init__(detail, 400)

View file

@ -4,9 +4,8 @@ from uuid import UUID
import httpx import httpx
from httpx import HTTPError, HTTPStatusError from httpx import HTTPError, HTTPStatusError
from loguru import logger
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.store.exceptions import APIKeyError, FilterError, ForbiddenError
from langflow.services.store.schema import ( from langflow.services.store.schema import (
CreateComponentResponse, CreateComponentResponse,
DownloadComponentResponse, DownloadComponentResponse,
@ -19,6 +18,7 @@ from langflow.services.store.utils import (
process_tags_for_post, process_tags_for_post,
update_components_with_user_data, update_components_with_user_data,
) )
from loguru import logger
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.settings.service import SettingsService from langflow.services.settings.service import SettingsService
@ -196,7 +196,7 @@ class StoreService(Service):
liked_filter = self.build_liked_filter() liked_filter = self.build_liked_filter()
filter_conditions.append(liked_filter) filter_conditions.append(liked_filter)
elif liked and not store_api_Key: elif liked and not store_api_Key:
raise ValueError("You must provide an API key to filter by likes") raise APIKeyError("You must provide an API key to filter by likes")
if filter_by_user and store_api_Key: if filter_by_user and store_api_Key:
user_data = user_data_var.get() user_data = user_data_var.get()
@ -204,7 +204,7 @@ class StoreService(Service):
raise ValueError("No user data") raise ValueError("No user data")
filter_conditions.append({"user_created": {"_eq": user_data["id"]}}) filter_conditions.append({"user_created": {"_eq": user_data["id"]}})
elif filter_by_user and not store_api_Key: elif filter_by_user and not store_api_Key:
raise ValueError("You must provide an API key to filter your components") raise APIKeyError("You must provide an API key to filter your components")
else: else:
filter_conditions.append({"status": {"_in": ["public", "Public"]}}) filter_conditions.append({"status": {"_in": ["public", "Public"]}})
@ -256,7 +256,7 @@ class StoreService(Service):
return results_objects, metadata return results_objects, metadata
async def get_liked_by_user_components(self, component_ids: List[UUID], api_key: str) -> List[str]: async def get_liked_by_user_components(self, component_ids: List[str], api_key: str) -> List[str]:
# Get fields id # Get fields id
# filter should be "id is in component_ids AND liked_by directus_users_id token is api_key" # filter should be "id is in component_ids AND liked_by directus_users_id token is api_key"
# return the ids # return the ids
@ -339,7 +339,7 @@ class StoreService(Service):
try: try:
errors = response.json() errors = response.json()
message = errors["errors"][0]["message"] message = errors["errors"][0]["message"]
raise ValueError(message) raise FilterError(message)
except UnboundLocalError: except UnboundLocalError:
pass pass
raise ValueError(f"Upload failed: {exc}") raise ValueError(f"Upload failed: {exc}")
@ -447,9 +447,9 @@ class StoreService(Service):
comp_count = metadata.get("filter_count", 0) comp_count = metadata.get("filter_count", 0)
except HTTPStatusError as exc: except HTTPStatusError as exc:
if exc.response.status_code == 403: if exc.response.status_code == 403:
raise ValueError("You are not authorized to access this public resource") raise ForbiddenError("You are not authorized to access this public resource")
elif exc.response.status_code == 401: elif exc.response.status_code == 401:
raise ValueError("You are not authorized to access this resource. Please check your API key.") raise APIKeyError("You are not authorized to access this resource. Please check your API key.")
try: try:
if result and not metadata: if result and not metadata:
if len(result) >= limit: if len(result) >= limit:
@ -464,9 +464,9 @@ class StoreService(Service):
comp_count = 0 comp_count = 0
except HTTPStatusError as exc: except HTTPStatusError as exc:
if exc.response.status_code == 403: if exc.response.status_code == 403:
raise ValueError("You are not authorized to access this public resource") raise ForbiddenError("You are not authorized to access this public resource")
elif exc.response.status_code == 401: elif exc.response.status_code == 401:
raise ValueError("You are not authorized to access this resource. Please check your API key.") raise APIKeyError("You are not authorized to access this resource. Please check your API key.")
if store_api_Key: if store_api_Key:
# Now, from the result, we need to get the components # Now, from the result, we need to get the components
@ -482,5 +482,5 @@ class StoreService(Service):
# If we get an error here, it means the user is not authorized # If we get an error here, it means the user is not authorized
authorized = False authorized = False
else: else:
authorized = True authorized = await self.check_api_key(store_api_Key)
return ListComponentResponseModel(results=result, authorized=authorized, count=comp_count) return ListComponentResponseModel(results=result, authorized=authorized, count=comp_count)