Skip to content
Open
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
27 changes: 25 additions & 2 deletions api/organisations/task_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ def send_api_flags_blocked_notification(organisation: Organisation) -> None:
userorganisation__organisation=organisation,
)

recipient_emails = list(recipient_list.values_list("email", flat=True))
if not recipient_emails:
logger.warning(
"notification.no_recipients_for_blocked_notification",
organisation__id=organisation.id,
)
return

url = get_current_site_url()
context = {
"organisation": organisation,
Expand All @@ -43,7 +51,7 @@ def send_api_flags_blocked_notification(organisation: Organisation) -> None:
subject="Flagsmith API use has been blocked due to overuse",
message=render_to_string(message, context),
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=list(recipient_list.values_list("email", flat=True)),
recipient_list=recipient_emails,
html_message=render_to_string(html_message, context),
fail_silently=True,
)
Expand Down Expand Up @@ -75,6 +83,21 @@ def _send_api_usage_notification(
message = "organisations/api_usage_notification_limit.txt"
html_message = "organisations/api_usage_notification_limit.html"

recipient_emails = list(recipient_list.values_list("email", flat=True))

if not recipient_emails:
logger.warning(
"notification.no_recipients",
organisation__id=organisation.id,
matched_threshold=matched_threshold,
)
OrganisationAPIUsageNotification.objects.create(
organisation=organisation,
percent_usage=matched_threshold,
notified_at=timezone.now(),
)
return

url = get_current_site_url()
context = {
"organisation": organisation,
Expand All @@ -87,7 +110,7 @@ def _send_api_usage_notification(
subject=f"Flagsmith API use has reached {matched_threshold}%",
message=render_to_string(message, context),
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=list(recipient_list.values_list("email", flat=True)),
recipient_list=recipient_emails,
html_message=render_to_string(html_message, context),
fail_silently=True,
)
Expand Down
64 changes: 64 additions & 0 deletions api/tests/unit/organisations/test_unit_organisations_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from organisations.subscriptions.xero.metadata import XeroSubscriptionMetadata
from organisations.task_helpers import (
handle_api_usage_notification_for_organisation,
send_api_flags_blocked_notification,
)
from organisations.tasks import ( # type: ignore[attr-defined]
ALERT_EMAIL_MESSAGE,
Expand Down Expand Up @@ -530,6 +531,45 @@ def test_handle_api_usage_notifications__usage_below_100_percent__sends_90_perce
)


@pytest.mark.django_db
@pytest.mark.freeze_time("2023-01-19T09:09:47.325132+00:00")
Comment thread
SahilJat marked this conversation as resolved.
def test_handle_api_usage_notifications__no_admin_users__skips_notification(
mocker: MockerFixture,
mailoutbox: list[EmailMultiAlternatives],
log: StructuredLogCapture,
enable_features: EnableFeaturesFixture,
) -> None:
# Given - an organisation with no users
organisation = Organisation.objects.create(name="No Users Org")
now = timezone.now()
organisation.subscription.plan = SCALE_UP
organisation.subscription.subscription_id = "fancy_id"
organisation.subscription.save()
OrganisationSubscriptionInformationCache.objects.create(
organisation=organisation,
allowed_30d_api_calls=100,
current_billing_term_starts_at=now - timedelta(days=45),
current_billing_term_ends_at=now + timedelta(days=320),
api_calls_30d=91,
)
mock_api_usage = mocker.patch(
"organisations.task_helpers.get_current_api_usage",
)
mock_api_usage.return_value = 91
enable_features("api_usage_alerting")

# When
handle_api_usage_notifications()

# Then - no email sent, warning logged
assert len(mailoutbox) == 0
assert any(e.get("event") == "notification.no_recipients" for e in log.events)
assert OrganisationAPIUsageNotification.objects.filter(
organisation=organisation,
percent_usage=91,
).exists()


@pytest.mark.freeze_time("2023-01-19T09:09:47.325132+00:00")
def test_handle_api_usage_notifications__usage_below_alert_thresholds__sends_no_email(
mocker: MockerFixture,
Expand Down Expand Up @@ -2136,3 +2176,27 @@ def test_update_organisation_subscription_information_cache__called__calls_updat
SubscriptionCacheEntity.CHARGEBEE, SubscriptionCacheEntity.API_USAGE
)
]


@pytest.mark.django_db
def test_send_api_flags_blocked_notification__no_recipients__skips_notification(
organisation: Organisation,
log: StructuredLogCapture,
mailoutbox: list[EmailMultiAlternatives],
) -> None:
# Given
# Ensure no users are associated with the organisation
UserOrganisation.objects.filter(organisation=organisation).delete()

# When
send_api_flags_blocked_notification(organisation)

# Then
assert len(mailoutbox) == 0
assert log.events == [
{
"level": "warning",
"event": "notification.no_recipients_for_blocked_notification",
"organisation__id": organisation.id,
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
### `api_usage.notification.evaluated`

Logged at `info` from:
- `api/organisations/task_helpers.py:153`
- `api/organisations/task_helpers.py:171`

Attributes:
- `allowed_api_calls`
Expand All @@ -16,15 +16,32 @@ Attributes:
### `api_usage.notification.missing_billing_starts_at`

Logged at `error` from:
- `api/organisations/task_helpers.py:118`
- `api/organisations/task_helpers.py:136`

Attributes:
- `organisation.id`

### `api_usage.notification.no_recipients`

Logged at `warning` from:
- `api/organisations/task_helpers.py:89`

Attributes:
- `matched_threshold`
- `organisation.id`

### `api_usage.notification.no_recipients_for_blocked_notification`

Logged at `warning` from:
- `api/organisations/task_helpers.py:34`

Attributes:
- `organisation.id`

### `api_usage.notification.sent`

Logged at `info` from:
- `api/organisations/task_helpers.py:176`
- `api/organisations/task_helpers.py:194`

Attributes:
- `matched_threshold`
Expand Down
Loading