️(api) add missing cache for stats endpoint

Add cache to display stats for anonymous
and improve some code.
This commit is contained in:
Sabrina Demagny
2025-01-23 17:57:35 +01:00
parent e09e1d1b80
commit c48957612b
3 changed files with 36 additions and 10 deletions

View File

@@ -12,6 +12,10 @@ and this project adheres to
- ✨(scripts) adapts release script after moving the deployment part - ✨(scripts) adapts release script after moving the deployment part
### Fixed
- ⚡️(api) add missing cache for stats endpoint
## [1.10.0] - 2025-01-21 ## [1.10.0] - 2025-01-21
### Added ### Added

View File

@@ -7,6 +7,8 @@ from functools import reduce
from django.conf import settings from django.conf import settings
from django.db.models import OuterRef, Q, Subquery, Value from django.db.models import OuterRef, Q, Subquery, Value
from django.db.models.functions import Coalesce 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 ( from rest_framework import (
decorators, decorators,
@@ -28,8 +30,6 @@ from core.utils.raw_sql import gen_sql_filter_json_array
from mailbox_manager import models as domains_models from mailbox_manager import models as domains_models
SIMILARITY_THRESHOLD = 0.04
class NestedGenericViewSet(viewsets.GenericViewSet): class NestedGenericViewSet(viewsets.GenericViewSet):
""" """
@@ -596,19 +596,20 @@ class StatView(views.APIView):
permission_classes = [AllowAny] permission_classes = [AllowAny]
@method_decorator(cache_page(3600))
def get(self, request): def get(self, request):
""" """
GET /api/v1.0/stats/ GET /api/v1.0/stats/
Return a dictionary of public metrics. Return a dictionary of public metrics.
""" """
context = { context = {
"total_users": models.User.objects.all().count(), "total_users": models.User.objects.count(),
"mau": models.User.objects.filter( "mau": models.User.objects.filter(
last_login__gte=datetime.datetime.now() - datetime.timedelta(30) last_login__gte=datetime.datetime.now() - datetime.timedelta(30)
).count(), ).count(),
"teams": models.Team.objects.all().count(), "teams": models.Team.objects.count(),
"domains": domains_models.MailDomain.objects.all().count(), "domains": domains_models.MailDomain.objects.count(),
"mailboxes": domains_models.Mailbox.objects.all().count(), "mailboxes": domains_models.Mailbox.objects.count(),
} }
return response.Response(context) return response.Response(context)

View File

@@ -2,6 +2,8 @@
Test stats endpoint Test stats endpoint
""" """
from django.core.cache import cache
import pytest import pytest
from rest_framework import status from rest_framework import status
from rest_framework.test import APIClient from rest_framework.test import APIClient
@@ -14,17 +16,34 @@ from mailbox_manager import models as domains_models
pytestmark = pytest.mark.django_db 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.""" """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.status_code == status.HTTP_200_OK
assert response.json() == { assert response.json() == {
"total_users": 0, "total_users": 0,
"mau": 0, "mau": 0,
"domains": 0, "domains": 5,
"mailboxes": 0, "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] 10, domain=domains_models.MailDomain.objects.all()[1]
) )
# clear cache to allow stats count
cache.clear()
response = APIClient().get("/api/v1.0/stats/") response = APIClient().get("/api/v1.0/stats/")
assert response.status_code == status.HTTP_200_OK assert response.status_code == status.HTTP_200_OK
assert response.json() == { assert response.json() == {