Squashed 'flask-bpmn/' changes from f3fc5394..42cebab5

42cebab5 send the sentry link for unknown exceptions w/ burnettk

git-subtree-dir: flask-bpmn
git-subtree-split: 42cebab51fe469ab9dcb45672917d34c1f30987a
This commit is contained in:
jasquat 2022-10-18 16:41:16 -04:00
parent 79e2bb98b7
commit fe10fdbe52

View File

@ -2,78 +2,47 @@
from __future__ import annotations from __future__ import annotations
import json import json
from dataclasses import dataclass
from dataclasses import field
from typing import Any from typing import Any
import flask.wrappers
import sentry_sdk import sentry_sdk
from flask import Blueprint from flask import Blueprint
from flask import current_app from flask import current_app
from flask import g from flask import g
from marshmallow import Schema from flask import jsonify
from flask import make_response
from sentry_sdk import capture_exception
from sentry_sdk import set_user
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ignore from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ignore
from SpiffWorkflow.exceptions import WorkflowException # type: ignore from SpiffWorkflow.exceptions import WorkflowException # type: ignore
from SpiffWorkflow.specs.base import TaskSpec # type: ignore from SpiffWorkflow.specs.base import TaskSpec # type: ignore
from SpiffWorkflow.task import Task # type: ignore from SpiffWorkflow.task import Task # type: ignore
from werkzeug.exceptions import InternalServerError from werkzeug.exceptions import InternalServerError
api_error_blueprint = Blueprint("api_error_blueprint", __name__) api_error_blueprint = Blueprint("api_error_blueprint", __name__)
@dataclass
class ApiError(Exception): class ApiError(Exception):
"""ApiError Class to help handle exceptions.""" """ApiError Class to help handle exceptions."""
def __init__( error_code: str
self, message: str
error_code: str, error_line: str = ""
message: str, error_type: str = ""
status_code: int = 400, file_name: str = ""
file_name: str = "", line_number: int = 0
task_id: str = "", offset: int = 0
task_name: str = "", sentry_link: str | None = None
tag: str = "", status_code: int = 400
task_data: dict | None | str = None, tag: str = ""
error_type: str = "", task_data: dict | str | None = field(default_factory=dict)
error_line: str = "", task_id: str = ""
line_number: int = 0, task_name: str = ""
offset: int = 0, task_trace: dict | None = field(default_factory=dict)
task_trace: dict | None = None,
) -> None:
"""The Init Method."""
if task_data is None:
task_data = {}
if task_trace is None:
task_trace = {}
self.status_code = status_code
self.error_code = error_code # a short consistent string describing the error.
self.message = message # A detailed message that provides more information.
# OPTIONAL: The id of the task in the BPMN Diagram.
self.task_id = task_id or ""
# OPTIONAL: The name of the task in the BPMN Diagram.
# OPTIONAL: The file that caused the error.
self.task_name = task_name or ""
self.file_name = file_name or ""
# OPTIONAL: The XML Tag that caused the issue.
self.tag = tag or ""
# OPTIONAL: A snapshot of data connected to the task when error occurred.
self.task_data = task_data or ""
self.line_number = line_number
self.offset = offset
self.error_type = error_type
self.error_line = error_line
self.task_trace = task_trace
try:
user = g.user.uid
except Exception:
user = "Unknown"
self.task_user = user
# This is for sentry logging into Slack
sentry_sdk.set_context("User", {"user": user})
Exception.__init__(self, self.message)
def __str__(self) -> str: def __str__(self) -> str:
"""Instructions to print instance as a string.""" """Instructions to print instance as a string."""
@ -189,43 +158,48 @@ class ApiError(Exception):
return ApiError.from_task_spec(error_code, message, exp.sender) return ApiError.from_task_spec(error_code, message, exp.sender)
class ApiErrorSchema(Schema): def set_user_sentry_context() -> None:
"""ApiErrorSchema Class.""" """Set_user_sentry_context."""
try:
class Meta: username = g.user.username
"""Sets the fields to search the error schema for.""" except Exception:
username = "Unknown"
fields = ( # This is for sentry logging into Slack
"error_code", sentry_sdk.set_context("User", {"user": username})
"message", set_user({"username": username})
"workflow_name",
"file_name",
"task_name",
"task_id",
"task_data",
"task_user",
"hint",
"line_number",
"offset",
"error_type",
"error_line",
"task_trace",
)
@api_error_blueprint.app_errorhandler(ApiError) @api_error_blueprint.app_errorhandler(ApiError)
def handle_invalid_usage(error: ApiError) -> tuple[str, int]: def handle_invalid_usage(error: ApiError) -> flask.wrappers.Response:
"""Handles invalid usage error.""" """Handles invalid usage error."""
response = ApiErrorSchema().dump(error) return make_response(jsonify(error), error.status_code)
return response, error.status_code
@api_error_blueprint.app_errorhandler(InternalServerError) @api_error_blueprint.app_errorhandler(InternalServerError)
def handle_internal_server_error(error: InternalServerError) -> tuple[str, int]: def handle_internal_server_error(error: InternalServerError) -> flask.wrappers.Response:
"""Handles internal server error.""" """Handles internal server error."""
original = getattr(error, "original_exception", None) original = getattr(error, "original_exception", None)
api_error = ApiError( api_error = ApiError(error_code="internal_server_error", message=str(original))
error_code="Internal Server Error (500)", message=str(original) return make_response(jsonify(api_error), 500)
@api_error_blueprint.app_errorhandler(Exception)
def handle_internal_server_exception(exception: Exception) -> flask.wrappers.Response:
"""Handles unexpected exceptions."""
set_user_sentry_context()
id = capture_exception(exception)
organization_slug = current_app.config.get("SENTRY_ORGANIZATION_SLUG")
project_slug = current_app.config.get("SENTRY_PROJECT_SLUG")
sentry_link = None
if organization_slug and project_slug:
sentry_link = (
f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}"
) )
response = ApiErrorSchema().dump(api_error)
return response, 500 api_exception = ApiError(
error_code="error",
message=f"{exception.__class__.__name__}",
sentry_link=sentry_link,
)
return make_response(jsonify(api_exception), 500)