-
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 #16 from ARYAN-NIKNEZHAD/fix/settings
🔧 feat: Add custom error handling and enhanced configuration
- Loading branch information
Showing
5 changed files
with
215 additions
and
116 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 |
---|---|---|
@@ -1,2 +1,12 @@ | ||
# constants.py | ||
GEOIP_PATH = None | ||
|
||
SAGE_CONTACT_GEOIP_PATH = None | ||
|
||
EMAIL_CONFIRMATION_SUBJECT = "We have received your contact request" | ||
EMAIL_EXTRA_HEADERS_MIME_VERSION = "1.0" | ||
EMAIL_EXTRA_HEADERS_CONTENT_TYPE = "text/html; charset=UTF-8" | ||
EMAIL_EXTRA_HEADERS_CONTENT_TRANSFER_ENCODING = "quoted-printable" | ||
EMAIL_EXTRA_HEADERS_X_PRIORITY = "3" | ||
EMAIL_EXTRA_HEADERS_X_AUTO_RESPONSE_SUPPRESS = "All" | ||
EMAIL_EXTRA_HEADERS_X_SPAMD_RESULT = "default: False [-0.90 / 15.00]" |
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
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 |
---|---|---|
@@ -1,122 +1,127 @@ | ||
# check.py | ||
import os | ||
|
||
from django.conf import settings | ||
from django.core.checks import Error, register | ||
from django.template.utils import get_app_template_dirs | ||
from typing import List, Dict, Any | ||
import os | ||
|
||
from .exc import DjangoSageContactError, DjangoSageContactConfigurationError | ||
|
||
|
||
@register() | ||
def check_geoip_path(app_configs, **kwargs): | ||
errors = [] | ||
geoip_path = getattr(settings, "GEOIP_PATH", None) | ||
def check_django_sage_contact_config(app_configs: Dict[str, Any], **kwargs: Any) -> List[Error]: | ||
""" | ||
Check the django-sage-contact and email template configuration for the application. | ||
if geoip_path is not None and not os.path.exists(geoip_path): | ||
errors.append( | ||
Error( | ||
"GEOIP_PATH is set to a non-existent path", | ||
hint="Ensure the path set in GEOIP_PATH exists.", | ||
id="geoip.E002", | ||
) | ||
) | ||
This function verifies that all required settings are present and ensures the settings are correct. | ||
Any errors encountered during these checks are returned. | ||
Parameters | ||
---------- | ||
app_configs : dict | ||
The application configurations. | ||
**kwargs | ||
Additional keyword arguments. | ||
Returns | ||
------- | ||
list of Error | ||
A list of Error objects representing any configuration errors found. | ||
# Check for SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH | ||
send_email = getattr(settings, "SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM", True) | ||
template_path = getattr(settings, "SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH", None) | ||
Examples | ||
-------- | ||
>>> errors = check_geoip_path(app_configs) | ||
>>> if errors: | ||
... for error in errors: | ||
... print(error) | ||
""" | ||
errors: List[Error] = [] | ||
|
||
if send_email: | ||
if not template_path: | ||
def get_settings() -> Dict[str, Any]: | ||
return { | ||
"SAGE_CONTACT_GEOIP_PATH": getattr(settings, 'SAGE_CONTACT_GEOIP_PATH', None), | ||
"SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM": getattr(settings, 'SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM', True), | ||
"SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH": getattr(settings, 'SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH', None), | ||
"EMAIL_HOST": getattr(settings, 'EMAIL_HOST', None), | ||
"EMAIL_PORT": getattr(settings, 'EMAIL_PORT', None), | ||
"EMAIL_HOST_USER": getattr(settings, 'EMAIL_HOST_USER', None), | ||
"EMAIL_HOST_PASSWORD": getattr(settings, 'EMAIL_HOST_PASSWORD', None), | ||
"EMAIL_USE_TLS": getattr(settings, 'EMAIL_USE_TLS', None), | ||
"BASE_DIR": getattr(settings, 'BASE_DIR', None), | ||
"DEBUG": getattr(settings, 'DEBUG', True), | ||
} | ||
|
||
def check_geoip_path_setting(geoip_path: str) -> None: | ||
if geoip_path and not os.path.exists(geoip_path): | ||
errors.append( | ||
Error( | ||
"SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH is not set", | ||
hint="Set the SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH in settings.", | ||
id="sage_contact.E001", | ||
'SAGE_CONTACT_GEOIP_PATH is set to a non-existent path', | ||
hint='Ensure the path set in SAGE_CONTACT_GEOIP_PATH exists.', | ||
id='geoip.E002', | ||
) | ||
) | ||
else: | ||
# Check in BASE_DIR first | ||
if not os.path.exists(os.path.join(settings.BASE_DIR, template_path)): | ||
# If not found in BASE_DIR, check in app template directories | ||
template_found = False | ||
for template_dir in get_app_template_dirs("templates"): | ||
if os.path.exists(os.path.join(template_dir, template_path)): | ||
template_found = True | ||
break | ||
if not template_found: | ||
errors.append( | ||
Error( | ||
"SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH is set to a non-existent path", | ||
hint="Ensure the path set in SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH exists.", | ||
id="sage_contact.E002", | ||
|
||
def check_email_template_path(send_email: bool, template_path: str, base_dir: str) -> None: | ||
if send_email: | ||
if not template_path: | ||
raise DjangoSageContactConfigurationError( | ||
detail='SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH is not set', | ||
code='E001', | ||
section_code='sage_contact' | ||
) | ||
else: | ||
if not os.path.exists(os.path.join(base_dir, template_path)): | ||
template_found = False | ||
for template_dir in get_app_template_dirs('templates'): | ||
if os.path.exists(os.path.join(template_dir, template_path)): | ||
template_found = True | ||
break | ||
if not template_found: | ||
raise DjangoSageContactConfigurationError( | ||
detail='SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH is set to a non-existent path', | ||
code='E002', | ||
section_code='sage_contact' | ||
) | ||
|
||
def check_email_settings(send_email: bool, debug: bool) -> None: | ||
if not debug and send_email: | ||
email_settings: List[str] = ['EMAIL_HOST', 'EMAIL_PORT', 'EMAIL_HOST_USER', 'EMAIL_HOST_PASSWORD', 'EMAIL_USE_TLS'] | ||
for setting in email_settings: | ||
if not getattr(settings, setting, None): | ||
raise DjangoSageContactConfigurationError( | ||
detail=f'{setting} is not set. The {setting} must be set in settings because DEBUG is False and ' | ||
'SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM is True. Ensure all necessary ' | ||
'email settings are configured for the application to send emails in ' | ||
'production.', | ||
code=f'E00{email_settings.index(setting) + 1}', | ||
section_code='sage_contact' | ||
) | ||
|
||
# Additional check for email settings when DEBUG is False | ||
if not settings.DEBUG and send_email: | ||
if not getattr(settings, "EMAIL_HOST", None): | ||
errors.append( | ||
Error( | ||
"EMAIL_HOST is not set", | ||
hint=( | ||
"The EMAIL_HOST must be set in settings because DEBUG is False and " | ||
"SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM is True. Ensure all necessary " | ||
"email settings are configured for the application to send emails in " | ||
"production." | ||
), | ||
id="sage_contact.E001", | ||
) | ||
) | ||
if not getattr(settings, "EMAIL_PORT", None): | ||
errors.append( | ||
Error( | ||
"EMAIL_PORT is not set", | ||
hint=( | ||
"The EMAIL_PORT must be set in settings because DEBUG is False and " | ||
"SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM is True. Ensure all necessary " | ||
"email settings are configured for the application to send emails in " | ||
"production." | ||
), | ||
id="sage_contact.E002", | ||
) | ||
) | ||
if not getattr(settings, "EMAIL_HOST_USER", None): | ||
errors.append( | ||
Error( | ||
"EMAIL_HOST_USER is not set", | ||
hint=( | ||
"The EMAIL_HOST_USER must be set in settings because DEBUG is False and " | ||
"SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM is True. Ensure all necessary " | ||
"email settings are configured for the application to send emails in " | ||
"production." | ||
), | ||
id="sage_contact.E003", | ||
) | ||
) | ||
if not getattr(settings, "EMAIL_HOST_PASSWORD", None): | ||
errors.append( | ||
Error( | ||
"EMAIL_HOST_PASSWORD is not set", | ||
hint=( | ||
"The EMAIL_HOST_PASSWORD must be set in settings because DEBUG is False and " | ||
"SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM is True. Ensure all necessary " | ||
"email settings are configured for the application to send emails in " | ||
"production." | ||
), | ||
id="sage_contact.E004", | ||
) | ||
try: | ||
settings_dict: Dict[str, Any] = get_settings() | ||
# get settings | ||
geoip_path: str = settings_dict["SAGE_CONTACT_GEOIP_PATH"] | ||
send_email_after_sage_contact_support_form: str = settings_dict["SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM"] | ||
sage_contact_support_email_template_path: str = settings_dict["SAGE_CONTACT_SUPPORT_EMAIL_TEMPLATE_PATH"] | ||
base_dir: str = settings_dict["BASE_DIR"] | ||
debug: bool = settings_dict["DEBUG"] | ||
|
||
check_geoip_path_setting(geoip_path) | ||
check_email_template_path(send_email_after_sage_contact_support_form, sage_contact_support_email_template_path, base_dir) | ||
check_email_settings(send_email_after_sage_contact_support_form, debug) | ||
except DjangoSageContactConfigurationError as e: | ||
errors.append( | ||
Error( | ||
str(e), | ||
id=f"{e.section_code}.{e.code}", | ||
) | ||
if not getattr(settings, "EMAIL_USE_TLS", None): | ||
errors.append( | ||
Error( | ||
"EMAIL_USE_TLS is not set", | ||
hint=( | ||
"The EMAIL_USE_TLS must be set in settings because DEBUG is False and " | ||
"SEND_EMAIL_AFTER_SAGE_CONTACT_SUPPORT_FORM is True. Ensure all necessary " | ||
"email settings are configured for the application to send emails in " | ||
"production." | ||
), | ||
id="sage_contact.E005", | ||
) | ||
) | ||
except DjangoSageContactError as e: | ||
errors.append( | ||
Error( | ||
str(e), | ||
id=f"{e.section_code}.{e.code}", | ||
|
||
) | ||
) | ||
|
||
return errors | ||
return errors |
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,74 @@ | ||
import uuid | ||
from typing import Optional | ||
|
||
|
||
class SageError(Exception): | ||
"""Base class for all Sage exceptions. | ||
Attributes: | ||
status_code (int): HTTP status code associated with the error. | ||
default_detail (str): Default error message. | ||
default_code (str): Default error code. | ||
section_code (str): Section code for categorizing errors. | ||
detail (str): Specific error message. | ||
code (str): Specific error code. | ||
section_code (str): Section code for the specific error. | ||
error_id (str): Unique identifier for the error instance. | ||
Methods: | ||
__init__(detail: Optional[str] = None, code: Optional[str] = None, section_code: Optional[str] = None): | ||
Initializes the error with specific details. | ||
__str__() -> str: Returns a formatted string representation of the error. | ||
""" | ||
|
||
status_code: int = 500 | ||
default_detail: str = "An error occurred." | ||
default_code: str = "E5000" | ||
section_code: str = "SAGE" | ||
|
||
def __init__(self, detail: Optional[str] = None, code: Optional[str] = None, section_code: Optional[str] = None): | ||
self.detail: str = detail if detail is not None else self.default_detail | ||
self.code: str = code if code is not None else self.default_code | ||
self.section_code: str = section_code if section_code is not None else self.section_code | ||
self.error_id: str = str(uuid.uuid4()) | ||
|
||
def __str__(self) -> str: | ||
return f"Error {self.section_code}{self.code} - {self.detail} (Error ID: {self.error_id})" | ||
|
||
|
||
class DjangoSageContactError(SageError): | ||
"""Exception raised for general django-sage-contact errors. | ||
Inherits from: | ||
SageError | ||
Attributes: | ||
status_code (int): HTTP status code associated with the error. | ||
default_detail (str): Default error message for django-sage-contact errors. | ||
default_code (str): Default error code for django-sage-contact errors. | ||
section_code (str): Section code for django-sage-contact errors. | ||
""" | ||
|
||
status_code: int = 500 | ||
default_detail: str = "A Django-Sage-Contact error occurred." | ||
default_code: str = "E5001" | ||
section_code: str = "DSC" | ||
|
||
|
||
class DjangoSageContactConfigurationError(DjangoSageContactError): | ||
"""Exception raised for django-sage-contact configuration errors. | ||
Inherits from: | ||
DjangoSageContactError | ||
Attributes: | ||
status_code (int): HTTP status code associated with the error. | ||
default_detail (str): Default error message for configuration errors. | ||
default_code (str): Default error code for configuration errors. | ||
section_code (str): Section code for configuration errors. | ||
""" | ||
|
||
status_code: int = 400 | ||
default_detail: str = "Invalid Django-Sage-Contact configuration." | ||
default_code: str = "E4001" | ||
section_code: str = "CFG" |
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