Extended HTTPException and Response with error handling

pull/220/head
kompotkot 2021-09-02 10:49:23 +00:00
rodzic 120b6a952c
commit 6ac9df74aa
7 zmienionych plików z 94 dodań i 74 usunięć

Wyświetl plik

@ -1,10 +1,11 @@
import logging import logging
from typing import Awaitable, Callable, Dict, Optional from typing import Any, Awaitable, Callable, Dict, Optional
from bugout.data import BugoutUser from bugout.data import BugoutUser
from bugout.exceptions import BugoutResponseException from bugout.exceptions import BugoutResponseException
from fastapi import HTTPException, Request, Response
from starlette.background import BackgroundTask
from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import Request, Response
from .reporter import reporter from .reporter import reporter
from .settings import MOONSTREAM_APPLICATION_ID, bugout_client as bc from .settings import MOONSTREAM_APPLICATION_ID, bugout_client as bc
@ -12,6 +13,44 @@ from .settings import MOONSTREAM_APPLICATION_ID, bugout_client as bc
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class MoonstreamResponse(Response):
"""
Extended Response to handle 500 Internal server errors
and send crash reports.
"""
def __init__(
self,
content: Any = None,
status_code: int = 200,
headers: dict = None,
media_type: str = None,
background: BackgroundTask = None,
internal_error: Exception = None,
):
super().__init__(content, status_code, headers, media_type, background)
if internal_error is not None:
reporter.error_report(internal_error)
class MoonstreamHTTPException(HTTPException):
"""
Extended HTTPException to handle 500 Internal server errors
and send crash reports.
"""
def __init__(
self,
status_code: int,
detail: Any = None,
headers: Optional[Dict[str, Any]] = None,
internal_error: Exception = None,
):
super().__init__(status_code, detail, headers)
if internal_error is not None:
reporter.error_report(internal_error)
class BroodAuthMiddleware(BaseHTTPMiddleware): class BroodAuthMiddleware(BaseHTTPMiddleware):
""" """
Checks the authorization header on the request. If it represents a verified Brood user, Checks the authorization header on the request. If it represents a verified Brood user,
@ -62,8 +101,9 @@ class BroodAuthMiddleware(BaseHTTPMiddleware):
return Response(status_code=e.status_code, content=e.detail) return Response(status_code=e.status_code, content=e.detail)
except Exception as e: except Exception as e:
logger.error(f"Error processing Brood response: {str(e)}") logger.error(f"Error processing Brood response: {str(e)}")
reporter.error_report(e) return MoonstreamResponse(
return Response(status_code=500, content="Internal server error") status_code=500, content="Internal server error", internal_error=e
)
request.state.user = user request.state.user = user
request.state.token = user_token request.state.token = user_token

Wyświetl plik

@ -3,15 +3,14 @@ from typing import Dict, List, Optional
from sqlalchemy.sql.expression import true from sqlalchemy.sql.expression import true
from fastapi import FastAPI, Depends, Query, HTTPException from fastapi import FastAPI, Depends, Query
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from moonstreamdb.db import yield_db_session from moonstreamdb.db import yield_db_session
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from .. import actions from .. import actions
from .. import data from .. import data
from ..middleware import BroodAuthMiddleware from ..middleware import BroodAuthMiddleware, MoonstreamHTTPException
from ..reporter import reporter
from ..settings import DOCS_TARGET_PATH, ORIGINS, DOCS_PATHS from ..settings import DOCS_TARGET_PATH, ORIGINS, DOCS_PATHS
from ..version import MOONSTREAM_VERSION from ..version import MOONSTREAM_VERSION
@ -74,16 +73,15 @@ async def addresses_labels_bulk_handler(
about known addresses. about known addresses.
""" """
if limit > 100: if limit > 100:
raise HTTPException( raise MoonstreamHTTPException(
status_code=406, detail="The limit cannot exceed 100 addresses" status_code=406, detail="The limit cannot exceed 100 addresses"
) )
try: try:
addresses_response = actions.get_address_labels( addresses_response = actions.get_address_labels(
db_session=db_session, start=start, limit=limit, addresses=addresses db_session=db_session, start=start, limit=limit, addresses=addresses
) )
except Exception as err: except Exception as e:
logger.error(f"Unable to get info about Ethereum addresses {err}") logger.error(f"Unable to get info about Ethereum addresses {e}")
reporter.error_report(err) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return addresses_response return addresses_response

Wyświetl plik

@ -5,14 +5,14 @@ import logging
from typing import Dict, List, Optional from typing import Dict, List, Optional
from bugout.data import BugoutResource from bugout.data import BugoutResource
from fastapi import FastAPI, HTTPException, Request, Query, Depends from fastapi import FastAPI, Request, Query, Depends
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from moonstreamdb import db from moonstreamdb import db
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from .. import data from .. import data
from ..middleware import BroodAuthMiddleware from ..middleware import BroodAuthMiddleware, MoonstreamHTTPException
from ..providers import ( from ..providers import (
ReceivingEventsException, ReceivingEventsException,
event_providers, event_providers,
@ -21,7 +21,6 @@ from ..providers import (
next_event, next_event,
previous_event, previous_event,
) )
from ..reporter import reporter
from ..settings import ( from ..settings import (
DOCS_TARGET_PATH, DOCS_TARGET_PATH,
MOONSTREAM_ADMIN_ACCESS_TOKEN, MOONSTREAM_ADMIN_ACCESS_TOKEN,
@ -137,12 +136,10 @@ async def stream_handler(
) )
except ReceivingEventsException as e: except ReceivingEventsException as e:
logger.error("Error receiving events from provider") logger.error("Error receiving events from provider")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
except Exception as e: except Exception as e:
logger.error("Unable to get events") logger.error("Unable to get events")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
response = data.GetEventsResponse(stream_boundary=stream_boundary, events=events) response = data.GetEventsResponse(stream_boundary=stream_boundary, events=events)
return response return response
@ -182,12 +179,10 @@ async def latest_events_handler(
) )
except ReceivingEventsException as e: except ReceivingEventsException as e:
logger.error("Error receiving events from provider") logger.error("Error receiving events from provider")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
except Exception as e: except Exception as e:
logger.error("Unable to get latest events") logger.error("Unable to get latest events")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return events return events
@ -239,12 +234,10 @@ async def next_event_handler(
) )
except ReceivingEventsException as e: except ReceivingEventsException as e:
logger.error("Error receiving events from provider") logger.error("Error receiving events from provider")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
except Exception as e: except Exception as e:
logger.error("Unable to get next events") logger.error("Unable to get next events")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return event return event
@ -296,11 +289,9 @@ async def previous_event_handler(
) )
except ReceivingEventsException as e: except ReceivingEventsException as e:
logger.error("Error receiving events from provider") logger.error("Error receiving events from provider")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
except Exception as e: except Exception as e:
logger.error("Unable to get previous events") logger.error("Unable to get previous events")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return event return event

Wyświetl plik

@ -6,12 +6,12 @@ from typing import Dict, List, Optional
from bugout.data import BugoutResource, BugoutResources from bugout.data import BugoutResource, BugoutResources
from bugout.exceptions import BugoutResponseException from bugout.exceptions import BugoutResponseException
from fastapi import FastAPI, HTTPException, Request, Form from fastapi import FastAPI, Request, Form
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from ..admin import subscription_types from ..admin import subscription_types
from .. import data from .. import data
from ..middleware import BroodAuthMiddleware from ..middleware import BroodAuthMiddleware, MoonstreamHTTPException
from ..reporter import reporter from ..reporter import reporter
from ..settings import ( from ..settings import (
DOCS_TARGET_PATH, DOCS_TARGET_PATH,
@ -78,7 +78,7 @@ async def add_subscription_handler(
] ]
if subscription_type_id not in available_subscription_type_ids: if subscription_type_id not in available_subscription_type_ids:
raise HTTPException( raise MoonstreamHTTPException(
status_code=404, status_code=404,
detail=f"Invalid subscription type: {subscription_type_id}.", detail=f"Invalid subscription type: {subscription_type_id}.",
) )
@ -100,10 +100,11 @@ async def add_subscription_handler(
application_id=MOONSTREAM_APPLICATION_ID, application_id=MOONSTREAM_APPLICATION_ID,
resource_data=resource_data, resource_data=resource_data,
) )
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
logger.error(f"Error creating subscription resource: {str(e)}") logger.error(f"Error creating subscription resource: {str(e)}")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return data.SubscriptionResourceData( return data.SubscriptionResourceData(
id=str(resource.id), id=str(resource.id),
@ -128,11 +129,10 @@ async def delete_subscription_handler(request: Request, subscription_id: str):
try: try:
deleted_resource = bc.delete_resource(token=token, resource_id=subscription_id) deleted_resource = bc.delete_resource(token=token, resource_id=subscription_id)
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail) raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
logger.error(f"Error deleting subscription: {str(e)}") logger.error(f"Error deleting subscription: {str(e)}")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return data.SubscriptionResourceData( return data.SubscriptionResourceData(
id=str(deleted_resource.id), id=str(deleted_resource.id),
@ -156,12 +156,14 @@ async def get_subscriptions_handler(request: Request) -> data.SubscriptionsListR
} }
try: try:
resources: BugoutResources = bc.list_resources(token=token, params=params) resources: BugoutResources = bc.list_resources(token=token, params=params)
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
logger.error( logger.error(
f"Error listing subscriptions for user ({request.user.id}) with token ({request.state.token}), error: {str(e)}" f"Error listing subscriptions for user ({request.user.id}) with token ({request.state.token}), error: {str(e)}"
) )
reporter.error_report(e) reporter.error_report(e)
raise HTTPException(status_code=500) raise MoonstreamHTTPException(status_code=500, internal_error=e)
return data.SubscriptionsListResponse( return data.SubscriptionsListResponse(
subscriptions=[ subscriptions=[
@ -211,11 +213,10 @@ async def update_subscriptions_handler(
).dict(), ).dict(),
) )
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail) raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
logger.error(f"Error getting user subscriptions: {str(e)}") logger.error(f"Error getting user subscriptions: {str(e)}")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return data.SubscriptionResourceData( return data.SubscriptionResourceData(
id=str(resource.id), id=str(resource.id),
@ -241,9 +242,10 @@ async def list_subscription_types() -> data.SubscriptionTypesListResponse:
data.SubscriptionTypeResourceData.validate(resource.resource_data) data.SubscriptionTypeResourceData.validate(resource.resource_data)
for resource in response.resources for resource in response.resources
] ]
except BugoutResponseException as e:
raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
logger.error(f"Error reading subscription types from Brood API: {str(e)}") logger.error(f"Error reading subscription types from Brood API: {str(e)}")
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return data.SubscriptionTypesListResponse(subscription_types=results) return data.SubscriptionTypesListResponse(subscription_types=results)

Wyświetl plik

@ -7,16 +7,10 @@ import uuid
from bugout.data import BugoutToken, BugoutUser from bugout.data import BugoutToken, BugoutUser
from bugout.exceptions import BugoutResponseException from bugout.exceptions import BugoutResponseException
from fastapi import ( from fastapi import FastAPI, Form, Request
FastAPI,
Form,
HTTPException,
Request,
)
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from ..middleware import BroodAuthMiddleware from ..middleware import BroodAuthMiddleware, MoonstreamHTTPException
from ..reporter import reporter
from ..settings import ( from ..settings import (
MOONSTREAM_APPLICATION_ID, MOONSTREAM_APPLICATION_ID,
DOCS_TARGET_PATH, DOCS_TARGET_PATH,
@ -76,10 +70,9 @@ async def create_user_handler(
application_id=MOONSTREAM_APPLICATION_ID, application_id=MOONSTREAM_APPLICATION_ID,
) )
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail) raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return user return user
@ -94,10 +87,9 @@ async def restore_password_handler(email: str = Form(...)) -> Dict[str, Any]:
try: try:
response = bc.restore_password(email=email) response = bc.restore_password(email=email)
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail) raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return response return response
@ -108,10 +100,9 @@ async def reset_password_handler(
try: try:
response = bc.reset_password(reset_id=reset_id, new_password=new_password) response = bc.reset_password(reset_id=reset_id, new_password=new_password)
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail) raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return response return response
@ -125,10 +116,9 @@ async def change_password_handler(
token=token, current_password=current_password, new_password=new_password token=token, current_password=current_password, new_password=new_password
) )
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail) raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return user return user
@ -141,10 +131,9 @@ async def delete_user_handler(
try: try:
user = bc.delete_user(token=token, user_id=user.id, password=password) user = bc.delete_user(token=token, user_id=user.id, password=password)
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail) raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return user return user
@ -159,12 +148,11 @@ async def login_handler(
application_id=MOONSTREAM_APPLICATION_ID, application_id=MOONSTREAM_APPLICATION_ID,
) )
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException( raise MoonstreamHTTPException(
status_code=e.status_code, detail=f"Error from Brood API: {e.detail}" status_code=e.status_code, detail=f"Error from Brood API: {e.detail}"
) )
except Exception as e: except Exception as e:
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return token return token
@ -174,8 +162,7 @@ async def logout_handler(request: Request) -> uuid.UUID:
try: try:
token_id: uuid.UUID = bc.revoke_token(token=token) token_id: uuid.UUID = bc.revoke_token(token=token)
except BugoutResponseException as e: except BugoutResponseException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail) raise MoonstreamHTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e: except Exception as e:
reporter.error_report(e) raise MoonstreamHTTPException(status_code=500, internal_error=e)
raise HTTPException(status_code=500)
return token_id return token_id

Wyświetl plik

@ -29,5 +29,6 @@ toml==0.10.2
tomli==1.0.4 tomli==1.0.4
types-python-dateutil==0.1.6 types-python-dateutil==0.1.6
typing-extensions==3.10.0.0 typing-extensions==3.10.0.0
types-requests==2.25.6
urllib3==1.26.6 urllib3==1.26.6
uvicorn==0.14.0 uvicorn==0.14.0

Wyświetl plik

@ -18,6 +18,7 @@ setup(
"python-dateutil", "python-dateutil",
"uvicorn", "uvicorn",
"types-python-dateutil", "types-python-dateutil",
"types-requests",
], ],
extras_require={ extras_require={
"dev": ["black", "mypy"], "dev": ["black", "mypy"],