Skip to content
Snippets Groups Projects
Verified Commit 47209ee5 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Added API to list and detail actors

parent bbc36201
No related merge requests found
......@@ -61,6 +61,21 @@ class ActorQuerySet(models.QuerySet):
return qs
def with_outbox_activities_count(self):
return self.annotate(
outbox_activities_count=models.Count("outbox_activities", distinct=True)
)
def with_followers_count(self):
return self.annotate(
followers_count=models.Count("received_follows", distinct=True)
)
def with_uploads_count(self):
return self.annotate(
uploads_count=models.Count("libraries__uploads", distinct=True)
)
class DomainQuerySet(models.QuerySet):
def external(self):
......@@ -71,7 +86,7 @@ class DomainQuerySet(models.QuerySet):
def with_outbox_activities_count(self):
return self.annotate(
outbox_activities_count=models.Count("actors__outbox_activities")
outbox_activities_count=models.Count("actors__outbox_activities", distinct=True)
)
......
from django_filters import rest_framework as filters
from funkwhale_api.common import fields
from funkwhale_api.common import search
from funkwhale_api.federation import models as federation_models
from funkwhale_api.music import models as music_models
from funkwhale_api.users import models as users_models
......@@ -29,6 +31,28 @@ class ManageDomainFilterSet(filters.FilterSet):
fields = ["name"]
class ManageActorFilterSet(filters.FilterSet):
q = fields.SmartSearchFilter(
config=search.SearchConfig(
search_fields={
"name": {"to": "name"},
"username": {"to": "preferred_username"},
"bio": {"to": "summary"},
"type": {"to": "type"},
},
filter_fields={"domain": {"to": "domain_id__iexact"}},
)
)
local = filters.BooleanFilter(name="_", method="filter_local")
class Meta:
model = federation_models.Actor
fields = ["q", "domain", "type", "manually_approves_followers", "local"]
def filter_local(self, queryset, name, value):
return queryset.local(value)
class ManageUserFilterSet(filters.FilterSet):
q = fields.SearchFilter(search_fields=["username", "email", "name"])
......
......@@ -191,3 +191,40 @@ class ManageDomainSerializer(serializers.ModelSerializer):
def get_outbox_activities_count(self, o):
return getattr(o, "outbox_activities_count", 0)
class ManageActorSerializer(serializers.ModelSerializer):
outbox_activities_count = serializers.SerializerMethodField()
uploads_count = serializers.SerializerMethodField()
followers_count = serializers.SerializerMethodField()
class Meta:
model = federation_models.Actor
fields = [
"id",
"url",
"fid",
"preferred_username",
"domain",
"name",
"summary",
"type",
"creation_date",
"last_fetch_date",
"inbox_url",
"outbox_url",
"shared_inbox_url",
"manually_approves_followers",
"outbox_activities_count",
"uploads_count",
"followers_count",
]
def get_uploads_count(self, o):
return getattr(o, "uploads_count", 0)
def get_followers_count(self, o):
return getattr(o, "followers_count", 0)
def get_outbox_activities_count(self, o):
return getattr(o, "outbox_activities_count", 0)
......@@ -11,6 +11,9 @@ users_router = routers.SimpleRouter()
users_router.register(r"users", views.ManageUserViewSet, "users")
users_router.register(r"invitations", views.ManageInvitationViewSet, "invitations")
other_router = routers.SimpleRouter()
other_router.register(r"accounts", views.ManageActorViewSet, "accounts")
urlpatterns = [
url(
r"^federation/",
......@@ -18,4 +21,4 @@ urlpatterns = [
),
url(r"^library/", include((library_router.urls, "instance"), namespace="library")),
url(r"^users/", include((users_router.urls, "instance"), namespace="users")),
]
] + other_router.urls
from rest_framework import mixins, response, viewsets
from rest_framework.decorators import detail_route, list_route
from django.shortcuts import get_object_or_404
from funkwhale_api.common import preferences
from funkwhale_api.federation import models as federation_models
......@@ -129,3 +130,45 @@ class ManageDomainViewSet(
def stats(self, request, *args, **kwargs):
domain = self.get_object()
return response.Response(domain.get_stats(), status=200)
class ManageActorViewSet(
mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
):
lookup_value_regex = r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)"
queryset = (
federation_models.Actor.objects.all()
.with_outbox_activities_count()
.with_followers_count()
.with_uploads_count()
.order_by("-creation_date")
)
serializer_class = serializers.ManageActorSerializer
filter_class = filters.ManageActorFilterSet
permission_classes = (HasUserPermission,)
required_permissions = ["moderation"]
ordering_fields = [
"name",
"preferred_username",
"domain",
"fid",
"creation_date",
"last_fetch_date",
"uploads_count",
"followers_count",
"outbox_activities_count",
]
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
username, domain = self.kwargs["pk"].split("@")
filter_kwargs = {"domain_id": domain, "preferred_username": username}
obj = get_object_or_404(queryset, **filter_kwargs)
self.check_object_permissions(self.request, obj)
return obj
@detail_route(methods=["get"])
def stats(self, request, *args, **kwargs):
domain = self.get_object()
return response.Response(domain.get_stats(), status=200)
......@@ -51,3 +51,32 @@ def test_manage_domain_serializer(factories, now):
s = serializers.ManageDomainSerializer(domain)
assert s.data == expected
def test_manage_actor_serializer(factories, now):
actor = factories["federation.Actor"]()
setattr(actor, "outbox_activities_count", 23)
setattr(actor, "followers_count", 42)
setattr(actor, "uploads_count", 66)
expected = {
"id": actor.id,
"name": actor.name,
"creation_date": actor.creation_date.isoformat().split("+")[0] + "Z",
"last_fetch_date": actor.last_fetch_date.isoformat().split("+")[0] + "Z",
"outbox_activities_count": 23,
"followers_count": 42,
"uploads_count": 66,
"fid": actor.fid,
"url": actor.url,
"outbox_url": actor.outbox_url,
"shared_inbox_url": actor.shared_inbox_url,
"inbox_url": actor.inbox_url,
"domain": actor.domain_id,
"type": actor.type,
"summary": actor.summary,
"preferred_username": actor.preferred_username,
"manually_approves_followers": actor.manually_approves_followers,
}
s = serializers.ManageActorSerializer(actor)
assert s.data == expected
......@@ -12,6 +12,7 @@ from funkwhale_api.manage import serializers, views
(views.ManageUserViewSet, ["settings"], "and"),
(views.ManageInvitationViewSet, ["settings"], "and"),
(views.ManageDomainViewSet, ["moderation"], "and"),
(views.ManageActorViewSet, ["moderation"], "and"),
],
)
def test_permissions(assert_user_permission, view, permissions, operator):
......@@ -112,3 +113,23 @@ def test_domain_stats(factories, superuser_api_client, mocker):
response = superuser_api_client.get(url)
assert response.status_code == 200
assert response.data == {"hello": "world"}
def test_actor_list(factories, superuser_api_client, settings):
actor = factories["federation.Actor"]()
url = reverse("api:v1:manage:accounts-list")
response = superuser_api_client.get(url)
assert response.status_code == 200
assert response.data["count"] == 1
assert response.data["results"][0]["id"] == actor.id
def test_actor_detail(factories, superuser_api_client):
actor = factories["federation.Actor"]()
url = reverse("api:v1:manage:accounts-detail", kwargs={"pk": actor.full_username})
response = superuser_api_client.get(url)
assert response.status_code == 200
assert response.data["id"] == actor.id
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment