-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #381 from bcgov/release/2024.10.1
Release/2024.10.1 to main
- Loading branch information
Showing
57 changed files
with
2,068 additions
and
1,783 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
from enum import Enum | ||
from dataclasses import dataclass | ||
|
||
@dataclass | ||
class EnumDetails: | ||
code: str | ||
description: str | ||
|
||
class BaseEnum(Enum): | ||
def __str__(self): | ||
return self.value.code | ||
|
||
@property | ||
def code(self): | ||
return self.value.code | ||
|
||
@property | ||
def description(self): | ||
return self.value.description | ||
|
||
class EventType(BaseEnum): | ||
TWELVE_HOUR = EnumDetails("TwelveHour", "12 Hour Prohibition") | ||
TWENTY_FOUR_HOUR = EnumDetails("TwentyFourHour", "24 Hour Prohibition") | ||
IRP = EnumDetails("IRP", "Immediate Roadside Prohibition") | ||
VI = EnumDetails("VI", "Vehicle Impoundment") | ||
OTHER = EnumDetails("OTHER", "Other") | ||
|
||
class ErrorCategory(BaseEnum): | ||
VALIDATION = EnumDetails("VALIDATION", "Validation Error") | ||
SYSTEM = EnumDetails("SYSTEM", "System Error") | ||
CONNECTION = EnumDetails("CONNECTION", "Connection Error") | ||
DATA = EnumDetails("DATA", "Data Error") | ||
OTHER = EnumDetails("OTHER", "Other Error") | ||
|
||
class ErrorSeverity(BaseEnum): | ||
LOW = EnumDetails("LOW", "Low Severity") | ||
MEDIUM = EnumDetails("MEDIUM", "Medium Severity") | ||
HIGH = EnumDetails("HIGH", "High Severity") | ||
CRITICAL = EnumDetails("CRITICAL", "Critical Severity") | ||
|
||
class ErrorStatus(BaseEnum): | ||
NEW = EnumDetails("NEW", "New Error") | ||
VIEWED = EnumDetails("VIEWED", "Viewed Error") | ||
IN_PROGRESS = EnumDetails("IN_PROGRESS", "In Progress") | ||
ASSIGNED = EnumDetails("ASSIGNED", "Assigned") | ||
RESOLVED = EnumDetails("RESOLVED", "Resolved") | ||
CANCELLED = EnumDetails("CANCELLED", "Cancelled") | ||
CLOSED = EnumDetails("CLOSED", "Closed") | ||
|
||
@dataclass | ||
class ErrorCodeDetails: | ||
code: str | ||
description: str # max 200 characters | ||
category: ErrorCategory | ||
severity: ErrorSeverity | ||
resolution: str | ||
is_business_error: bool | ||
|
||
class ErrorCode(BaseEnum): | ||
# General error | ||
G00 = ErrorCodeDetails("G00", "General error", ErrorCategory.OTHER, ErrorSeverity.LOW, | ||
"Contact DF application support for further investigation", False) | ||
|
||
# Events related error | ||
|
||
E01 = ErrorCodeDetails("E01", "Event saving error", ErrorCategory.DATA, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
E02 = ErrorCodeDetails("E02", "Event PDF saving error", ErrorCategory.DATA, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
|
||
E03 = ErrorCodeDetails("E03", "Error putting event to queue", ErrorCategory.SYSTEM, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
E04 = ErrorCodeDetails("E04", "Form handler: Unknown Event Type", ErrorCategory.DATA, ErrorSeverity.CRITICAL, | ||
"Contact DF application support for further investigation", False) | ||
E05 = ErrorCodeDetails("E05", "Form handler: Retry count exceed maximum retires", ErrorCategory.DATA, ErrorSeverity.CRITICAL, | ||
"Contact DF application support for further investigation", False) | ||
E06 = ErrorCodeDetails("E06", "Form handler: Event On Hold", ErrorCategory.DATA, ErrorSeverity.CRITICAL, | ||
"Contact DF application support for further investigation", False) | ||
E07 = ErrorCodeDetails("E07", "Form handlerr: Event process error", ErrorCategory.DATA, ErrorSeverity.CRITICAL, | ||
"Contact DF application support for further investigation", False) | ||
E08 = ErrorCodeDetails("E08", "General Event process error", ErrorCategory.SYSTEM, ErrorSeverity.CRITICAL, | ||
"Contact DF application support for further investigation", False) | ||
|
||
# Forms related error | ||
|
||
F01 = ErrorCodeDetails("F01", "Form id lease error", ErrorCategory.DATA, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
F02 = ErrorCodeDetails("F02", "Renew form id lease error", ErrorCategory.DATA, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
F03 = ErrorCodeDetails("F02", "Admin form create error", ErrorCategory.DATA, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
|
||
# Ride actions related error | ||
|
||
R01 = ErrorCodeDetails("R01", "Error in sending event to RIDE", ErrorCategory.CONNECTION, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
|
||
# ICBC actions related error | ||
|
||
I01 = ErrorCodeDetails("I01", "Error in sending event to ICBC", ErrorCategory.CONNECTION, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
I02 = ErrorCodeDetails("I02", "Error in preparing payload to ICBC", ErrorCategory.DATA, ErrorSeverity.HIGH, | ||
"Contact DF application support for further investigation", False) | ||
|
||
|
||
# Geocoding location related error | ||
|
||
L01 = ErrorCodeDetails("L01", "Error in getting coordinates", ErrorCategory.CONNECTION, ErrorSeverity.MEDIUM, | ||
"Contact DF application support for further investigation", False) | ||
|
||
|
||
# Add more error codes as needed... | ||
|
||
@property | ||
def category(self): | ||
return self.value.category | ||
|
||
@property | ||
def severity(self): | ||
return self.value.severity | ||
|
||
@property | ||
def resolution(self): | ||
return self.value.resolution | ||
|
||
@property | ||
def is_business_error(self): | ||
return self.value.is_business_error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# Error middleware functions | ||
import json | ||
import traceback | ||
import logging | ||
import functools | ||
import inspect | ||
from flask import request, current_app | ||
from sqlalchemy.exc import SQLAlchemyError | ||
from datetime import datetime | ||
|
||
from python.common.enums import EventType, ErrorCode, ErrorSeverity, ErrorStatus, ErrorCategory | ||
from python.common.models import db, DFErrors, Event | ||
|
||
def get_safe_payload(): | ||
""" | ||
Safely extract and serialize the request payload. | ||
""" | ||
try: | ||
# Try to get JSON payload | ||
payload = request.get_json(silent=True) | ||
if payload is not None: | ||
return json.dumps(payload) | ||
|
||
# If not JSON, try to get form data | ||
payload = request.form.to_dict() | ||
if payload: | ||
return json.dumps(payload) | ||
|
||
# If no form data, get query parameters | ||
payload = request.args.to_dict() | ||
if payload: | ||
return json.dumps(payload) | ||
|
||
# If all else fails, return a message indicating no payload | ||
return json.dumps({"message": "No payload found in request"}) | ||
except Exception as e: | ||
return json.dumps({"message": "No payload found in request"}) | ||
|
||
def get_function_info(func): | ||
""" | ||
Extract detailed information about the function or method. | ||
""" | ||
module = inspect.getmodule(func) | ||
if module: | ||
module_name = module.__name__ | ||
else: | ||
module_name = "unknown_module" | ||
|
||
if inspect.ismethod(func): | ||
class_name = func.__self__.__class__.__name__ | ||
return f"{module_name}.{class_name}.{func.__name__}" | ||
elif inspect.isfunction(func): | ||
return f"{module_name}.{func.__name__}" | ||
else: | ||
return f"{module_name}.unknown_function" | ||
|
||
def record_error(error_code: ErrorCode, error_details, event_id: int = None, event_type: EventType = None, ticket_no=None, func=None, payload=None): | ||
""" | ||
Record an error in the database. | ||
""" | ||
try: | ||
|
||
function_path = get_function_info(func) if func else "unknown" | ||
|
||
if not payload: | ||
payload = get_safe_payload() | ||
|
||
# Handle stack trace extraction if error_details is an exception | ||
if isinstance(error_details, Exception): | ||
stack_trace = ''.join(traceback.format_exception(type(error_details), error_details, error_details.__traceback__)) | ||
else: | ||
stack_trace = str(error_details) | ||
|
||
error_log = DFErrors( | ||
error_cd=str(error_code.code), | ||
error_cd_desc=str(error_code.description), | ||
error_category_cd=str(error_code.category), | ||
error_resolution=str(error_code.resolution), | ||
error_severity_level_cd=str(error_code.severity), | ||
error_status_cd=str(ErrorStatus.NEW), | ||
event_id=event_id, | ||
event_type=str(event_type) if event_type else None, | ||
ticket_no=ticket_no, | ||
req_payload=payload, | ||
error_details=stack_trace, | ||
error_path=function_path, | ||
created_by='SYSTEM', | ||
received_dt=datetime.now(), | ||
) | ||
db.session.add(error_log) | ||
db.session.commit() | ||
logging.error(f"Error recorded: {error_code} - {error_code.description} - Event ID: {event_id} - Event Type: {event_type} - Function: {function_path} - {error_details}") | ||
except SQLAlchemyError as e: | ||
db.session.rollback() | ||
logging.error(f"Failed to record error: {error_code} - {error_code.description} - Event ID: {event_id} - Event Type: {event_type} - Function: {function_path} - {error_details}") | ||
|
||
def error_handler(func): | ||
""" | ||
Decorator to handle errors in functions. | ||
""" | ||
@functools.wraps(func) | ||
def wrapper(*args, **kwargs): | ||
try: | ||
return func(*args, **kwargs) | ||
except Exception as e: | ||
error_code = ErrorCode.G00 # Default to general error | ||
error_details = str(e) | ||
|
||
# Attempt to get event_id from kwargs or request | ||
event_id = kwargs.get('event_id') | ||
if not event_id and hasattr(request, 'view_args'): | ||
event_id = request.view_args.get('event_id') | ||
|
||
if not event_id: | ||
current_app.logger.warning("No event_id found for error logging") | ||
event_id = None # or some default value | ||
|
||
record_error(error_code, error_details, event_id, func=func) | ||
raise # Re-raise the exception after recording | ||
return wrapper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
|
||
import logging | ||
from python.common.models import City | ||
|
||
|
||
def get_city_name(city_code, args) -> str: | ||
application = args.get('app') | ||
db = args.get('db') | ||
with application.app_context(): | ||
city_data = db.session.query(City) \ | ||
.filter(City.objectCd == city_code) \ | ||
.all() | ||
if len(city_data) == 0: | ||
logging.error("city not found") | ||
else: | ||
return city_data[0].objectDsc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.