Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions services/resend/text/issues_disabled_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from config import PRODUCT_NAME
from constants.urls import SETTINGS_TRIGGERS_URL
from services.resend.constants import EMAIL_SIGNATURE


def get_issues_disabled_email_text(user_name: str | None, owner: str, repo: str):
subject = "Enable Issues to use GitAuto"

text = f"""Hi {user_name},

{PRODUCT_NAME} couldn't run on {owner}/{repo} because GitHub Issues are disabled. To continue:

1. Enable GitHub Issues: https://github.com/{owner}/{repo}/settings
2. Re-enable {PRODUCT_NAME} schedule: {SETTINGS_TRIGGERS_URL}

{EMAIL_SIGNATURE}"""

return subject, text
121 changes: 121 additions & 0 deletions services/resend/text/test_issues_disabled_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from unittest.mock import patch

import pytest

from services.resend.text.issues_disabled_email import get_issues_disabled_email_text


def test_get_issues_disabled_email_text_basic():
subject, text = get_issues_disabled_email_text("John", "owner", "repo")

assert subject == "Enable Issues to use GitAuto"
assert "Hi John," in text
assert "GitHub Issues are disabled" in text
assert "https://github.com/owner/repo/settings" in text
assert "Re-enable" in text
assert "Wes\nGitAuto" in text


def test_get_issues_disabled_email_text_with_full_name():
subject, text = get_issues_disabled_email_text("John Doe", "myorg", "myrepo")

assert subject == "Enable Issues to use GitAuto"
assert "Hi John Doe," in text
assert "myorg/myrepo" in text
assert "https://github.com/myorg/myrepo/settings" in text


def test_get_issues_disabled_email_text_with_special_characters():
subject, text = get_issues_disabled_email_text(
"José María", "org-name", "repo_name"
)

assert subject == "Enable Issues to use GitAuto"
assert "Hi José María," in text
assert "org-name/repo_name" in text
assert "https://github.com/org-name/repo_name/settings" in text


def test_get_issues_disabled_email_text_with_empty_string():
subject, text = get_issues_disabled_email_text("", "owner", "repo")

assert subject == "Enable Issues to use GitAuto"
assert "Hi ," in text
assert "owner/repo" in text


def test_get_issues_disabled_email_text_with_none():
subject, text = get_issues_disabled_email_text(None, "owner", "repo")

assert subject == "Enable Issues to use GitAuto"
assert "Hi None," in text
assert "owner/repo" in text


def test_get_issues_disabled_email_text_includes_instructions():
_, text = get_issues_disabled_email_text("Alice", "owner", "repo")

assert "https://github.com/owner/repo/settings" in text
assert "Re-enable" in text
assert "schedule" in text


def test_get_issues_disabled_email_text_includes_settings_url():
_, text = get_issues_disabled_email_text("Bob", "owner", "repo")

assert "gitauto.ai/settings/triggers" in text


def test_get_issues_disabled_email_text_includes_email_signature():
with patch(
"services.resend.text.issues_disabled_email.EMAIL_SIGNATURE",
"Custom Signature",
):
_, text = get_issues_disabled_email_text("Bob", "owner", "repo")

assert "Custom Signature" in text
assert text.endswith("Custom Signature")


def test_get_issues_disabled_email_text_return_type():
result = get_issues_disabled_email_text("Test User", "owner", "repo")

assert isinstance(result, tuple)
assert len(result) == 2
assert isinstance(result[0], str)
assert isinstance(result[1], str)


@pytest.mark.parametrize(
"user_name,owner,repo,expected_greeting,expected_repo",
[
("Alice", "owner1", "repo1", "Hi Alice,", "owner1/repo1"),
("Bob Smith", "myorg", "myrepo", "Hi Bob Smith,", "myorg/myrepo"),
("李小明", "chinese-org", "test-repo", "Hi 李小明,", "chinese-org/test-repo"),
("O'Connor", "org", "app", "Hi O'Connor,", "org/app"),
(
"user@example.com",
"company",
"project",
"Hi user@example.com,",
"company/project",
),
("123", "num-org", "num-repo", "Hi 123,", "num-org/num-repo"),
(
"user-name_test",
"test-org",
"test_repo",
"Hi user-name_test,",
"test-org/test_repo",
),
],
)
def test_get_issues_disabled_email_text_parametrized(
user_name, owner, repo, expected_greeting, expected_repo
):
subject, text = get_issues_disabled_email_text(user_name, owner, repo)

assert subject == "Enable Issues to use GitAuto"
assert expected_greeting in text
assert expected_repo in text
assert f"https://github.com/{owner}/{repo}/settings" in text
7 changes: 5 additions & 2 deletions services/supabase/users/get_user.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import Any
from typing import cast

from schemas.supabase.types import Users
from services.supabase.client import supabase
from utils.error.handle_exceptions import handle_exceptions

Expand All @@ -12,6 +14,7 @@ def get_user(user_id: int):
.execute()
)
if len(data[1]) > 0:
user: dict[str, Any] = data[1][0]
user = cast(Users, data[1][0])
return user

return None
13 changes: 13 additions & 0 deletions services/webhook/schedule_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from services.github.trees.get_file_tree import get_file_tree

# Local imports (Notifications)
from services.resend.send_email import send_email
from services.resend.text.issues_disabled_email import get_issues_disabled_email_text
from services.slack.slack_notify import slack_notify

# Local imports (Supabase)
Expand All @@ -27,6 +29,7 @@
from services.supabase.coverages.update_issue_url import update_issue_url
from services.supabase.repositories.get_repository import get_repository
from services.supabase.repositories.update_repository import update_repository
from services.supabase.users.get_user import get_user
from services.stripe.check_availability import check_availability

# Local imports (Utils)
Expand Down Expand Up @@ -279,6 +282,16 @@ def schedule_handler(event: EventBridgeSchedulerEvent):
schedule_name = f"gitauto-repo-{owner_id}-{repo_id}"
delete_scheduler(schedule_name)

# Send email notification to user
if user_id:
user = get_user(user_id=user_id)
email = user.get("email") if user else None
if email:
subject, text = get_issues_disabled_email_text(
user_name, owner_name, repo_name
)
send_email(to=email, subject=subject, text=text)

msg = f"Issues are disabled for {owner_name}/{repo_name}. Disabled schedule trigger."
logging.warning(msg)
slack_notify(msg)
Expand Down
20 changes: 20 additions & 0 deletions services/webhook/test_schedule_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ def mock_empty_content_side_effect(file_path=None, **_):
assert "src/app.ts" in call_kwargs["title"]
assert result["status"] == "success"

@patch("services.webhook.schedule_handler.send_email")
@patch("services.webhook.schedule_handler.get_user")
@patch("services.webhook.schedule_handler.slack_notify")
@patch("services.webhook.schedule_handler.delete_scheduler")
@patch("services.webhook.schedule_handler.update_repository")
Expand Down Expand Up @@ -383,6 +385,8 @@ def test_schedule_handler_410_issues_disabled(
mock_update_repository,
mock_delete_scheduler,
mock_slack_notify,
mock_get_user,
mock_send_email,
mock_event,
):
"""Test that schedule_handler handles 410 (issues disabled) correctly."""
Expand Down Expand Up @@ -434,6 +438,9 @@ def test_schedule_handler_410_issues_disabled(
# Mock create_issue to return 410 (issues disabled)
mock_create_issue.return_value = (410, None)

# Mock get_user to return user with email
mock_get_user.return_value = {"email": "test@example.com", "user_id": 789}

result = schedule_handler(cast(EventBridgeSchedulerEvent, mock_event))

# Verify the function handled 410 correctly
Expand All @@ -448,6 +455,19 @@ def test_schedule_handler_410_issues_disabled(
# Verify that it deleted the AWS scheduler
mock_delete_scheduler.assert_called_once_with("gitauto-repo-123-456")

# Verify that it sent email notification to user
mock_get_user.assert_called_once_with(user_id=789)
mock_send_email.assert_called_once()
email_call = mock_send_email.call_args
assert email_call.kwargs["to"] == "test@example.com"
assert email_call.kwargs["subject"] == "Enable Issues to use GitAuto"
assert "Hi test-user," in email_call.kwargs["text"]
assert "test-org/test-repo" in email_call.kwargs["text"]
assert (
"https://github.com/test-org/test-repo/settings"
in email_call.kwargs["text"]
)

# Verify that it sent a Slack notification
mock_slack_notify.assert_called_once()
slack_msg = mock_slack_notify.call_args[0][0]
Expand Down