feat(email): HTML branded templates with inline logo, wire remaining notification types
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
- All webhook emails now render as branded HTML (dark header, inline base64 Aegis logo, card layout, CTA-button links) instead of plain text. - Wired the 7 remaining notification-preference keys that had no trigger: stale coverage alerts, campaign-activated assignment emails, generic test-state-change steps (execution started / blue evaluating / in review), all-team-validation broadcasts on every lead vote, webhook delivery failures (3rd consecutive failure), new user registration, and background job errors (APScheduler global error listener). - New notify_roles_by_email() helper for role-scoped, preference-gated, actor-excludable broadcasts. - Fixed apscheduler.events stubbing gaps in several test files' sys.modules fakes that broke full-suite collection after adding the APScheduler error-listener import.
This commit is contained in:
@@ -291,6 +291,30 @@ def notify_all_users_by_email(db: Session, *, preference_key: str, subject: str,
|
||||
pass # nosec B110 — one user's failure must not skip the rest
|
||||
|
||||
|
||||
def notify_roles_by_email(
|
||||
db: Session, *, roles: list[str], preference_key: str, subject: str, message: str,
|
||||
exclude_user_id=None,
|
||||
) -> None:
|
||||
"""Email every active, opted-in user holding any of *roles*.
|
||||
|
||||
For workflow-wide callouts to a team (e.g. every lead vote) rather than
|
||||
a single actor's own action — no in-app notification is created here,
|
||||
only the email; pair with ``create_notification``/``notify_role_with_email``
|
||||
if an in-app entry is also needed.
|
||||
"""
|
||||
users = db.query(User).filter(User.role.in_(roles), User.is_active == True).all() # noqa: E712
|
||||
for user in users:
|
||||
if exclude_user_id and user.id == exclude_user_id:
|
||||
continue
|
||||
if not _preference_allows(user, preference_key):
|
||||
continue
|
||||
try:
|
||||
from app.services.webhook_email_service import send_webhook_email
|
||||
send_webhook_email(db, to=user.email, subject=subject, message=message, full_name=user.full_name)
|
||||
except Exception:
|
||||
pass # nosec B110 — one user's failure must not skip the rest
|
||||
|
||||
|
||||
def notify_role_with_email(
|
||||
db: Session,
|
||||
*,
|
||||
@@ -362,6 +386,12 @@ def notify_test_state_change(db: Session, test, new_state: str) -> None:
|
||||
# Keyword argument: entity_id
|
||||
entity_id=test_id,
|
||||
)
|
||||
notify_user_by_email(
|
||||
db, creator_id,
|
||||
preference_key="email_on_test_state_change",
|
||||
subject=f"Test Execution Started: {test_name}",
|
||||
message=f'Your test "{test_name}" has moved to the execution phase.',
|
||||
)
|
||||
|
||||
# Alternative: new_state == "blue_evaluating"
|
||||
elif new_state == "blue_evaluating":
|
||||
@@ -385,6 +415,12 @@ def notify_test_state_change(db: Session, test, new_state: str) -> None:
|
||||
# Keyword argument: entity_id
|
||||
entity_id=test_id,
|
||||
)
|
||||
notify_user_by_email(
|
||||
db, user.id,
|
||||
preference_key="email_on_test_state_change",
|
||||
subject=f"Test Ready for Blue Evaluation: {test_name}",
|
||||
message=f'Test "{test_name}" needs blue team evaluation.',
|
||||
)
|
||||
|
||||
# Alternative: new_state == "in_review"
|
||||
elif new_state == "in_review":
|
||||
@@ -414,6 +450,12 @@ def notify_test_state_change(db: Session, test, new_state: str) -> None:
|
||||
# Keyword argument: entity_id
|
||||
entity_id=test_id,
|
||||
)
|
||||
notify_user_by_email(
|
||||
db, user.id,
|
||||
preference_key="email_on_test_state_change",
|
||||
subject=f"Test Ready for Validation: {test_name}",
|
||||
message=f'Test "{test_name}" is awaiting your review.',
|
||||
)
|
||||
|
||||
# Alternative: new_state == "rejected" and creator_id
|
||||
elif new_state == "rejected" and creator_id:
|
||||
|
||||
Reference in New Issue
Block a user