mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-23 11:16:07 +02:00
Merge pull request #2077 from cisagov/rjm/1849-auditlog-status-changes-trail
Issue #1849: Implement audit log trail for status changes - (RJM)
This commit is contained in:
commit
d945703dc7
4 changed files with 146 additions and 3 deletions
|
@ -112,12 +112,20 @@ html[data-theme="light"] {
|
||||||
.change-list .usa-table--borderless thead th,
|
.change-list .usa-table--borderless thead th,
|
||||||
.change-list .usa-table thead td,
|
.change-list .usa-table thead td,
|
||||||
.change-list .usa-table thead th,
|
.change-list .usa-table thead th,
|
||||||
|
.change-form .usa-table,
|
||||||
|
.change-form .usa-table--striped tbody tr:nth-child(odd) td,
|
||||||
|
.change-form .usa-table--borderless thead th,
|
||||||
|
.change-form .usa-table thead td,
|
||||||
|
.change-form .usa-table thead th,
|
||||||
body.dashboard,
|
body.dashboard,
|
||||||
body.change-list,
|
body.change-list,
|
||||||
body.change-form,
|
body.change-form,
|
||||||
.analytics {
|
.analytics {
|
||||||
color: var(--body-fg);
|
color: var(--body-fg);
|
||||||
}
|
}
|
||||||
|
.usa-table td {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firefox needs this to be specifically set
|
// Firefox needs this to be specifically set
|
||||||
|
@ -127,11 +135,20 @@ html[data-theme="dark"] {
|
||||||
.change-list .usa-table--borderless thead th,
|
.change-list .usa-table--borderless thead th,
|
||||||
.change-list .usa-table thead td,
|
.change-list .usa-table thead td,
|
||||||
.change-list .usa-table thead th,
|
.change-list .usa-table thead th,
|
||||||
|
.change-form .usa-table,
|
||||||
|
.change-form .usa-table--striped tbody tr:nth-child(odd) td,
|
||||||
|
.change-form .usa-table--borderless thead th,
|
||||||
|
.change-form .usa-table thead td,
|
||||||
|
.change-form .usa-table thead th,
|
||||||
body.dashboard,
|
body.dashboard,
|
||||||
body.change-list,
|
body.change-list,
|
||||||
body.change-form {
|
body.change-form,
|
||||||
|
.analytics {
|
||||||
color: var(--body-fg);
|
color: var(--body-fg);
|
||||||
}
|
}
|
||||||
|
.usa-table td {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#branding h1 a:link, #branding h1 a:visited {
|
#branding h1 a:link, #branding h1 a:visited {
|
||||||
|
|
|
@ -16,12 +16,21 @@ from .utility.time_stamped_model import TimeStampedModel
|
||||||
from ..utility.email import send_templated_email, EmailSendingError
|
from ..utility.email import send_templated_email, EmailSendingError
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
|
from auditlog.models import AuditlogHistoryField # type: ignore
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DomainRequest(TimeStampedModel):
|
class DomainRequest(TimeStampedModel):
|
||||||
"""A registrant's domain request for a new domain."""
|
"""A registrant's domain request for a new domain."""
|
||||||
|
|
||||||
|
# https://django-auditlog.readthedocs.io/en/latest/usage.html#object-history
|
||||||
|
# If we note any performace degradation due to this addition,
|
||||||
|
# we can query the auditlogs table in admin.py and add the results to
|
||||||
|
# extra_context in the change_view method for DomainRequestAdmin.
|
||||||
|
# This is the more straightforward way so trying it first.
|
||||||
|
history = AuditlogHistoryField()
|
||||||
|
|
||||||
# Constants for choice fields
|
# Constants for choice fields
|
||||||
class DomainRequestStatus(models.TextChoices):
|
class DomainRequestStatus(models.TextChoices):
|
||||||
STARTED = "started", "Started"
|
STARTED = "started", "Started"
|
||||||
|
|
|
@ -67,7 +67,35 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
{% endblock field_readonly %}
|
{% endblock field_readonly %}
|
||||||
|
|
||||||
{% block after_help_text %}
|
{% block after_help_text %}
|
||||||
{% if field.field.name == "creator" %}
|
{% if field.field.name == "status" and original_object.history.count > 0 %}
|
||||||
|
<div class="flex-container">
|
||||||
|
<label aria-label="Submitter contact details"></label>
|
||||||
|
<div class="usa-table-container--scrollable" tabindex="0">
|
||||||
|
<table class="usa-table usa-table--borderless">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>User</th>
|
||||||
|
<th>Changed at</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for log_entry in original_object.history.all %}
|
||||||
|
{% for key, value in log_entry.changes_display_dict.items %}
|
||||||
|
{% if key == "status" %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ value.1|default:"None" }}</td>
|
||||||
|
<td>{{ log_entry.actor|default:"None" }}</td>
|
||||||
|
<td>{{ log_entry.timestamp|default:"None" }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% elif field.field.name == "creator" %}
|
||||||
<div class="flex-container tablet:margin-top-2">
|
<div class="flex-container tablet:margin-top-2">
|
||||||
<label aria-label="Creator contact details"></label>
|
<label aria-label="Creator contact details"></label>
|
||||||
{% include "django/admin/includes/contact_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly user_verification_type=original_object.creator.get_verification_type_display%}
|
{% include "django/admin/includes/contact_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly user_verification_type=original_object.creator.get_verification_type_display%}
|
||||||
|
|
|
@ -888,6 +888,96 @@ class TestDomainRequestAdmin(MockEppLib):
|
||||||
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
|
def test_status_logs(self):
|
||||||
|
"""
|
||||||
|
Tests that the status changes are shown in a table on the domain request change form,
|
||||||
|
accurately and in chronological order.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create a fake domain request and domain
|
||||||
|
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED)
|
||||||
|
|
||||||
|
p = "adminpass"
|
||||||
|
self.client.login(username="superuser", password=p)
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make sure the page loaded, and that we're on the right page
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, domain_request.requested_domain.name)
|
||||||
|
|
||||||
|
# Table will contain one row for Started
|
||||||
|
self.assertContains(response, "<td>Started</td>", count=1)
|
||||||
|
self.assertNotContains(response, "<td>Submitted</td>")
|
||||||
|
|
||||||
|
domain_request.submit()
|
||||||
|
domain_request.save()
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table will contain and extra row for Submitted
|
||||||
|
self.assertContains(response, "<td>Started</td>", count=1)
|
||||||
|
self.assertContains(response, "<td>Submitted</td>", count=1)
|
||||||
|
|
||||||
|
domain_request.in_review()
|
||||||
|
domain_request.save()
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table will contain and extra row for In review
|
||||||
|
self.assertContains(response, "<td>Started</td>", count=1)
|
||||||
|
self.assertContains(response, "<td>Submitted</td>", count=1)
|
||||||
|
self.assertContains(response, "<td>In review</td>", count=1)
|
||||||
|
|
||||||
|
domain_request.action_needed()
|
||||||
|
domain_request.save()
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table will contain and extra row for Action needed
|
||||||
|
self.assertContains(response, "<td>Started</td>", count=1)
|
||||||
|
self.assertContains(response, "<td>Submitted</td>", count=1)
|
||||||
|
self.assertContains(response, "<td>In review</td>", count=1)
|
||||||
|
self.assertContains(response, "<td>Action needed</td>", count=1)
|
||||||
|
|
||||||
|
domain_request.in_review()
|
||||||
|
domain_request.save()
|
||||||
|
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define the expected sequence of status changes
|
||||||
|
expected_status_changes = [
|
||||||
|
"<td>In review</td>",
|
||||||
|
"<td>Action needed</td>",
|
||||||
|
"<td>In review</td>",
|
||||||
|
"<td>Submitted</td>",
|
||||||
|
"<td>Started</td>",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Test for the order of status changes
|
||||||
|
for status_change in expected_status_changes:
|
||||||
|
self.assertContains(response, status_change, html=True)
|
||||||
|
|
||||||
|
# Table now contains 2 rows for Approved
|
||||||
|
self.assertContains(response, "<td>Started</td>", count=1)
|
||||||
|
self.assertContains(response, "<td>Submitted</td>", count=1)
|
||||||
|
self.assertContains(response, "<td>In review</td>", count=2)
|
||||||
|
self.assertContains(response, "<td>Action needed</td>", count=1)
|
||||||
|
|
||||||
def test_collaspe_toggle_button_markup(self):
|
def test_collaspe_toggle_button_markup(self):
|
||||||
"""
|
"""
|
||||||
Tests for the correct collapse toggle button markup
|
Tests for the correct collapse toggle button markup
|
||||||
|
@ -906,7 +996,6 @@ class TestDomainRequestAdmin(MockEppLib):
|
||||||
# Make sure the page loaded, and that we're on the right page
|
# Make sure the page loaded, and that we're on the right page
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertContains(response, domain_request.requested_domain.name)
|
self.assertContains(response, domain_request.requested_domain.name)
|
||||||
|
|
||||||
self.test_helper.assertContains(response, "<span>Show details</span>")
|
self.test_helper.assertContains(response, "<span>Show details</span>")
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue