diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f6469..5b6c4e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to - ✨(scripts) adapts release script after moving the deployment part +### Fixed + +- ⚡️(api) add missing cache for stats endpoint + ## [1.10.0] - 2025-01-21 ### Added diff --git a/src/backend/core/api/client/viewsets.py b/src/backend/core/api/client/viewsets.py index adfc3e6..bc63f5d 100644 --- a/src/backend/core/api/client/viewsets.py +++ b/src/backend/core/api/client/viewsets.py @@ -7,6 +7,8 @@ from functools import reduce from django.conf import settings from django.db.models import OuterRef, Q, Subquery, Value from django.db.models.functions import Coalesce +from django.utils.decorators import method_decorator +from django.views.decorators.cache import cache_page from rest_framework import ( decorators, @@ -28,8 +30,6 @@ from core.utils.raw_sql import gen_sql_filter_json_array from mailbox_manager import models as domains_models -SIMILARITY_THRESHOLD = 0.04 - class NestedGenericViewSet(viewsets.GenericViewSet): """ @@ -596,19 +596,20 @@ class StatView(views.APIView): permission_classes = [AllowAny] + @method_decorator(cache_page(3600)) def get(self, request): """ GET /api/v1.0/stats/ Return a dictionary of public metrics. """ context = { - "total_users": models.User.objects.all().count(), + "total_users": models.User.objects.count(), "mau": models.User.objects.filter( last_login__gte=datetime.datetime.now() - datetime.timedelta(30) ).count(), - "teams": models.Team.objects.all().count(), - "domains": domains_models.MailDomain.objects.all().count(), - "mailboxes": domains_models.Mailbox.objects.all().count(), + "teams": models.Team.objects.count(), + "domains": domains_models.MailDomain.objects.count(), + "mailboxes": domains_models.Mailbox.objects.count(), } return response.Response(context) diff --git a/src/backend/core/tests/test_api_stats.py b/src/backend/core/tests/test_api_stats.py index 86e8dba..6effa2f 100644 --- a/src/backend/core/tests/test_api_stats.py +++ b/src/backend/core/tests/test_api_stats.py @@ -2,6 +2,8 @@ Test stats endpoint """ +from django.core.cache import cache + import pytest from rest_framework import status from rest_framework.test import APIClient @@ -14,17 +16,34 @@ from mailbox_manager import models as domains_models pytestmark = pytest.mark.django_db -def test_api_stats__anonymous(): +def test_api_stats__anonymous(django_assert_num_queries): """Stats endpoint should be available even when not connected.""" - response = APIClient().get("/api/v1.0/stats/") + domains_factories.MailDomainFactory.create_batch(5) + core_factories.TeamFactory.create_batch(3) + + # clear cache to allow stats count + cache.clear() + with django_assert_num_queries(5): + response = APIClient().get("/api/v1.0/stats/") assert response.status_code == status.HTTP_200_OK assert response.json() == { "total_users": 0, "mau": 0, - "domains": 0, + "domains": 5, "mailboxes": 0, - "teams": 0, + "teams": 3, + } + # no new request made due to caching + with django_assert_num_queries(0): + response = APIClient().get("/api/v1.0/stats/") + assert response.status_code == status.HTTP_200_OK + assert response.json() == { + "total_users": 0, + "mau": 0, + "domains": 5, + "mailboxes": 0, + "teams": 3, } @@ -43,6 +62,8 @@ def test_api_stats__expected_count(): 10, domain=domains_models.MailDomain.objects.all()[1] ) + # clear cache to allow stats count + cache.clear() response = APIClient().get("/api/v1.0/stats/") assert response.status_code == status.HTTP_200_OK assert response.json() == {