Support custom fields, include actor for custom fields changes

F
This commit is contained in:
shamoon 2024-04-13 22:37:12 -07:00
parent 42eef35988
commit 25f0036a50
4 changed files with 107 additions and 6 deletions

View File

@ -37,6 +37,12 @@
<code class="text-primary">{{ change.value["objects"].join(', ') }}</code>
</li>
}
@else if (change.value["type"] === 'custom_field') {
<li>
<span class="text-light">{{ change.value["field"] }}</span>:&nbsp;
<code class="text-primary">{{ change.value["value"] }}</code>
</li>
}
@else {
<li>
<span class="text-light">{{ change.key | titlecase }}</span>:&nbsp;

View File

@ -5,6 +5,7 @@ import zoneinfo
from decimal import Decimal
import magic
from auditlog.context import set_actor
from celery import states
from django.conf import settings
from django.contrib.auth.models import Group
@ -746,7 +747,11 @@ class DocumentSerializer(
for tag in instance.tags.all()
if tag not in inbox_tags_not_being_added
]
super().update(instance, validated_data)
if settings.AUDIT_LOG_ENABLED:
with set_actor(self.user):
super().update(instance, validated_data)
else:
super().update(instance, validated_data)
return instance
def __init__(self, *args, **kwargs):

View File

@ -339,6 +339,70 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
{"title": ["First title", "New title"]},
)
def test_document_audit_action_w_custom_fields(self):
"""
GIVEN:
- Document with custom fields
WHEN:
- Document is updated
THEN:
- Audit log contains custom field changes
"""
doc = Document.objects.create(
title="First title",
checksum="123",
mime_type="application/pdf",
)
custom_field = CustomField.objects.create(
name="custom field str",
data_type=CustomField.FieldDataType.STRING,
)
self.client.force_login(user=self.user)
self.client.patch(
f"/api/documents/{doc.pk}/",
data={
"custom_fields": [
{
"field": custom_field.pk,
"value": "custom value",
},
],
},
format="json",
)
response = self.client.get(f"/api/documents/{doc.pk}/audit/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data[1]["actor"]["id"], self.user.id)
self.assertEqual(response.data[1]["action"], "create")
self.assertEqual(
response.data[1]["changes"],
{
"custom_fields": {
"type": "custom_field",
"field": "custom field str",
"value": "custom value",
},
},
)
@override_settings(AUDIT_LOG_ENABLED=False)
def test_document_audit_action_disabled(self):
doc = Document.objects.create(
title="First title",
checksum="123",
mime_type="application/pdf",
)
self.client.force_login(user=self.user)
self.client.patch(
f"/api/documents/{doc.pk}/",
{"title": "New title"},
format="json",
)
response = self.client.get(f"/api/documents/{doc.pk}/audit/")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_document_filters(self):
doc1 = Document.objects.create(
title="none1",

View File

@ -18,6 +18,7 @@ import pathvalidate
from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.db import connections
from django.db.migrations.loader import MigrationLoader
from django.db.migrations.recorder import MigrationRecorder
@ -105,6 +106,7 @@ from documents.matching import match_storage_paths
from documents.matching import match_tags
from documents.models import Correspondent
from documents.models import CustomField
from documents.models import CustomFieldInstance
from documents.models import Document
from documents.models import DocumentType
from documents.models import Note
@ -743,24 +745,48 @@ class DocumentViewSet(
raise Http404
if request.method == "GET":
# documents
entries = [
{
"id": entry.id,
"timestamp": entry.timestamp,
"action": entry.get_action_display(),
"changes": json.loads(entry.changes),
"remote_addr": entry.remote_addr,
"actor": (
{"id": entry.actor.id, "username": entry.actor.username}
if entry.actor
else None
),
}
for entry in LogEntry.objects.filter(object_pk=doc.pk).order_by(
"-timestamp",
)
for entry in LogEntry.objects.filter(object_pk=doc.pk)
]
return Response(entries)
# custom fields
for entry in LogEntry.objects.filter(
object_pk__in=doc.custom_fields.values_list("id", flat=True),
content_type=ContentType.objects.get_for_model(CustomFieldInstance),
):
entries.append(
{
"id": entry.id,
"timestamp": entry.timestamp,
"action": entry.get_action_display(),
"changes": {
"custom_fields": {
"type": "custom_field",
"field": str(entry.object_repr).split(":")[0].strip(),
"value": str(entry.object_repr).split(":")[1].strip(),
},
},
"actor": (
{"id": entry.actor.id, "username": entry.actor.username}
if entry.actor
else None
),
},
)
return Response(sorted(entries, key=lambda x: x["timestamp"], reverse=True))
class SearchResultSerializer(DocumentSerializer, PassUserMixin):