Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(projectHistoryLogs): project history logs for bulk actions TASK-1229 #5270

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8ceada0
Project history retention days configuration
Guitlle Oct 24, 2024
5ea0997
Merge remote-tracking branch 'origin/main' into task-972-remove-logs-…
Guitlle Oct 24, 2024
018eac9
Stub for task-972
Guitlle Oct 28, 2024
2e49ccd
Feat: project history logs automatic expiration delete
Guitlle Nov 4, 2024
ccf9da3
Merge remote-tracking branch 'origin/main' into task-972-remove-logs-…
Guitlle Nov 5, 2024
ecf731a
Merge remote-tracking branch 'origin/main' into task-972-remove-logs-…
Guitlle Nov 8, 2024
388f936
Fix unit test for logs delete function
Guitlle Nov 8, 2024
157ad14
Fix broken test due to extra SQL query
Guitlle Nov 8, 2024
dd02826
Merge remote-tracking branch 'origin/main' into task-972-remove-logs-…
Guitlle Nov 9, 2024
c9759b9
Merge remote-tracking branch 'origin/main' into task-972-remove-logs-…
Guitlle Nov 12, 2024
d800a32
Fix broken test for project history logs expiration config test
Guitlle Nov 12, 2024
2a29b79
Fix envStore definition, add new field
Guitlle Nov 12, 2024
f735746
Merge remote-tracking branch 'origin/main' into task-972-remove-logs-…
Guitlle Nov 12, 2024
01549dd
Make suggested changes
Guitlle Nov 12, 2024
c1e9148
Project history logs for bulk archive action
Guitlle Nov 13, 2024
a97d2f1
Expand test to handle the four supported bulk actions
Guitlle Nov 14, 2024
aa9513d
Fix logic for bulk actions project history logging
Guitlle Nov 14, 2024
d24fa3c
Fix linter errors
Guitlle Nov 14, 2024
38be6fd
Merge remote-tracking branch 'origin/main' into task-1229-project_his…
Guitlle Nov 14, 2024
f25e07d
Merge remote-tracking branch 'origin/main' into task-1229-project_his…
Guitlle Nov 14, 2024
6451575
Fix broken test for bulk actions logs
Guitlle Nov 14, 2024
32ea0d3
Fix using the wrong manager
Guitlle Nov 14, 2024
70a9409
Merge remote-tracking branch 'origin/main' into task-1229-project_his…
Guitlle Nov 14, 2024
333bdaa
Fix linter error
Guitlle Nov 14, 2024
1b4759c
Refactor the implementation to fit with the same pattern as other log…
Guitlle Nov 19, 2024
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
1 change: 1 addition & 0 deletions kobo/apps/audit_log/audit_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class AuditAction(models.TextChoices):
REMOVE = 'remove'
REPLACE_FORM = 'replace-form'
UNARCHIVE = 'unarchive'
UNDELETE = 'undelete'
UPDATE = 'update'
UPDATE_CONTENT = 'update-content'
UPDATE_NAME = 'update-name'
Expand Down
1 change: 1 addition & 0 deletions kobo/apps/audit_log/base_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class AuditLoggedViewSet(viewsets.GenericViewSet):
def initialize_request(self, request, *args, **kwargs):
request = super().initialize_request(request, *args, **kwargs)
request._request.log_type = self.log_type
request._request._data = request.data.copy()
return request

def get_object(self):
Expand Down
7 changes: 6 additions & 1 deletion kobo/apps/audit_log/middleware.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from rest_framework import status

from kobo.apps.audit_log.models import AuditType, ProjectHistoryLog


Expand All @@ -7,7 +9,10 @@ def create_audit_logs(request):
if request.method in ['GET', 'HEAD']:
return response
log_type = getattr(request, 'log_type', None)
if log_type == AuditType.PROJECT_HISTORY:
if (
status.is_success(response.status_code) and
log_type == AuditType.PROJECT_HISTORY
):
ProjectHistoryLog.create_from_request(request)
return response

Expand Down
42 changes: 42 additions & 0 deletions kobo/apps/audit_log/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,55 @@ def create_from_request(cls, request):
'asset-file-list': cls.create_from_file_request,
'asset-export-list': cls.create_from_export_request,
'exporttask-list': cls.create_from_v1_export,
'asset-bulk': cls.create_from_bulk_request,
}
url_name = request.resolver_match.url_name
method = url_name_to_action.get(url_name, None)
if not method:
return
method(request)

@staticmethod
def create_from_bulk_request(request):
try:
payload = request._data['payload']
action = payload['action']
asset_uids = payload['asset_uids']
except KeyError:
return # Incorrect payload

if type(asset_uids) is not list or len(asset_uids) == 0: # Nothing to do
return

bulk_action_to_audit_action = {
'archive': AuditAction.ARCHIVE,
'unarchive': AuditAction.UNARCHIVE,
'delete': AuditAction.DELETE,
'undelete': AuditAction.UNDELETE,
}
audit_action = bulk_action_to_audit_action[action]
if audit_action is None:
return # Unsupported action

source = get_human_readable_client_user_agent(request)
client_ip = get_client_ip(request)

for asset_uid in asset_uids:
asset = Asset.all_objects.get(uid=asset_uid)
object_id = asset.pk
metadata = {
'asset_uid': asset_uid,
'log_subtype': PROJECT_HISTORY_LOG_PROJECT_SUBTYPE,
'ip_address': client_ip,
'source': source,
}
ProjectHistoryLog.objects.create(
user=request.user,
object_id=object_id,
action=audit_action,
metadata=metadata,
)

@staticmethod
def create_from_deployment_request(request):
audit_log_info = getattr(request, 'additional_audit_log_info', None)
Expand Down
54 changes: 54 additions & 0 deletions kobo/apps/audit_log/tests/test_project_history_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from ddt import data, ddt, unpack
from django.test import override_settings
from django.urls import reverse
from rest_framework.response import Response
from rest_framework.reverse import reverse as drf_reverse

from kobo.apps.audit_log.audit_actions import AuditAction
Expand Down Expand Up @@ -90,6 +91,23 @@ def _base_project_history_log_test(
self._check_common_metadata(log.metadata, expected_subtype)
return log.metadata

def _make_bulk_request(self, asset_uids, action) -> Response:
"""
Make a bulk action request for a list of asset uid's and an action name

asset_uids: [list_of_uids]
action: [archive, unarchive, delete, undelete]
"""
payload = {
'payload': {
'asset_uids': asset_uids,
'action': action,
}
}
url = reverse(self._get_endpoint('asset-bulk'))
response = self.client.post(url, data=payload, format='json')
return response

def test_first_time_deployment_creates_log(self):
post_data = {
'active': True,
Expand Down Expand Up @@ -862,3 +880,39 @@ def test_export_v1_creates_log(self):
log = log_query.first()
self._check_common_metadata(log.metadata, PROJECT_HISTORY_LOG_PROJECT_SUBTYPE)
self.assertEqual(log.object_id, self.asset.id)

@data(
('archive', AuditAction.ARCHIVE),
('unarchive', AuditAction.UNARCHIVE),
('undelete', AuditAction.UNDELETE),
('delete', AuditAction.DELETE),
)
@unpack
def test_bulk_actions(self, bulk_action, audit_action):
assets = [Asset.objects.create(
content={
'survey': [
{
'type': 'text',
'label': 'Question 1',
'name': 'q1',
'$kuid': 'abc',
},
]
},
owner=self.user,
asset_type='survey',
) for i in range(0, 2)]
for asset in assets:
asset.deploy(backend='mock', active=True)

uids = [asset.uid for asset in assets]

if bulk_action == 'undelete':
self._make_bulk_request(uids, 'delete')

self._make_bulk_request(uids, bulk_action)
project_hist_logs = ProjectHistoryLog.objects.filter(
object_id__in=[asset.id for asset in assets], action=audit_action
)
self.assertEqual(project_hist_logs.count(), 2)
1 change: 0 additions & 1 deletion kobo/apps/hook/tasks.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# coding: utf-8
import time

import constance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was a complain from the linter :S ... probably from some merge, that is not related to this PR

from celery import shared_task
from django.conf import settings
from django.core.mail import EmailMultiAlternatives, get_connection
Expand Down
4 changes: 2 additions & 2 deletions kpi/serializers/v2/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ def to_representation(self, instance):
if delete_request:
if put_back_:
message = nt(
f'%(count)d project has been undeleted',
f'%(count)d projects have been undeleted',
'%(count)d project has been undeleted',
'%(count)d projects have been undeleted',
instance['project_counts'],
) % {'count': instance['project_counts']}
else:
Expand Down
Loading