diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cecfab..65bb6bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to ### Added +- ✨(api) create stats endpoint - ✨(teams) add Team dependencies #560 - ✨(organization) add admin action for plugin #640 - ✨(anct) fetch and display organization names of communes #583 diff --git a/src/backend/core/api/client/viewsets.py b/src/backend/core/api/client/viewsets.py index f40f86b..adfc3e6 100644 --- a/src/backend/core/api/client/viewsets.py +++ b/src/backend/core/api/client/viewsets.py @@ -1,5 +1,6 @@ """API endpoints""" +import datetime import operator from functools import reduce @@ -25,6 +26,10 @@ from core.api import permissions from core.api.client import serializers 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): """ @@ -586,6 +591,28 @@ class ConfigView(views.APIView): return response.Response(dict_settings) +class StatView(views.APIView): + """API ViewSet for sharing some public metrics.""" + + permission_classes = [AllowAny] + + def get(self, request): + """ + GET /api/v1.0/stats/ + Return a dictionary of public metrics. + """ + context = { + "total_users": models.User.objects.all().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(), + } + return response.Response(context) + + class ServiceProviderFilter(filters.BaseFilterBackend): """ Filter service providers. diff --git a/src/backend/core/tests/test_api_stats.py b/src/backend/core/tests/test_api_stats.py new file mode 100644 index 0000000..86e8dba --- /dev/null +++ b/src/backend/core/tests/test_api_stats.py @@ -0,0 +1,54 @@ +""" +Test stats endpoint +""" + +import pytest +from rest_framework import status +from rest_framework.test import APIClient + +from core import factories as core_factories + +from mailbox_manager import factories as domains_factories +from mailbox_manager import models as domains_models + +pytestmark = pytest.mark.django_db + + +def test_api_stats__anonymous(): + """Stats endpoint should be available even when not connected.""" + + 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, + "mailboxes": 0, + "teams": 0, + } + + +def test_api_stats__expected_count(): + """Objects should be correctly counted.""" + + core_factories.UserFactory.create_batch(4) + logged_in_users = core_factories.UserFactory.create_batch(6) + client = APIClient() + for user in logged_in_users: + client.force_login(user) + + core_factories.TeamFactory.create_batch(3) + domains_factories.MailDomainFactory.create_batch(2) + domains_factories.MailboxFactory.create_batch( + 10, domain=domains_models.MailDomain.objects.all()[1] + ) + + response = APIClient().get("/api/v1.0/stats/") + assert response.status_code == status.HTTP_200_OK + assert response.json() == { + "total_users": 10, + "mau": 6, + "domains": 2, + "mailboxes": 10, + "teams": 3, + } diff --git a/src/backend/people/api_urls.py b/src/backend/people/api_urls.py index 3194683..9e05750 100644 --- a/src/backend/people/api_urls.py +++ b/src/backend/people/api_urls.py @@ -51,4 +51,5 @@ urlpatterns = [ ), path(f"api/{settings.API_VERSION}/", include("mailbox_manager.urls")), path(f"api/{settings.API_VERSION}/config/", viewsets.ConfigView.as_view()), + path(f"api/{settings.API_VERSION}/stats/", viewsets.StatView.as_view()), ]