mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-01-14 12:14:46 +00:00
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:
parent
79e2bb98b7
commit
fe10fdbe52
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user