diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 424292ddca..36dfd28bbb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,9 +17,13 @@ Unreleased ---------- * nothing unreleased +[4.21.10] +---------- +* created migration to create a system-wide enterprise role named `enterprise_provisioning_admin`. + [4.21.9] --------- -* created migration to `update_or_create` a system-wide enterprise role named `enterprise_provisioning_admin. +* fix: fixed search fetch crashing because of server taking too long for api request logs. [4.21.8] --------- diff --git a/enterprise/__init__.py b/enterprise/__init__.py index 6e8970888c..c9327aba92 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -2,4 +2,4 @@ Your project description goes here. """ -__version__ = "4.21.8" +__version__ = "4.21.9" diff --git a/integrated_channels/integrated_channel/admin/__init__.py b/integrated_channels/integrated_channel/admin/__init__.py index b4d8c0e701..ff7ba8879c 100644 --- a/integrated_channels/integrated_channel/admin/__init__.py +++ b/integrated_channels/integrated_channel/admin/__init__.py @@ -104,14 +104,10 @@ class IntegratedChannelAPIRequestLogAdmin(admin.ModelAdmin): "status_code", ] search_fields = [ - "status_code", - "enterprise_customer__name", - "enterprise_customer__uuid", - "enterprise_customer_configuration_id", - "endpoint", - "time_taken", - "response_body", - "payload", + "enterprise_customer__name__icontains", + "enterprise_customer__uuid__iexact", + "enterprise_customer_configuration_id__iexact", + "endpoint__icontains", ] readonly_fields = [ "status_code", @@ -122,12 +118,25 @@ class IntegratedChannelAPIRequestLogAdmin(admin.ModelAdmin): "response_body", "payload", ] + list_filter = ('status_code',) list_per_page = 20 def get_queryset(self, request): + """ + Optimize queryset by selecting related 'enterprise_customer' and limiting fields. + """ queryset = super().get_queryset(request) - return queryset.select_related('enterprise_customer') + return queryset.select_related('enterprise_customer').only( + 'id', + 'endpoint', + 'enterprise_customer_id', + 'time_taken', + 'status_code', + 'enterprise_customer__name', + 'enterprise_customer__uuid', + 'enterprise_customer_configuration_id' + ) class Meta: model = IntegratedChannelAPIRequestLogs diff --git a/tests/test_admin/test_admin.py b/tests/test_admin/test_admin.py new file mode 100644 index 0000000000..2a38bdca35 --- /dev/null +++ b/tests/test_admin/test_admin.py @@ -0,0 +1,77 @@ +""" +Tests for the IntegratedChannelAPIRequest admin module in `edx-enterprise`. +""" + +from django.contrib.admin.sites import AdminSite +from django.db import connection +from django.test import TestCase +from django.test.utils import CaptureQueriesContext + +from integrated_channels.integrated_channel.admin import IntegratedChannelAPIRequestLogAdmin +from integrated_channels.integrated_channel.models import IntegratedChannelAPIRequestLogs +from test_utils import factories + + +class IntegratedChannelAPIRequestLogAdminTest(TestCase): + """ + Test the admin functionality for the IntegratedChannelAPIRequestLogs model. + """ + + def setUp(self): + """ + Set up the test environment by creating a test admin instance and sample data. + """ + self.site = AdminSite() + self.admin = IntegratedChannelAPIRequestLogAdmin(IntegratedChannelAPIRequestLogs, self.site) + + self.enterprise_customer = factories.EnterpriseCustomerFactory(name='Test Enterprise') + self.log_entry = IntegratedChannelAPIRequestLogs.objects.create( + enterprise_customer=self.enterprise_customer, + enterprise_customer_configuration_id=1, + endpoint="http://test.com", + payload="test payload", + time_taken=1.0, + status_code=200, + response_body="test response" + ) + + def test_get_queryset_optimization(self): + """ + Test that the get_queryset method optimizes query by using select_related and only selecting specified fields. + """ + request = None + + with CaptureQueriesContext(connection) as queries: + queryset = self.admin.get_queryset(request) + + list(queryset) + + self.assertEqual(len(queries), 1) + + query = queries[0]['sql'] + self.assertIn('integrated_channel_integratedchannelapirequestlogs', query) + self.assertIn('enterprise_enterprisecustomer', query) + + self.assertIn('"integrated_channel_integratedchannelapirequestlogs"."id"', query) + self.assertIn('"integrated_channel_integratedchannelapirequestlogs"."endpoint"', query) + self.assertIn('"integrated_channel_integratedchannelapirequestlogs"."enterprise_customer_id"', query) + self.assertIn('"integrated_channel_integratedchannelapirequestlogs"."time_taken"', query) + self.assertIn('"integrated_channel_integratedchannelapirequestlogs"."status_code"', query) + self.assertIn('"enterprise_enterprisecustomer"."name"', query) + self.assertIn('"enterprise_enterprisecustomer"."uuid"', query) + self.assertIn( + '"integrated_channel_integratedchannelapirequestlogs"."enterprise_customer_configuration_id"', query + ) + + self.assertNotIn('payload', query) + self.assertNotIn('response_body', query) + + log_entry = queryset.get(id=self.log_entry.id) + self.assertEqual(log_entry.endpoint, "http://test.com") + self.assertEqual(log_entry.enterprise_customer.name, "Test Enterprise") + + # Verify that accessing an unselected field causes an additional query + with CaptureQueriesContext(connection) as queries: + _ = log_entry.payload + + self.assertEqual(len(queries), 1, "Accessing unselected field should cause exactly one additional query")