Skip to content

Commit

Permalink
✨(backend) add admin action to check domain health
Browse files Browse the repository at this point in the history
Allow to select some domains to check status
thanks to a dimail call.
  • Loading branch information
sdemagny committed Jan 9, 2025
1 parent 66f44af commit 99aa404
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to

### Added

- ✨(backend) add admin action to check domain health
- ✨(dimail) check domain health
- ✨(frontend) disable mailbox and allow to create pending mailbox
- ✨(organizations) add siret to name conversion #584
Expand Down
3 changes: 3 additions & 0 deletions src/backend/admin/templates/admin/base_site.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
{% if ADMIN_HEADER_BACKGROUND %}--header-bg: {{ ADMIN_HEADER_BACKGROUND }};{% endif %}
{% if ADMIN_HEADER_COLOR %}--header-color: {{ ADMIN_HEADER_COLOR }};{% endif %}
}
ul.messagelist li.info {
background-color: #EEEEEE;
}
</style>
{% endblock %}
49 changes: 47 additions & 2 deletions src/backend/mailbox_manager/admin.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"""Admin classes and registrations for People's mailbox manager app."""

from django.contrib import admin, messages
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _

from requests import exceptions

from mailbox_manager import models
from mailbox_manager import enums, models
from mailbox_manager.utils.dimail import DimailAPIClient


Expand All @@ -32,6 +33,50 @@ def sync_mailboxes_from_dimail(modeladmin, request, queryset): # pylint: disabl
)


@admin.action(description=_("Check domain health"))
def check_domain_health(modeladmin, request, queryset): # pylint: disable=unused-argument
"""Admin action to check domain health with dimail and update domain status."""
client = DimailAPIClient()
domains_updated, excluded_domains, msg_error = [], [], []
success = False
for domain in queryset:
# do not check disabled domains
if domain.status == enums.MailDomainStatusChoices.DISABLED:
excluded_domains.append(domain.name)
continue

old_status = domain.status
try:
response = client.fetch_domain_status(domain)
except exceptions.HTTPError as err:
msg_error.append(_(f"""- <b>{domain.name}</b> with message: '{err}'"""))
else:
success = True
# temporary display content of the response to debug broken state
if domain.status == enums.MailDomainStatusChoices.FAILED:
messages.info(request, response.json())
if old_status != domain.status:
domains_updated.append(domain.name)
if success:
msg_success = [
_("Check domains done with success."),
_(f"Domains updated: {', '.join(domains_updated)}")
if domains_updated
else _("No domain updated."),
]
messages.success(request, format_html("<br> ".join(map(str, msg_success))))
if msg_error:
msg_error.insert(0, _("Check domain failed for:"))
messages.error(request, format_html("<br> ".join(map(str, msg_error))))
if excluded_domains:
messages.warning(
request,
_(
f"Domains disabled are excluded from check: {', '.join(excluded_domains)}"
),
)


class UserMailDomainAccessInline(admin.TabularInline):
"""Inline admin class for mail domain accesses."""

Expand All @@ -54,7 +99,7 @@ class MailDomainAdmin(admin.ModelAdmin):
search_fields = ("name",)
readonly_fields = ["created_at", "slug"]
inlines = (UserMailDomainAccessInline,)
actions = (sync_mailboxes_from_dimail,)
actions = (sync_mailboxes_from_dimail, check_domain_health)


@admin.register(models.Mailbox)
Expand Down
78 changes: 78 additions & 0 deletions src/backend/mailbox_manager/tests/test_admin_actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
Unit tests for admin actions
"""

import json
import re

from django.urls import reverse

import pytest
import responses

from core import factories as core_factories

from mailbox_manager import enums, factories

from .fixtures.dimail import CHECK_DOMAIN_BROKEN


@pytest.mark.django_db
def test_admin_action__check_domain_health(client):
"""Test admin action to check health of some domains"""
admin = core_factories.UserFactory(is_staff=True, is_superuser=True)
client.force_login(admin)
domain1 = factories.MailDomainEnabledFactory()
domain2 = factories.MailDomainEnabledFactory()
data = {
"action": "check_domain_health",
"_selected_action": [
domain1.id,
domain2.id,
],
}
url = reverse("admin:mailbox_manager_maildomain_changelist")

with responses.RequestsMock() as rsps:
body_content_domain1 = CHECK_DOMAIN_BROKEN.copy()
body_content_domain1["name"] = domain1.name
body_content_domain2 = CHECK_DOMAIN_BROKEN.copy()
body_content_domain2["name"] = domain2.name
rsps.add(
rsps.GET,
re.compile(rf".*/domains/{domain1.name}/check/"),
body=json.dumps(body_content_domain1),
status=200,
content_type="application/json",
)
rsps.add(
rsps.GET,
re.compile(rf".*/domains/{domain2.name}/check/"),
body=json.dumps(body_content_domain2),
status=200,
content_type="application/json",
)
response = client.post(url, data, follow=True)
assert response.status_code == 200
domain1.refresh_from_db()
domain2.refresh_from_db()
assert domain1.status == enums.MailDomainStatusChoices.FAILED
assert domain2.status == enums.MailDomainStatusChoices.FAILED
assert "Check domain done with success" in response.content.decode("utf-8")

# check with a valid domain info from dimail
body_content_domain1["state"] = "ok"
rsps.add(
rsps.GET,
re.compile(rf".*/domains/{domain1.name}/check/"),
body=json.dumps(body_content_domain1),
status=200,
content_type="application/json",
)
response = client.post(url, data, follow=True)
assert response.status_code == 200
domain1.refresh_from_db()
domain2.refresh_from_db()
assert domain1.status == enums.MailDomainStatusChoices.ENABLED
assert domain2.status == enums.MailDomainStatusChoices.FAILED
assert "Check domain done with success" in response.content.decode("utf-8")

0 comments on commit 99aa404

Please sign in to comment.