From da45a0292814defae0228b2be23e942860c3dc07 Mon Sep 17 00:00:00 2001 From: MueezKhan246 <93375917+MueezKhan246@users.noreply.github.com> Date: Fri, 26 Jul 2024 06:54:47 +0500 Subject: [PATCH] Fixed search fetch crashing because of server taking too long for api request logs (#2178) * fix: fixed search fetch crashing because of server taking too long for api request logs --- CHANGELOG.rst | 4 + enterprise/__init__.py | 2 +- .../integrated_channel/admin/__init__.py | 27 ++++--- tests/test_admin/test_admin.py | 77 +++++++++++++++++++ 4 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 tests/test_admin/test_admin.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 616ccb2300..91e4381c37 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,10 @@ Unreleased ---------- * nothing unreleased +[4.21.9] +--------- +* fix: fixed search fetch crashing because of server taking too long for api request logs. + [4.21.8] --------- * fix: fixed 500 error for search filter for api request logs in admin view. 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")