Repository: WeblateOrg/weblate

Issue:
make user's subscription list api 
**Is your feature request related to a problem? Please describe.**
I make a third party app. I can not found api that has not  users' subscription list.
please make user's subscription list.



Discussion:
There is plan to rewrite the notification system (see https://github.com/WeblateOrg/weblate/issues/2542), so the API for that should be done after those changes.
👍 
Please leave that open, that issue doesn't mention api.
How it could look like:

* GET /api/users/nijel/notifications/ - list user subscribed events
* POST /api/users/nijel/notifications/ - create new subscription
* GET /api/users/nijel/notifications/123/ - get individual subscription
* PUT/PATCH /api/users/nijel/notifications/123/ - modify
* DELETE /api/users/nijel/notifications/123/ - remove subscription

@choiks14 can you please check that it matches your needs?

Resolving patch:
diff --git a/weblate/api/serializers.py b/weblate/api/serializers.py
--- a/weblate/api/serializers.py
+++ b/weblate/api/serializers.py
@@ -21,6 +21,7 @@
 from django.core.exceptions import PermissionDenied
 from rest_framework import serializers
 
+from weblate.accounts.models import Subscription
 from weblate.auth.models import Group, Permission, Role, User
 from weblate.lang import data
 from weblate.lang.models import Language, Plural
@@ -168,6 +169,9 @@ class UserSerializer(serializers.ModelSerializer):
     groups = serializers.HyperlinkedIdentityField(
         view_name="api:group-detail", lookup_field="id", many=True, read_only=True,
     )
+    subscriptions = serializers.HyperlinkedIdentityField(
+        view_name="api:user-notifications", lookup_field="username",
+    )
 
     class Meta:
         model = User
@@ -176,6 +180,7 @@ class Meta:
             "full_name",
             "username",
             "groups",
+            "subscriptions",
             "is_superuser",
             "is_active",
             "date_joined",
@@ -415,6 +420,22 @@ def to_representation(self, instance):
         return result
 
 
+class NotificationSerializer(serializers.ModelSerializer):
+    project = ProjectSerializer(read_only=True)
+    component = ComponentSerializer(read_only=True)
+
+    class Meta:
+        model = Subscription
+        fields = (
+            "notification",
+            "id",
+            "scope",
+            "frequency",
+            "project",
+            "component",
+        )
+
+
 class TranslationSerializer(RemovableSerializer):
     web_url = AbsoluteURLField(source="get_absolute_url", read_only=True)
     share_url = AbsoluteURLField(source="get_share_url", read_only=True)
diff --git a/weblate/api/views.py b/weblate/api/views.py
--- a/weblate/api/views.py
+++ b/weblate/api/views.py
@@ -40,6 +40,7 @@
 from rest_framework.utils import formatting
 from rest_framework.views import APIView
 
+from weblate.accounts.models import Subscription
 from weblate.accounts.utils import remove_user
 from weblate.api.serializers import (
     ChangeSerializer,
@@ -50,6 +51,7 @@
     LockRequestSerializer,
     LockSerializer,
     MonolingualUnitSerializer,
+    NotificationSerializer,
     ProjectSerializer,
     RepoRequestSerializer,
     RoleSerializer,
@@ -314,6 +316,68 @@ def groups(self, request, **kwargs):
 
         return Response(serializer.data, status=status.HTTP_200_OK)
 
+    @action(
+        detail=True, methods=["get", "post"], serializer_class=NotificationSerializer
+    )
+    def notifications(self, request, **kwargs):
+        obj = self.get_object()
+        if request.method == "POST":
+            self.perm_check(request)
+            with transaction.atomic():
+                serializer = NotificationSerializer(
+                    data=request.data, context={"request": request}
+                )
+                serializer.is_valid(raise_exception=True)
+                serializer.save(user=obj)
+                return Response(serializer.data, status=status.HTTP_201_CREATED,)
+
+        queryset = obj.subscription_set.order_by("id")
+        page = self.paginate_queryset(queryset)
+        serializer = NotificationSerializer(
+            page, many=True, context={"request": request}
+        )
+
+        return self.get_paginated_response(serializer.data)
+
+    @action(
+        detail=True,
+        methods=["get", "put", "patch", "delete"],
+        url_path="notifications/(?P<subscription_id>[^/.]+)",
+        serializer_class=NotificationSerializer,
+    )
+    def notifications_details(self, request, username, subscription_id):
+        obj = self.get_object()
+
+        try:
+            subscription = obj.subscription_set.get(id=subscription_id)
+        except (Subscription.DoesNotExist, ValueError) as error:
+            return Response(
+                data={"result": "Unsuccessful", "detail": force_str(error)},
+                status=status.HTTP_400_BAD_REQUEST,
+            )
+
+        if request.method == "DELETE":
+            self.perm_check(request)
+            subscription.delete()
+            return Response(status=status.HTTP_204_NO_CONTENT)
+
+        if request.method == "GET":
+            serializer = NotificationSerializer(
+                subscription, context={"request": request}
+            )
+        else:
+            self.perm_check(request)
+            serializer = NotificationSerializer(
+                subscription,
+                data=request.data,
+                context={"request": request},
+                partial=request.method == "PATCH",
+            )
+            serializer.is_valid(raise_exception=True)
+            serializer.save()
+
+        return Response(serializer.data, status=status.HTTP_200_OK)
+
 
 class GroupViewSet(viewsets.ModelViewSet):
     """Groups API."""


Test patch:
diff --git a/weblate/api/tests.py b/weblate/api/tests.py
--- a/weblate/api/tests.py
+++ b/weblate/api/tests.py
@@ -24,6 +24,7 @@
 from rest_framework.exceptions import ErrorDetail
 from rest_framework.test import APITestCase
 
+from weblate.accounts.models import Subscription
 from weblate.auth.models import Group, Role, User
 from weblate.lang.models import Language
 from weblate.screenshots.models import Screenshot
@@ -203,6 +204,111 @@ def test_add_group(self):
             request={"group_id": group.id},
         )
 
+    def test_list_notifications(self):
+        response = self.do_request(
+            "api:user-notifications",
+            kwargs={"username": User.objects.filter(is_active=True).first().username},
+            method="get",
+            superuser=True,
+            code=200,
+        )
+        self.assertEqual(response.data["count"], 9)
+
+    def test_post_notifications(self):
+        self.do_request(
+            "api:user-notifications",
+            kwargs={"username": User.objects.filter(is_active=True).first().username},
+            method="post",
+            code=403,
+        )
+        self.do_request(
+            "api:user-notifications",
+            kwargs={"username": User.objects.filter(is_active=True).first().username},
+            method="post",
+            superuser=True,
+            code=201,
+            request={
+                "notification": "RepositoryNotification",
+                "scope": 10,
+                "frequency": 1,
+            },
+        )
+        self.assertEqual(Subscription.objects.count(), 10)
+
+    def test_get_notifications(self):
+        user = User.objects.filter(is_active=True).first()
+        self.do_request(
+            "api:user-notifications-details",
+            kwargs={"username": user.username, "subscription_id": -1},
+            method="get",
+            code=400,
+        )
+        self.do_request(
+            "api:user-notifications-details",
+            kwargs={
+                "username": user.username,
+                "subscription_id": Subscription.objects.filter(user=user).first().id,
+            },
+            method="get",
+            code=200,
+        )
+
+    def test_put_notifications(self):
+        user = User.objects.filter(is_active=True).first()
+        response = self.do_request(
+            "api:user-notifications-details",
+            kwargs={
+                "username": user.username,
+                "subscription_id": Subscription.objects.filter(
+                    user=user, notification="NewAnnouncementNotificaton"
+                )
+                .first()
+                .id,
+            },
+            method="put",
+            superuser=True,
+            code=200,
+            request={
+                "notification": "RepositoryNotification",
+                "scope": 10,
+                "frequency": 1,
+            },
+        )
+        self.assertEqual(response.data["notification"], "RepositoryNotification")
+
+    def test_patch_notifications(self):
+        user = User.objects.filter(is_active=True).first()
+        response = self.do_request(
+            "api:user-notifications-details",
+            kwargs={
+                "username": user.username,
+                "subscription_id": Subscription.objects.filter(
+                    user=user, notification="NewAnnouncementNotificaton"
+                )
+                .first()
+                .id,
+            },
+            method="patch",
+            superuser=True,
+            code=200,
+            request={"notification": "RepositoryNotification"},
+        )
+        self.assertEqual(response.data["notification"], "RepositoryNotification")
+
+    def test_delete_notifications(self):
+        user = User.objects.filter(is_active=True).first()
+        self.do_request(
+            "api:user-notifications-details",
+            kwargs={
+                "username": user.username,
+                "subscription_id": Subscription.objects.filter(user=user).first().id,
+            },
+            method="delete",
+            superuser=True,
+            code=204,
+        )
+        self.assertEqual(Subscription.objects.count(), 8)
+
     def test_put(self):
         self.do_request(
             "api:user-detail",