kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
migrate privavy_level to actor, replace "favorite" by "like", delete apiv2 endpoints
rodzic
878cb32b96
commit
890963db30
|
@ -15,11 +15,6 @@ v2_patterns += [
|
|||
r"^radios/",
|
||||
include(("funkwhale_api.radios.urls_v2", "radios"), namespace="radios"),
|
||||
),
|
||||
# to do : to delete
|
||||
# re_path(
|
||||
# r"^users/",
|
||||
# include(("funkwhale_api.users.api_urls_v2", "users"), namespace="users"),
|
||||
# ),
|
||||
]
|
||||
|
||||
urlpatterns = [re_path("", include((v2_patterns, "v2"), namespace="v2"))]
|
||||
|
|
|
@ -37,12 +37,8 @@ def combined_recent(limit, **kwargs):
|
|||
return records
|
||||
|
||||
|
||||
# to do : this should look into actor privacy_level and not iuser privacy level if we want to handle federated
|
||||
# privacy_level acces mmanagement
|
||||
def get_activity(user, limit=20):
|
||||
query = fields.privacy_level_query(
|
||||
user, "actor__user__privacy_level", "actor__user"
|
||||
)
|
||||
query = fields.privacy_level_query(user, "actor__privacy_level", "actor__user")
|
||||
querysets = [
|
||||
Listening.objects.filter(query).select_related(
|
||||
"track", "actor", "track__artist", "track__album__artist"
|
||||
|
|
|
@ -24,8 +24,20 @@ def privacy_level_query(user, lookup_field="privacy_level", user_field="user"):
|
|||
if user.is_anonymous:
|
||||
return models.Q(**{lookup_field: "everyone"})
|
||||
|
||||
return models.Q(**{f"{lookup_field}__in": ["instance", "everyone"]}) | models.Q(
|
||||
**{lookup_field: "me", user_field: user}
|
||||
actors_follows = user.actor.user_follows.filter(approved=True).values_list(
|
||||
"target", flat=True
|
||||
)
|
||||
|
||||
followers_query = models.Q(
|
||||
**{
|
||||
f"{lookup_field}": "followers",
|
||||
f"{user_field}__actor__pk__in": actors_follows,
|
||||
}
|
||||
)
|
||||
return (
|
||||
models.Q(**{f"{lookup_field}__in": ["instance", "everyone"]})
|
||||
| models.Q(**{lookup_field: "me", user_field: user})
|
||||
| followers_query
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -69,12 +69,12 @@ class PrivacyLevelPermission(BasePermission):
|
|||
# to do : it's a remote actor. We could trigger an update of the remote actor data
|
||||
# to avoid leaking data
|
||||
return True
|
||||
if obj.user.privacy_level == "everyone":
|
||||
if obj.user.actor.privacy_level == "everyone":
|
||||
return True
|
||||
# user is anonymous
|
||||
elif not hasattr(request.user, "actor"):
|
||||
return False
|
||||
elif obj.user.privacy_level == "instance":
|
||||
elif obj.user.actor.privacy_level == "instance":
|
||||
# user is local
|
||||
if hasattr(request.user, "actor"):
|
||||
return True
|
||||
|
@ -83,11 +83,14 @@ class PrivacyLevelPermission(BasePermission):
|
|||
else:
|
||||
return False
|
||||
|
||||
elif obj.user.privacy_level == "me" and obj.user.actor == request.user.actor:
|
||||
elif (
|
||||
obj.user.actor.privacy_level == "me"
|
||||
and obj.user.actor == request.user.actor
|
||||
):
|
||||
return True
|
||||
|
||||
elif (
|
||||
obj.user.privacy_level == "followers"
|
||||
obj.user.actor.privacy_level == "followers"
|
||||
and request.user.actor in obj.user.actor.get_followers()
|
||||
):
|
||||
return True
|
||||
|
|
|
@ -104,7 +104,7 @@ def sync_listenings_from_listenbrainz(user, conf):
|
|||
logger.info("Getting listenings from ListenBrainz")
|
||||
try:
|
||||
last_ts = (
|
||||
history_models.Listening.objects.filter(user=user)
|
||||
history_models.Listening.objects.filter(actor=user.actor)
|
||||
.filter(source="Listenbrainz")
|
||||
.latest("creation_date")
|
||||
.values_list("creation_date", flat=True)
|
||||
|
@ -124,7 +124,7 @@ def sync_favorites_from_listenbrainz(user, conf):
|
|||
return
|
||||
try:
|
||||
last_ts = (
|
||||
favorites_models.TrackFavorite.objects.filter(user=user)
|
||||
favorites_models.TrackFavorite.objects.filter(actor=user.actor)
|
||||
.filter(source="Listenbrainz")
|
||||
.latest("creation_date")
|
||||
.creation_date.timestamp()
|
||||
|
|
|
@ -106,7 +106,7 @@ def add_lb_listenings_to_db(listens, user):
|
|||
listen.listened_at, timezone.utc
|
||||
),
|
||||
track=track,
|
||||
user=user,
|
||||
actor=user.actor,
|
||||
source="Listenbrainz",
|
||||
)
|
||||
fw_listens.append(fw_listen)
|
||||
|
@ -147,7 +147,7 @@ def add_lb_feedback_to_db(feedbacks, user):
|
|||
|
||||
if feedback["score"] == 1:
|
||||
favorites_models.TrackFavorite.objects.get_or_create(
|
||||
user=user,
|
||||
actor=user.actor,
|
||||
creation_date=datetime.datetime.fromtimestamp(
|
||||
feedback["created"], timezone.utc
|
||||
),
|
||||
|
@ -157,7 +157,7 @@ def add_lb_feedback_to_db(feedbacks, user):
|
|||
elif feedback["score"] == 0:
|
||||
try:
|
||||
favorites_models.TrackFavorite.objects.get(
|
||||
user=user, track=track
|
||||
actor=user.actor, track=track
|
||||
).delete()
|
||||
except favorites_models.TrackFavorite.DoesNotExist:
|
||||
continue
|
||||
|
|
|
@ -8,7 +8,7 @@ record.registry.register_serializer(serializers.TrackFavoriteActivitySerializer)
|
|||
|
||||
@record.registry.register_consumer("favorites.TrackFavorite")
|
||||
def broadcast_track_favorite_to_instance_activity(data, obj):
|
||||
if obj.actor.user.privacy_level not in ["instance", "everyone"]:
|
||||
if obj.actor.privacy_level not in ["instance", "everyone"]:
|
||||
return
|
||||
|
||||
channels.group_send(
|
||||
|
|
|
@ -19,7 +19,7 @@ def gen_uuid(apps, schema_editor):
|
|||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("federation", "0029_userfollow"),
|
||||
("favorites", "0001_initial"),
|
||||
("favorites", "0002_trackfavorite_source"),
|
||||
]
|
||||
|
||||
operations = [
|
|
@ -15,7 +15,7 @@ class Migration(migrations.Migration):
|
|||
dependencies = [
|
||||
("federation", "0029_userfollow"),
|
||||
("music", "0057_auto_20221118_2108"),
|
||||
("favorites", "0002_trackfavorite_actor_trackfavorite_fid_and_more"),
|
||||
("favorites", "0003_trackfavorite_actor_trackfavorite_fid_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -18,24 +18,24 @@ FAVORITE_PRIVACY_LEVEL_CHOICES = [
|
|||
class TrackFavoriteQuerySet(models.QuerySet, common_models.LocalFromFidQuerySet):
|
||||
def viewable_by(self, actor):
|
||||
if actor is None:
|
||||
return self.filter(actor__user__privacy_level="everyone")
|
||||
return self.filter(actor__privacy_level="everyone")
|
||||
|
||||
if hasattr(actor, "user"):
|
||||
me_query = models.Q(actor__user__privacy_level="me", actor=actor)
|
||||
me_query = models.Q(actor__user__privacy_level="me", actor=actor)
|
||||
me_query = models.Q(actor__privacy_level="me", actor=actor)
|
||||
me_query = models.Q(actor__privacy_level="me", actor=actor)
|
||||
|
||||
instance_query = models.Q(
|
||||
actor__user__privacy_level="instance", actor__domain=actor.domain
|
||||
actor__privacy_level="instance", actor__domain=actor.domain
|
||||
)
|
||||
instance_actor_query = models.Q(
|
||||
actor__user__privacy_level="instance", actor__domain=actor.domain
|
||||
actor__privacy_level="instance", actor__domain=actor.domain
|
||||
)
|
||||
|
||||
return self.filter(
|
||||
me_query
|
||||
| instance_query
|
||||
| instance_actor_query
|
||||
| models.Q(actor__user__privacy_level="everyone")
|
||||
| models.Q(actor__privacy_level="everyone")
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class TrackFavoriteViewSet(
|
|||
)
|
||||
record.send(instance)
|
||||
routes.outbox.dispatch(
|
||||
{"type": "Create", "object": {"type": "Favorite"}},
|
||||
{"type": "Like", "object": {"type": "Track"}},
|
||||
context={"favorite": instance},
|
||||
)
|
||||
return Response(
|
||||
|
@ -65,7 +65,7 @@ class TrackFavoriteViewSet(
|
|||
queryset = super().get_queryset()
|
||||
queryset = queryset.filter(
|
||||
fields.privacy_level_query(
|
||||
self.request.user, "actor__user__privacy_level", "actor__user"
|
||||
self.request.user, "actor__privacy_level", "actor__user"
|
||||
)
|
||||
)
|
||||
tracks = Track.objects.with_playable_uploads(
|
||||
|
@ -90,7 +90,7 @@ class TrackFavoriteViewSet(
|
|||
except (AttributeError, ValueError, models.TrackFavorite.DoesNotExist):
|
||||
return Response({}, status=400)
|
||||
routes.outbox.dispatch(
|
||||
{"type": "Delete", "object": {"type": "Favorite"}},
|
||||
{"type": "Delete", "object": {"type": "Like"}},
|
||||
context={"favorite": favorite},
|
||||
)
|
||||
favorite.delete()
|
||||
|
|
|
@ -55,7 +55,6 @@ FUNKWHALE_OBJECT_TYPES = [
|
|||
("Album", "Album"),
|
||||
("Track", "Track"),
|
||||
("Library", "Library"),
|
||||
("Favorite", "Favorite"),
|
||||
]
|
||||
OBJECT_TYPES = (
|
||||
[
|
||||
|
|
|
@ -294,8 +294,6 @@ CONTEXTS = [
|
|||
"Track": "fw:Track",
|
||||
"Artist": "fw:Artist",
|
||||
"Library": "fw:Library",
|
||||
# might be possible to do "Favorite": "as:Like" ?
|
||||
"Favorite": "fw:Favorite",
|
||||
"bitrate": {"@id": "fw:bitrate", "@type": "xsd:nonNegativeInteger"},
|
||||
"size": {"@id": "fw:size", "@type": "xsd:nonNegativeInteger"},
|
||||
"position": {"@id": "fw:position", "@type": "xsd:nonNegativeInteger"},
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# Generated by Django 4.2.9 on 2024-04-17 19:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def gen_privacy_level(apps, schema_editor):
|
||||
user_model = apps.get_model("users", "User")
|
||||
for user in user_model.objects.all():
|
||||
user.actor.privacy_level = user.actor.privacy_level
|
||||
user.actor.save(update_fields=["privacy_level"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("federation", "0029_userfollow"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="actor",
|
||||
name="privacy_level",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("me", "Only me"),
|
||||
("followers", "Me and my followers"),
|
||||
("instance", "Everyone on my instance, and my followers"),
|
||||
("everyone", "Everyone, including people on other instances"),
|
||||
],
|
||||
default="instance",
|
||||
max_length=30,
|
||||
),
|
||||
),
|
||||
migrations.RunPython(gen_privacy_level, reverse_code=migrations.RunPython.noop),
|
||||
]
|
|
@ -14,7 +14,7 @@ from django.dispatch import receiver
|
|||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from funkwhale_api.common import session
|
||||
from funkwhale_api.common import session, fields
|
||||
from funkwhale_api.common import utils as common_utils
|
||||
from funkwhale_api.common import validators as common_validators
|
||||
from funkwhale_api.music import utils as music_utils
|
||||
|
@ -218,6 +218,7 @@ class Actor(models.Model):
|
|||
on_delete=models.SET_NULL,
|
||||
related_name="iconed_actor",
|
||||
)
|
||||
privacy_level = fields.get_privacy_field()
|
||||
|
||||
objects = ActorQuerySet.as_manager()
|
||||
|
||||
|
@ -254,7 +255,7 @@ class Actor(models.Model):
|
|||
def should_autoapprove_follow(self, actor):
|
||||
if self.get_channel():
|
||||
return True
|
||||
if self.user.privacy_level == "public":
|
||||
if self.user.actor.privacy_level == "public":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -402,8 +403,6 @@ class Fetch(models.Model):
|
|||
serializers.ChannelUploadSerializer,
|
||||
],
|
||||
contexts.FW.Library: [serializers.LibrarySerializer],
|
||||
# to do : don't need to fetch a favorite since we can fetch the track and actor already ?
|
||||
# contexts.FW.Favorite: [serializers.TrackFavoriteSerializer],
|
||||
contexts.AS.Group: [serializers.ActorSerializer],
|
||||
contexts.AS.Person: [serializers.ActorSerializer],
|
||||
contexts.AS.Organization: [serializers.ActorSerializer],
|
||||
|
|
|
@ -612,46 +612,31 @@ def outbox_delete_album(context):
|
|||
}
|
||||
|
||||
|
||||
@outbox.register({"type": "Create", "object.type": "Favorite"})
|
||||
@outbox.register({"type": "Like", "object.type": "Track"})
|
||||
def outbox_create_favorite(context):
|
||||
from funkwhale_api.favorites import serializers as favorites_serializers
|
||||
|
||||
favorite = context["favorite"]
|
||||
actor = favorite.actor
|
||||
|
||||
serializer = serializers.ActivitySerializer(
|
||||
{
|
||||
"type": "Create",
|
||||
"object": serializers.TrackFavoriteSerializer(favorite).data,
|
||||
"actor": actor.fid,
|
||||
}
|
||||
{"type": "Like", "object": {"type": "Like", "id": favorite.fid}}
|
||||
)
|
||||
|
||||
yield {
|
||||
"type": "Create",
|
||||
"type": "Like",
|
||||
"actor": actor,
|
||||
"payload": with_recipients(
|
||||
serializer.data,
|
||||
to=[{"type": "followers", "target": actor}],
|
||||
),
|
||||
"object": favorite,
|
||||
"target": actor,
|
||||
}
|
||||
|
||||
|
||||
@outbox.register({"type": "Delete", "object.type": "Favorite"})
|
||||
@outbox.register({"type": "Delete", "object.type": "Like"})
|
||||
def outbox_delete_favorite(context):
|
||||
favorite = context["favorite"]
|
||||
actor = favorite.actor
|
||||
|
||||
serializer = serializers.ActivitySerializer(
|
||||
{
|
||||
"type": "Delete",
|
||||
"object": serializers.TrackFavoriteSerializer(favorite).data,
|
||||
"actor": actor.fid,
|
||||
}
|
||||
{"type": "Delete", "object": {"type": "Like", "id": favorite.fid}}
|
||||
)
|
||||
|
||||
yield {
|
||||
"type": "Delete",
|
||||
"actor": actor,
|
||||
|
@ -659,24 +644,18 @@ def outbox_delete_favorite(context):
|
|||
serializer.data,
|
||||
to=[{"type": "followers", "target": actor}],
|
||||
),
|
||||
"object": favorite,
|
||||
"target": actor,
|
||||
}
|
||||
|
||||
|
||||
@inbox.register({"type": "Create", "object.type": "Favorite"})
|
||||
@inbox.register({"type": "Like", "object.type": "Track"})
|
||||
def inbox_create_favorite(payload, context):
|
||||
from funkwhale_api.favorites import serializers as favorites_serializers
|
||||
|
||||
actor = context["actor"]
|
||||
favorite = payload["object"]
|
||||
serializer = serializers.TrackFavoriteSerializer(data=favorite)
|
||||
serializer = serializers.TrackFavoriteSerializer(data=payload)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
instance = serializer.save()
|
||||
return {"object": instance}
|
||||
|
||||
|
||||
@inbox.register({"type": "Delete", "object.type": "Favorite"})
|
||||
@inbox.register({"type": "Delete", "object.type": "Like"})
|
||||
def inbox_delete_favorite(payload, context):
|
||||
actor = context["actor"]
|
||||
favorite_id = payload["object"].get("id")
|
||||
|
|
|
@ -2094,24 +2094,21 @@ class IndexSerializer(jsonld.JsonLdSerializer):
|
|||
class TrackFavoriteSerializer(jsonld.JsonLdSerializer):
|
||||
type = serializers.ChoiceField(choices=[contexts.AS.Like])
|
||||
id = serializers.URLField(max_length=500)
|
||||
# to do : should thi be target like followserializer ?
|
||||
track = TrackSerializer(required=True)
|
||||
object = serializers.URLField(max_length=500)
|
||||
actor = serializers.URLField(max_length=500)
|
||||
|
||||
class Meta:
|
||||
jsonld_mapping = {
|
||||
"track": jsonld.first_obj(contexts.FW.track),
|
||||
"object": jsonld.first_id(contexts.AS.object),
|
||||
"actor": jsonld.first_id(contexts.AS.actor),
|
||||
}
|
||||
|
||||
def to_representation(self, favorite):
|
||||
payload = {
|
||||
"type": "Favorite",
|
||||
"type": "Like",
|
||||
"id": favorite.fid,
|
||||
"actor": favorite.actor.fid,
|
||||
"track": TrackSerializer(
|
||||
favorite.track, context={"include_ap_context": False}
|
||||
).data,
|
||||
"object": favorite.track.fid,
|
||||
}
|
||||
if self.context.get("include_ap_context", True):
|
||||
payload["@context"] = jsonld.get_default_context()
|
||||
|
@ -2119,9 +2116,8 @@ class TrackFavoriteSerializer(jsonld.JsonLdSerializer):
|
|||
|
||||
def create(self, validated_data):
|
||||
actor = actors.get_actor(validated_data["actor"])
|
||||
|
||||
track = utils.retrieve_ap_object(
|
||||
validated_data["track"]["id"],
|
||||
validated_data["object"],
|
||||
actor=actors.get_service_actor(),
|
||||
serializer_class=TrackSerializer,
|
||||
)
|
||||
|
@ -2131,5 +2127,4 @@ class TrackFavoriteSerializer(jsonld.JsonLdSerializer):
|
|||
uuid=uuid.uuid4(),
|
||||
actor=actor,
|
||||
track=track,
|
||||
user=None,
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@ record.registry.register_serializer(serializers.ListeningActivitySerializer)
|
|||
|
||||
@record.registry.register_consumer("history.Listening")
|
||||
def broadcast_listening_to_instance_activity(data, obj):
|
||||
if obj.actor.user.privacy_level not in ["instance", "everyone"]:
|
||||
if obj.actor.privacy_level not in ["instance", "everyone"]:
|
||||
return
|
||||
|
||||
channels.group_send(
|
||||
|
|
|
@ -15,7 +15,7 @@ def gen_uuid(apps, schema_editor):
|
|||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("federation", "0029_userfollow"),
|
||||
("history", "0002_auto_20180325_1433"),
|
||||
("history", "0003_listening_source"),
|
||||
]
|
||||
|
||||
operations = [
|
|
@ -13,7 +13,7 @@ def get_user_actor(apps, schema_editor):
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("history", "0003_listening_actor_listening_fid_listening_url"),
|
||||
("history", "0004_listening_actor_listening_fid_listening_url"),
|
||||
]
|
||||
|
||||
operations = [
|
|
@ -10,7 +10,7 @@ from funkwhale_api.federation import utils as federation_utils
|
|||
from funkwhale_api.music.models import Track
|
||||
|
||||
|
||||
class TrackFavoriteQuerySet(models.QuerySet, common_models.LocalFromFidQuerySet):
|
||||
class ListeningQuerySet(models.QuerySet, common_models.LocalFromFidQuerySet):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -33,7 +33,7 @@ class Listening(federation_models.FederationMixin):
|
|||
session_key = models.CharField(max_length=100, null=True, blank=True)
|
||||
source = models.CharField(max_length=100, null=True, blank=True)
|
||||
federation_namespace = "listenings"
|
||||
objects = TrackFavoriteQuerySet.as_manager()
|
||||
objects = ListeningQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
ordering = ("-creation_date",)
|
||||
|
|
|
@ -48,7 +48,9 @@ class ListeningViewSet(
|
|||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
queryset = queryset.filter(
|
||||
fields.privacy_level_query(self.request.user, "actor__user__privacy_level")
|
||||
fields.privacy_level_query(
|
||||
self.request.user, "actor__privacy_level", "actor__user"
|
||||
)
|
||||
)
|
||||
tracks = Track.objects.with_playable_uploads(
|
||||
music_utils.get_actor_from_request(self.request)
|
||||
|
|
|
@ -295,7 +295,7 @@ class ManageActorFilterSet(filters.FilterSet):
|
|||
|
||||
class Meta:
|
||||
model = federation_models.Actor
|
||||
fields = ["domain", "type", "manually_approves_followers"]
|
||||
fields = ["domain", "type", "manually_approves_followers", "privacy_level"]
|
||||
|
||||
def filter_local(self, queryset, name, value):
|
||||
return queryset.local(value)
|
||||
|
@ -316,7 +316,6 @@ class ManageUserFilterSet(filters.FilterSet):
|
|||
model = users_models.User
|
||||
fields = [
|
||||
"is_active",
|
||||
"privacy_level",
|
||||
"is_staff",
|
||||
"is_superuser",
|
||||
"permission_library",
|
||||
|
|
|
@ -43,7 +43,6 @@ class ManageUserSimpleSerializer(serializers.ModelSerializer):
|
|||
"is_superuser",
|
||||
"date_joined",
|
||||
"last_activity",
|
||||
"privacy_level",
|
||||
"upload_quota",
|
||||
)
|
||||
|
||||
|
@ -67,7 +66,6 @@ class ManageUserSerializer(serializers.ModelSerializer):
|
|||
"date_joined",
|
||||
"last_activity",
|
||||
"permissions",
|
||||
"privacy_level",
|
||||
"upload_quota",
|
||||
"full_username",
|
||||
)
|
||||
|
@ -224,6 +222,7 @@ class ManageBaseActorSerializer(serializers.ModelSerializer):
|
|||
"shared_inbox_url",
|
||||
"manually_approves_followers",
|
||||
"is_local",
|
||||
"privacy_level",
|
||||
]
|
||||
read_only_fields = ["creation_date", "instance_policy"]
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ class PlaylistViewSet(
|
|||
return serializer.save(
|
||||
user=self.request.user,
|
||||
privacy_level=serializer.validated_data.get(
|
||||
"privacy_level", self.request.user.privacy_level
|
||||
"privacy_level", self.request.user.actor.privacy_level
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -292,17 +292,17 @@ class SimilarRadio(RelatedObjectRadio):
|
|||
FROM (
|
||||
SELECT
|
||||
track_id,
|
||||
creation_date,
|
||||
h.creation_date,
|
||||
LEAD(track_id) OVER (
|
||||
PARTITION by user_id order by creation_date asc
|
||||
PARTITION by actor_id ORDER BY h.creation_date ASC
|
||||
) AS next
|
||||
FROM history_listening
|
||||
INNER JOIN users_user ON (users_user.id = user_id)
|
||||
WHERE users_user.privacy_level = 'instance' OR users_user.privacy_level = 'everyone' OR user_id = %s
|
||||
ORDER BY creation_date ASC
|
||||
) t WHERE track_id = %s AND next != %s GROUP BY next ORDER BY c DESC;
|
||||
FROM history_listening h
|
||||
INNER JOIN federation_actor fa ON (fa.id = h.actor_id)
|
||||
WHERE fa.privacy_level = 'instance' OR fa.privacy_level = 'everyone' OR h.actor_id = %s
|
||||
ORDER BY h.creation_date ASC
|
||||
) t WHERE track_id = %s AND next IS NOT NULL AND next != %s GROUP BY next ORDER BY c DESC;
|
||||
"""
|
||||
cursor.execute(query, [self.session.user_id, seed, seed])
|
||||
cursor.execute(query, [self.session.user.actor, seed, seed])
|
||||
next_candidates = list(cursor.fetchall())
|
||||
|
||||
if not next_candidates:
|
||||
|
|
|
@ -327,22 +327,21 @@ class SimilarRadio(RelatedObjectRadio):
|
|||
|
||||
def find_next_id(self, queryset, seed):
|
||||
with connection.cursor() as cursor:
|
||||
query = """
|
||||
SELECT next, count(next) AS c
|
||||
query = """SELECT next, count(next) AS c
|
||||
FROM (
|
||||
SELECT
|
||||
track_id,
|
||||
creation_date,
|
||||
h.creation_date,
|
||||
LEAD(track_id) OVER (
|
||||
PARTITION by user_id order by creation_date asc
|
||||
PARTITION by actor_id ORDER BY h.creation_date ASC
|
||||
) AS next
|
||||
FROM history_listening
|
||||
INNER JOIN users_user ON (users_user.id = user_id)
|
||||
WHERE users_user.privacy_level = 'instance' OR users_user.privacy_level = 'everyone' OR user_id = %s
|
||||
ORDER BY creation_date ASC
|
||||
) t WHERE track_id = %s AND next != %s GROUP BY next ORDER BY c DESC;
|
||||
FROM history_listening h
|
||||
INNER JOIN federation_actor fa ON (fa.id = h.actor_id)
|
||||
WHERE fa.privacy_level = 'instance' OR fa.privacy_level = 'everyone' OR h.actor_id = %s
|
||||
ORDER BY h.creation_date ASC
|
||||
) t WHERE track_id = %s AND next IS NOT NULL AND next != %s GROUP BY next ORDER BY c DESC;
|
||||
"""
|
||||
cursor.execute(query, [self.session.user_id, seed, seed])
|
||||
cursor.execute(query, [self.session.user.actor, seed, seed])
|
||||
next_candidates = list(cursor.fetchall())
|
||||
|
||||
if not next_candidates:
|
||||
|
|
|
@ -60,14 +60,13 @@ class UserAdmin(AuthUserAdmin):
|
|||
list_filter = [
|
||||
"is_superuser",
|
||||
"is_staff",
|
||||
"privacy_level",
|
||||
"permission_settings",
|
||||
"permission_library",
|
||||
"permission_moderation",
|
||||
]
|
||||
actions = [disable, enable]
|
||||
fieldsets = (
|
||||
(None, {"fields": ("username", "password", "privacy_level")}),
|
||||
(None, {"fields": ("username", "password")}),
|
||||
(
|
||||
_("Personal info"),
|
||||
{"fields": ("first_name", "last_name", "email", "avatar")},
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# to do : to delete
|
||||
# from django.urls import re_path, include
|
||||
|
||||
# from funkwhale_api.common import routers
|
||||
|
||||
# from funkwhale_api.federation import api_views as federation_views
|
||||
|
||||
# from . import views_v2
|
||||
|
||||
|
||||
# router = routers.OptionalSlashRouter()
|
||||
# router.register(r"", views_v2.UserViewSet, "users")
|
||||
|
||||
# urlpatterns = [
|
||||
# re_path(r"^login/?$", views_v2.login, name="login"),
|
||||
# re_path(r"^logout/?$", views_v2.logout, name="logout"),
|
||||
# re_path(
|
||||
# r"^(?P<user_pk>[0-9]+)/follow_requests/(?P<follow_pk>[0-9]+)/?$",
|
||||
# views_v2.follow_request_patch,
|
||||
# name="follow_request_patch",
|
||||
# ),
|
||||
# ] + router.urls
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 4.2.9 on 2024-04-18 17:24
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("users", "0023_merge_20221125_1902"),
|
||||
("federation", "0030_actor_privacy_level"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="user",
|
||||
name="privacy_level",
|
||||
),
|
||||
]
|
|
@ -120,7 +120,6 @@ class User(AbstractUser):
|
|||
|
||||
# updated on logout or password change, to invalidate JWT
|
||||
secret_key = models.UUIDField(default=uuid.uuid4, null=True)
|
||||
privacy_level = fields.get_privacy_field()
|
||||
|
||||
# Unfortunately, Subsonic API assumes a MD5/password authentication
|
||||
# scheme, which is weak in terms of security, and not achievable
|
||||
|
@ -214,6 +213,10 @@ class User(AbstractUser):
|
|||
u.settings[key] = value
|
||||
u.save(update_fields=["settings"])
|
||||
self.settings = u.settings
|
||||
# to do : this is never called
|
||||
if "privacy_level" in settings:
|
||||
u.actor.privacy_level = settings["privacy_level"]
|
||||
u.actor.save()
|
||||
|
||||
def has_permissions(self, *perms, **kwargs):
|
||||
operator = kwargs.pop("operator", "and")
|
||||
|
|
|
@ -157,7 +157,6 @@ class UserWriteSerializer(serializers.ModelSerializer):
|
|||
model = models.User
|
||||
fields = [
|
||||
"name",
|
||||
"privacy_level",
|
||||
"avatar",
|
||||
"instance_support_message_display_date",
|
||||
"funkwhale_support_message_display_date",
|
||||
|
@ -204,7 +203,6 @@ class UserReadSerializer(serializers.ModelSerializer):
|
|||
"is_superuser",
|
||||
"permissions",
|
||||
"date_joined",
|
||||
"privacy_level",
|
||||
"avatar",
|
||||
]
|
||||
|
||||
|
|
|
@ -94,7 +94,6 @@ class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
|
|||
"""Return information about the current user or delete it"""
|
||||
new_settings = request.data
|
||||
request.user.set_settings(**new_settings)
|
||||
# to do : privacy downgrade
|
||||
if "privacy_level" in new_settings:
|
||||
dispatch_privacy_downgrade(new_settings["privacy_level"], request.user)
|
||||
return Response(request.user.settings)
|
||||
|
@ -140,9 +139,17 @@ class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
|
|||
serializer.save(request)
|
||||
return Response(status=204)
|
||||
|
||||
# to do : this work but maybe front should send privacy level update on the actor endpoint an not hte user endpoint ?
|
||||
def update(self, request, *args, **kwargs):
|
||||
if not self.request.user.username == kwargs.get("username"):
|
||||
return Response(status=403)
|
||||
if "privacy_level" in request.data:
|
||||
user = self.get_object()
|
||||
request.data._mutable = True
|
||||
privacy_level = request.data.pop("privacy_level")
|
||||
request.data._mutable = False
|
||||
user.actor.privacy_level = privacy_level[0]
|
||||
user.actor.save()
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
||||
def partial_update(self, request, *args, **kwargs):
|
||||
|
@ -187,7 +194,13 @@ def logout(request):
|
|||
# to do : privacy downgrade
|
||||
def dispatch_privacy_downgrade(privacy_level, user):
|
||||
if privacy_level == "me" or privacy_level == "instance":
|
||||
routes.outbox.dispatch({"type": "Delete"}, context={"actor": user.actor})
|
||||
|
||||
# this will automatically delete all related actor acitivities
|
||||
routes.outbox.dispatch(
|
||||
{"type": "Delete", "object": {"type": user.actor.type}},
|
||||
context={"actor": user.actor},
|
||||
)
|
||||
if privacy_level == "followers":
|
||||
routes.outbox.dispatch({"type": "Update"}, context={"actor": user.actor})
|
||||
routes.outbox.dispatch(
|
||||
{"type": "Update", "object": {"type": user.actor.type}},
|
||||
context={"actor": user.actor},
|
||||
)
|
||||
|
|
|
@ -1,270 +0,0 @@
|
|||
# to do : to delete import json
|
||||
|
||||
# from allauth.account.adapter import get_adapter
|
||||
# from allauth.account.utils import send_email_confirmation
|
||||
# from dj_rest_auth import views as rest_auth_views
|
||||
# from dj_rest_auth.registration import views as registration_views
|
||||
# from django import http
|
||||
# from django.contrib import auth
|
||||
# from django.middleware import csrf
|
||||
# from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
# from rest_framework import mixins, viewsets, exceptions
|
||||
|
||||
# from rest_framework.decorators import action, api_view
|
||||
|
||||
# from rest_framework.response import Response
|
||||
|
||||
# from funkwhale_api.common import preferences, throttling
|
||||
|
||||
# from . import models, serializers, tasks
|
||||
# from funkwhale_api.federation import models as federation_models
|
||||
# from funkwhale_api.federation import api_serializers as api_federation_serializers
|
||||
# from funkwhale_api.federation import serializers as federation_serializers
|
||||
# from funkwhale_api.federation import routes
|
||||
|
||||
# from . import models, serializers, tasks
|
||||
|
||||
# from django.shortcuts import get_object_or_404
|
||||
|
||||
|
||||
# class UserViewSet(mixins.UpdateModelMixin, viewsets.GenericViewSet):
|
||||
# queryset = models.User.objects.all().select_related("actor__attachment_icon")
|
||||
# serializer_class = serializers.UserWriteSerializer
|
||||
# lookup_field = "pk"
|
||||
# lookup_value_regex = r"[a-zA-Z0-9-_.]+"
|
||||
# required_scope = "profile"
|
||||
|
||||
# @extend_schema(operation_id="follow_requests")
|
||||
# @action(methods=["post"], detail=True)
|
||||
# def follow_requests(self, *args, **kwargs):
|
||||
# user_id = kwargs["pk"]
|
||||
# actor = self.request.user.actor
|
||||
|
||||
# serializer = api_federation_serializers.UserFollowSerializer(
|
||||
# data={"actor": actor, "target": user_id},
|
||||
# context={"actor": self.request.user.actor},
|
||||
# )
|
||||
# serializer.is_valid(raise_exception=True)
|
||||
# follow = serializer.save(actor=self.request.user.actor)
|
||||
|
||||
# routes.outbox.dispatch({"type": "Follow"}, context={"follow": follow})
|
||||
|
||||
# return Response(status=204)
|
||||
|
||||
# @extend_schema(operation_id="unfollow")
|
||||
# @action(methods=["post"], detail=True)
|
||||
# def unfollow(self, *args, **kwargs):
|
||||
# follow = get_object_or_404(
|
||||
# federation_models.UserFollow,
|
||||
# actor=self.request.user.actor,
|
||||
# target=self.get_object(),
|
||||
# )
|
||||
# follow.delete()
|
||||
# routes.outbox.dispatch({"type": "Delete"}, context={"follow": follow})
|
||||
# return Response(status=200)
|
||||
|
||||
# @extend_schema(operation_id="followings")
|
||||
# @action(
|
||||
# methods=["get"],
|
||||
# detail=True,
|
||||
# )
|
||||
# def followings(self, *args, **kwargs):
|
||||
# user = self.get_object()
|
||||
# if (
|
||||
# self.request.user != self.get_object()
|
||||
# and self.get_object().privacy_level == "private"
|
||||
# ):
|
||||
# raise exceptions.PermissionDenied
|
||||
|
||||
# if (
|
||||
# self.request.user != self.get_object()
|
||||
# and self.request.user.actor.is_local is False
|
||||
# and self.get_object().privacy_level == "pod"
|
||||
# ):
|
||||
# raise exceptions.PermissionDenied
|
||||
|
||||
# followings = federation_models.UserFollow.objects.filter(
|
||||
# actor=user.actor
|
||||
# ).order_by("creation_date")
|
||||
# serializer = api_federation_serializers.UserFollowSerializer(
|
||||
# followings, many=True
|
||||
# )
|
||||
# page = self.paginate_queryset(followings)
|
||||
# if page is not None:
|
||||
# serializer = api_federation_serializers.UserFollowSerializer(
|
||||
# page, many=True, required=False
|
||||
# )
|
||||
# return self.get_paginated_response(serializer.data)
|
||||
|
||||
# serializer = api_federation_serializers.UserFollowSerializer(
|
||||
# followings, many=True, required=False
|
||||
# )
|
||||
# return Response(serializer.data)
|
||||
|
||||
# @extend_schema(operation_id="followers")
|
||||
# @action(
|
||||
# methods=["get"],
|
||||
# detail=True,
|
||||
# )
|
||||
# def followers(self, *args, **kwargs):
|
||||
# user = self.get_object()
|
||||
# if (
|
||||
# self.request.user != self.get_object()
|
||||
# and self.get_object().privacy_level == "private"
|
||||
# ):
|
||||
# raise exceptions.PermissionDenied
|
||||
|
||||
# if (
|
||||
# self.request.user != self.get_object()
|
||||
# and self.request.actor.is_local is False
|
||||
# and self.get_object().privacy_level == "pod"
|
||||
# ):
|
||||
# raise exceptions.PermissionDenied
|
||||
|
||||
# followers = federation_models.UserFollow.objects.filter(target=user).order_by(
|
||||
# "creation_date"
|
||||
# )
|
||||
# serializer = api_federation_serializers.UserFollowSerializer(
|
||||
# followers, many=True
|
||||
# )
|
||||
# page = self.paginate_queryset(followers)
|
||||
# if page is not None:
|
||||
# serializer = api_federation_serializers.UserFollowSerializer(
|
||||
# page, many=True, required=False
|
||||
# )
|
||||
# return self.get_paginated_response(serializer.data)
|
||||
|
||||
# serializer = api_federation_serializers.UserFollowSerializer(
|
||||
# followers, many=True, required=False
|
||||
# )
|
||||
# return Response(serializer.data)
|
||||
|
||||
# @extend_schema(operation_id="get_authenticated_user", methods=["get"])
|
||||
# @extend_schema(operation_id="delete_authenticated_user", methods=["delete"])
|
||||
# @action(methods=["get", "delete"], detail=False)
|
||||
# def me(self, request, *args, **kwargs):
|
||||
# """Return information about the current user or delete it"""
|
||||
# if request.method.lower() == "delete":
|
||||
# serializer = serializers.UserDeleteSerializer(
|
||||
# request.user, data=request.data
|
||||
# )
|
||||
# serializer.is_valid(raise_exception=True)
|
||||
# tasks.delete_account.delay(user_id=request.user.pk)
|
||||
# # at this point, password is valid, we launch deletion
|
||||
# return Response(status=204)
|
||||
# serializer = serializers.MeSerializer(request.user)
|
||||
# return Response(serializer.data)
|
||||
|
||||
# @extend_schema(operation_id="update_settings")
|
||||
# @action(methods=["post"], detail=False, url_name="settings", url_path="settings")
|
||||
# def set_settings(self, request, *args, **kwargs):
|
||||
# """Return information about the current user or delete it"""
|
||||
# new_settings = request.data
|
||||
# request.user.set_settings(**new_settings)
|
||||
# return Response(request.user.settings)
|
||||
|
||||
# @action(
|
||||
# methods=["get", "post", "delete"],
|
||||
# required_scope="security",
|
||||
# url_path="subsonic-token",
|
||||
# detail=True,
|
||||
# )
|
||||
# def subsonic_token(self, request, *args, **kwargs):
|
||||
# if not self.request.user.username == kwargs.get("username"):
|
||||
# return Response(status=403)
|
||||
# if not preferences.get("subsonic__enabled"):
|
||||
# return Response(status=405)
|
||||
# if request.method.lower() == "get":
|
||||
# return Response(
|
||||
# {"subsonic_api_token": self.request.user.subsonic_api_token}
|
||||
# )
|
||||
# if request.method.lower() == "delete":
|
||||
# self.request.user.subsonic_api_token = None
|
||||
# self.request.user.save(update_fields=["subsonic_api_token"])
|
||||
# return Response(status=204)
|
||||
# self.request.user.update_subsonic_api_token()
|
||||
# self.request.user.save(update_fields=["subsonic_api_token"])
|
||||
# data = {"subsonic_api_token": self.request.user.subsonic_api_token}
|
||||
# return Response(data)
|
||||
|
||||
# @extend_schema(operation_id="change_email", responses={200: None, 403: None})
|
||||
# @action(
|
||||
# methods=["post"],
|
||||
# required_scope="security",
|
||||
# url_path="change-email",
|
||||
# detail=False,
|
||||
# )
|
||||
# def change_email(self, request, *args, **kwargs):
|
||||
# if not self.request.user.is_authenticated:
|
||||
# return Response(status=403)
|
||||
# serializer = serializers.UserChangeEmailSerializer(
|
||||
# request.user, data=request.data, context={"user": request.user}
|
||||
# )
|
||||
# serializer.is_valid(raise_exception=True)
|
||||
# serializer.save(request)
|
||||
# return Response(status=204)
|
||||
|
||||
# def update(self, request, *args, **kwargs):
|
||||
# if not self.request.user.username == kwargs.get("username"):
|
||||
# return Response(status=403)
|
||||
# return super().update(request, *args, **kwargs)
|
||||
|
||||
# def partial_update(self, request, *args, **kwargs):
|
||||
# if not self.request.user.username == kwargs.get("username"):
|
||||
# return Response(status=403)
|
||||
# return super().partial_update(request, *args, **kwargs)
|
||||
|
||||
|
||||
# @extend_schema(operation_id="login")
|
||||
# @action(methods=["post"], detail=False)
|
||||
# def login(request):
|
||||
# throttling.check_request(request, "login")
|
||||
# if request.method != "POST":
|
||||
# return http.HttpResponse(status=405)
|
||||
# serializer = serializers.LoginSerializer(
|
||||
# data=request.POST, context={"request": request}
|
||||
# )
|
||||
# if not serializer.is_valid():
|
||||
# return http.HttpResponse(
|
||||
# json.dumps(serializer.errors), status=400, content_type="application/json"
|
||||
# )
|
||||
# serializer.save(request)
|
||||
# csrf.rotate_token(request)
|
||||
# token = csrf.get_token(request)
|
||||
# response = http.HttpResponse(status=200)
|
||||
# response.set_cookie("csrftoken", token, max_age=None)
|
||||
# return response
|
||||
|
||||
|
||||
# @extend_schema(operation_id="logout")
|
||||
# @action(methods=["post"], detail=False)
|
||||
# def logout(request):
|
||||
# if request.method != "POST":
|
||||
# return http.HttpResponse(status=405)
|
||||
# auth.logout(request)
|
||||
# token = csrf.get_token(request)
|
||||
# response = http.HttpResponse(status=200)
|
||||
# response.set_cookie("csrftoken", token, max_age=None)
|
||||
# return response
|
||||
|
||||
|
||||
# @action(methods=["patch"], detail=False)
|
||||
# @extend_schema(operation_id="follow_request_patch")
|
||||
# def follow_request_patch(request, user_pk, follow_pk):
|
||||
# try:
|
||||
# user = models.User.objects.get(pk=user_pk)
|
||||
# follow = federation_models.UserFollow.objects.get(pk=follow_pk)
|
||||
# except (models.User.DoesNotExist, federation_models.UserFollow.DoesNotExist):
|
||||
# raise http.Http404
|
||||
# if request.user != user:
|
||||
# raise exceptions.PermissionDenied
|
||||
|
||||
# request_body = request.body.decode("utf-8")
|
||||
# data = json.loads(request_body)
|
||||
# if not isinstance(data["approved"], bool):
|
||||
# raise BaseException("Approved typemust be boolean")
|
||||
# follow.approved = data["approved"]
|
||||
# follow.save()
|
||||
|
||||
# routes.outbox.dispatch({"type": "Update"}, context={"follow": follow})
|
||||
# return http.HttpResponse(status=204)
|
|
@ -13,11 +13,16 @@ def test_get_activity(factories):
|
|||
|
||||
|
||||
def test_get_activity_honors_privacy_level(factories, anonymous_user):
|
||||
user = factories["users.User"](privacy_level="me")
|
||||
user2 = factories["users.User"](privacy_level="instance")
|
||||
factories["history.Listening"](actor=user.actor)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level="everyone")
|
||||
user2 = factories["users.User"]()
|
||||
user2.create_actor(privacy_level="instance")
|
||||
|
||||
listening1 = factories["history.Listening"](actor=user.actor)
|
||||
favorite1 = factories["favorites.TrackFavorite"](actor=user.actor)
|
||||
|
||||
factories["favorites.TrackFavorite"](actor=user2.actor)
|
||||
|
||||
objects = list(utils.get_activity(anonymous_user))
|
||||
assert objects == [favorite1]
|
||||
assert objects == [favorite1, listening1]
|
||||
# to do : test others
|
||||
|
|
|
@ -5,7 +5,8 @@ from funkwhale_api.activity import serializers, utils
|
|||
|
||||
def test_activity_view(factories, api_client, preferences, anonymous_user):
|
||||
preferences["common__api_authentication_required"] = False
|
||||
user = factories["users.User"](privacy_level="everyone")
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level="everyone")
|
||||
factories["favorites.TrackFavorite"](actor=user.actor)
|
||||
factories["history.Listening"]()
|
||||
url = reverse("api:v1:activity-list")
|
||||
|
|
|
@ -1,25 +1,91 @@
|
|||
import pytest
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.db.models import Q
|
||||
from django.db.models import Q, QuerySet
|
||||
|
||||
from funkwhale_api.common import fields
|
||||
from funkwhale_api.users.factories import UserFactory
|
||||
|
||||
from funkwhale_api.history import models
|
||||
from funkwhale_api.favorites import models as favorite_models
|
||||
from funkwhale_api.federation import models as federation_models
|
||||
|
||||
|
||||
def test_privacy_level_query(factories):
|
||||
user = factories["users.User"](with_actor=True)
|
||||
user_query = (
|
||||
Q(privacy_level__in=["instance", "everyone"])
|
||||
| Q(privacy_level="me", user=user)
|
||||
| Q(
|
||||
privacy_level="followers",
|
||||
user__actor__pk__in=user.actor.user_follows.filter(
|
||||
approved=True
|
||||
).values_list("target", flat=True),
|
||||
)
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"user,expected",
|
||||
[
|
||||
(AnonymousUser(), Q(privacy_level="everyone")),
|
||||
(
|
||||
UserFactory.build(pk=1),
|
||||
Q(privacy_level__in=["instance", "everyone"])
|
||||
| Q(privacy_level="me", user=UserFactory.build(pk=1)),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_privacy_level_query(user, expected):
|
||||
query = fields.privacy_level_query(user)
|
||||
assert query == expected
|
||||
assert str(query) == str(user_query)
|
||||
|
||||
user = AnonymousUser()
|
||||
user_query = Q(privacy_level="everyone")
|
||||
query = fields.privacy_level_query(user)
|
||||
assert str(query) == str(user_query)
|
||||
|
||||
|
||||
def test_privacy_level_query_followers(factories):
|
||||
user = factories["users.User"](with_actor=True)
|
||||
target = factories["users.User"]()
|
||||
target.create_actor(privacy_level="followers")
|
||||
|
||||
target.refresh_from_db()
|
||||
|
||||
userfollow = factories["federation.UserFollow"](
|
||||
actor=user.actor, target=target.actor, approved=True
|
||||
)
|
||||
listening = factories["history.Listening"](actor=userfollow.target)
|
||||
favorite = factories["favorites.TrackFavorite"](actor=userfollow.target)
|
||||
|
||||
factories["history.Listening"]()
|
||||
factories["history.Listening"]()
|
||||
factories["favorites.TrackFavorite"]()
|
||||
factories["favorites.TrackFavorite"]()
|
||||
|
||||
queryset = models.Listening.objects.all().filter(
|
||||
fields.privacy_level_query(user, "actor__privacy_level", "actor__user")
|
||||
)
|
||||
fav_qs = favorite_models.TrackFavorite.objects.all().filter(
|
||||
fields.privacy_level_query(user, "actor__privacy_level", "actor__user")
|
||||
)
|
||||
|
||||
assert listening in queryset
|
||||
assert favorite in fav_qs
|
||||
|
||||
|
||||
def test_privacy_level_query_not_followers(factories):
|
||||
user = factories["users.User"](with_actor=True)
|
||||
target = factories["users.User"]()
|
||||
target.create_actor(privacy_level="followers")
|
||||
|
||||
target.refresh_from_db()
|
||||
|
||||
userfollow = factories["federation.UserFollow"](target=target.actor, approved=True)
|
||||
listening = factories["history.Listening"](actor=userfollow.target)
|
||||
favorite = factories["favorites.TrackFavorite"](actor=userfollow.target)
|
||||
|
||||
factories["history.Listening"]()
|
||||
factories["history.Listening"]()
|
||||
factories["favorites.TrackFavorite"]()
|
||||
factories["favorites.TrackFavorite"]()
|
||||
|
||||
queryset = models.Listening.objects.all().filter(
|
||||
fields.privacy_level_query(user, "actor__privacy_level", "actor__user")
|
||||
)
|
||||
fav_qs = favorite_models.TrackFavorite.objects.all().filter(
|
||||
fields.privacy_level_query(user, "actor__privacy_level", "actor__user")
|
||||
)
|
||||
|
||||
assert listening not in queryset
|
||||
assert favorite not in fav_qs
|
||||
|
||||
|
||||
def test_generic_relation_field(factories):
|
||||
|
|
|
@ -48,7 +48,8 @@ def test_owner_permission_read_only(anonymous_user, nodb_factories, api_request)
|
|||
def test_privacylevel_permission_anonymous(
|
||||
factories, api_request, anonymous_user, privacy_level, expected
|
||||
):
|
||||
user = factories["users.User"](with_actor=True, privacy_level=privacy_level)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level=privacy_level)
|
||||
view = APIView.as_view()
|
||||
permission = permissions.PrivacyLevelPermission()
|
||||
request = api_request.get("/")
|
||||
|
@ -65,7 +66,8 @@ def test_privacylevel_permission_anonymous(
|
|||
def test_privacylevel_permission_instance(
|
||||
factories, api_request, anonymous_user, privacy_level, expected, mocker
|
||||
):
|
||||
user = factories["users.User"](with_actor=True, privacy_level=privacy_level)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level=privacy_level)
|
||||
request_user = factories["users.User"](with_actor=True)
|
||||
view = APIView.as_view()
|
||||
permission = permissions.PrivacyLevelPermission()
|
||||
|
@ -83,7 +85,8 @@ def test_privacylevel_permission_instance(
|
|||
def test_privacylevel_permission_me(
|
||||
factories, api_request, anonymous_user, privacy_level, expected, mocker
|
||||
):
|
||||
user = factories["users.User"](with_actor=True, privacy_level=privacy_level)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level=privacy_level)
|
||||
view = APIView.as_view()
|
||||
permission = permissions.PrivacyLevelPermission()
|
||||
request = api_request.get("/")
|
||||
|
|
|
@ -13,6 +13,7 @@ from funkwhale_api.history import models as history_models
|
|||
|
||||
|
||||
def test_listenbrainz_submit_listen(logged_in_client, mocker, factories):
|
||||
logged_in_client.user.create_actor()
|
||||
config = plugins.get_plugin_config(
|
||||
name="listenbrainz",
|
||||
description="A plugin that allows you to submit or sync your listens and favorites to ListenBrainz.",
|
||||
|
@ -38,7 +39,7 @@ def test_listenbrainz_submit_listen(logged_in_client, mocker, factories):
|
|||
url = reverse("api:v1:history:listenings-list")
|
||||
logged_in_client.post(url, {"track": track.pk})
|
||||
logged_in_client.get(url)
|
||||
listening = history_models.Listening.objects.get(user=logged_in_client.user)
|
||||
listening = history_models.Listening.objects.get(actor=logged_in_client.user.actor)
|
||||
handler.assert_called_once_with(listening=listening, conf=None)
|
||||
|
||||
|
||||
|
@ -117,14 +118,14 @@ def test_sync_favorites_from_listenbrainz(factories, mocker, caplog):
|
|||
logger = logging.getLogger("plugins")
|
||||
caplog.set_level(logging.INFO)
|
||||
logger.addHandler(caplog.handler)
|
||||
user = factories["users.User"]()
|
||||
user = factories["users.User"](with_actor=True)
|
||||
# track lb fav
|
||||
factories["music.Track"](mbid="195565db-65f9-4d0d-b347-5f0c85509528")
|
||||
# random track
|
||||
factories["music.Track"]()
|
||||
# track lb neutral
|
||||
track = factories["music.Track"](mbid="c5af5351-dbbf-4481-b52e-a480b6c57986")
|
||||
favorite = factories["favorites.TrackFavorite"](track=track, user=user)
|
||||
favorite = factories["favorites.TrackFavorite"](track=track, actor=user.actor)
|
||||
# last_sync
|
||||
track_last_sync = factories["music.Track"](
|
||||
mbid="c878ef2f-c08d-4a81-a047-f2a9f978cec7"
|
||||
|
@ -189,12 +190,12 @@ def test_sync_favorites_from_listenbrainz_since(factories, mocker, caplog):
|
|||
logger = logging.getLogger("plugins")
|
||||
caplog.set_level(logging.INFO)
|
||||
logger.addHandler(caplog.handler)
|
||||
user = factories["users.User"]()
|
||||
user = factories["users.User"](with_actor=True)
|
||||
# track lb fav
|
||||
factories["music.Track"](mbid="195565db-65f9-4d0d-b347-5f0c85509528")
|
||||
# track lb neutral
|
||||
track = factories["music.Track"](mbid="c5af5351-dbbf-4481-b52e-a480b6c57986")
|
||||
favorite = factories["favorites.TrackFavorite"](track=track, user=user)
|
||||
favorite = factories["favorites.TrackFavorite"](track=track, actor=user.actor)
|
||||
# track should be not synced
|
||||
factories["music.Track"](mbid="1fd02cf2-7247-4715-8862-c378ec196000")
|
||||
# last_sync
|
||||
|
@ -203,7 +204,7 @@ def test_sync_favorites_from_listenbrainz_since(factories, mocker, caplog):
|
|||
)
|
||||
factories["favorites.TrackFavorite"](
|
||||
track=track_last_sync,
|
||||
user=user,
|
||||
actor=user.actor,
|
||||
source="Listenbrainz",
|
||||
creation_date=datetime.datetime.fromtimestamp(1690775094),
|
||||
)
|
||||
|
|
|
@ -54,7 +54,9 @@ def test_broadcast_track_favorite_to_instance_activity(factories, mocker):
|
|||
|
||||
def test_broadcast_track_favorite_to_instance_activity_private(factories, mocker):
|
||||
p = mocker.patch("funkwhale_api.common.channels.group_send")
|
||||
user = factories["users.User"](privacy_level="me", with_actor=True)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level="me")
|
||||
|
||||
favorite = factories["favorites.TrackFavorite"](actor=user.actor)
|
||||
data = serializers.TrackFavoriteActivitySerializer(favorite).data
|
||||
consumer = activities.broadcast_track_favorite_to_instance_activity
|
||||
|
|
|
@ -10,7 +10,8 @@ from funkwhale_api.favorites import models
|
|||
def test_playable_by_local_actor(privacy_level, expected, factories):
|
||||
actor = factories["federation.Actor"](local=True)
|
||||
# default user actor is local
|
||||
user = factories["users.User"](with_actor=True, privacy_level=privacy_level)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level=privacy_level)
|
||||
favorite = factories["favorites.TrackFavorite"](actor=user.actor)
|
||||
queryset = models.TrackFavorite.objects.all().viewable_by(actor)
|
||||
match = favorite in list(queryset)
|
||||
|
@ -23,10 +24,9 @@ def test_playable_by_local_actor(privacy_level, expected, factories):
|
|||
def test_not_playable_by_remote_actor(privacy_level, expected, factories):
|
||||
actor = factories["federation.Actor"]()
|
||||
# default user actor is local
|
||||
user = factories["users.User"](
|
||||
with_actor=True,
|
||||
privacy_level=privacy_level,
|
||||
)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level=privacy_level)
|
||||
|
||||
favorite = factories["favorites.TrackFavorite"](actor=user.actor)
|
||||
queryset = models.TrackFavorite.objects.all().viewable_by(actor)
|
||||
match = favorite in list(queryset)
|
||||
|
|
|
@ -5,7 +5,8 @@ from django.urls import reverse
|
|||
@pytest.mark.parametrize("level", ["instance", "me", "followers"])
|
||||
def test_privacy_filter(preferences, level, factories, api_client):
|
||||
preferences["common__api_authentication_required"] = False
|
||||
user = factories["users.User"](with_actor=True, privacy_level=level)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level=level)
|
||||
factories["favorites.TrackFavorite"](actor=user.actor)
|
||||
url = reverse("api:v1:favorites:tracks-list")
|
||||
response = api_client.get(url)
|
||||
|
|
|
@ -40,7 +40,7 @@ from funkwhale_api.favorites import serializers as favorites_serializers
|
|||
({"type": "Delete", "object": {"type": "Tombstone"}}, routes.inbox_delete),
|
||||
({"type": "Flag"}, routes.inbox_flag),
|
||||
(
|
||||
{"type": "Create", "object": {"type": "Favorite"}},
|
||||
{"type": "Like", "object": {"type": "Track"}},
|
||||
routes.inbox_create_favorite,
|
||||
),
|
||||
],
|
||||
|
@ -90,7 +90,7 @@ def test_inbox_routes(route, handler):
|
|||
routes.outbox_delete_actor,
|
||||
),
|
||||
(
|
||||
{"type": "Create", "object": {"type": "Favorite"}},
|
||||
{"type": "Like", "object": {"type": "Track"}},
|
||||
routes.outbox_create_favorite,
|
||||
),
|
||||
],
|
||||
|
@ -144,7 +144,7 @@ def test_inbox_follow_user_autoapprove(factories, mocker):
|
|||
"funkwhale_api.federation.activity.OutboxRouter.dispatch"
|
||||
)
|
||||
|
||||
local_actor = factories["users.User"](privacy_level="public").create_actor()
|
||||
local_actor = factories["users.User"]().create_actor(privacy_level="public")
|
||||
remote_actor = factories["federation.Actor"]()
|
||||
ii = factories["federation.InboxItem"](actor=local_actor)
|
||||
|
||||
|
@ -1039,43 +1039,29 @@ def test_outbox_flag(factory_name, factory_kwargs, factories, mocker):
|
|||
def test_outbox_create_favorite(factories, mocker):
|
||||
user = factories["users.User"](with_actor=True)
|
||||
favorite = factories["favorites.TrackFavorite"](actor=user.actor)
|
||||
userfollow = factories["federation.UserFollow"](target=favorite.actor)
|
||||
|
||||
activity = list(routes.outbox_create_favorite({"favorite": favorite}))[0]
|
||||
serializer = serializers.ActivitySerializer(
|
||||
{
|
||||
"type": "Create",
|
||||
"object": serializers.TrackFavoriteSerializer(favorite).data,
|
||||
"actor": favorite.actor.fid,
|
||||
}
|
||||
{"type": "Like", "object": {"type": "Like", "id": favorite.fid}}
|
||||
)
|
||||
expected = serializer.data
|
||||
expected["to"] = [{"type": "followers", "target": favorite.actor}]
|
||||
assert dict(activity["payload"]) == dict(expected)
|
||||
assert activity["actor"] == favorite.actor
|
||||
assert activity["target"] == favorite.actor.received_user_follows.all()
|
||||
assert activity["object"] == favorite
|
||||
|
||||
|
||||
def test_inbox_create_favorite(factories, mocker):
|
||||
actor = factories["federation.Actor"]()
|
||||
favorite = factories["favorites.TrackFavorite"](actor=actor)
|
||||
follow = factories["federation.UserFollow"](target=actor)
|
||||
|
||||
data = serializers.TrackFavoriteSerializer(favorite).data
|
||||
serializer = serializers.ActivitySerializer(
|
||||
{
|
||||
"type": "Create",
|
||||
"object": data,
|
||||
"actor": actor.fid,
|
||||
}
|
||||
)
|
||||
serializer = serializers.TrackFavoriteSerializer(favorite)
|
||||
|
||||
init = mocker.spy(serializers.TrackFavoriteSerializer, "__init__")
|
||||
save = mocker.spy(serializers.TrackFavoriteSerializer, "save")
|
||||
track_data = serializers.TrackSerializer(favorite.track).data
|
||||
mocker.patch.object(utils, "retrieve_ap_object", return_value=favorite.track)
|
||||
|
||||
favorite.delete()
|
||||
|
||||
result = routes.inbox_create_favorite(
|
||||
serializer.data,
|
||||
context={
|
||||
|
|
|
@ -54,8 +54,9 @@ def test_broadcast_listening_to_instance_activity(factories, mocker):
|
|||
|
||||
def test_broadcast_listening_to_instance_activity_private(factories, mocker):
|
||||
p = mocker.patch("funkwhale_api.common.channels.group_send")
|
||||
user = factories["users.User"](privacy_level="me", with_actor=True)
|
||||
listening = factories["history.Listening"](actor__user=user)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level="me")
|
||||
listening = factories["history.Listening"](actor=user.actor)
|
||||
data = serializers.ListeningActivitySerializer(listening).data
|
||||
consumer = activities.broadcast_listening_to_instance_activity
|
||||
consumer(data=data, obj=listening)
|
||||
|
|
|
@ -5,7 +5,8 @@ from django.urls import reverse
|
|||
@pytest.mark.parametrize("level", ["instance", "me", "followers"])
|
||||
def test_privacy_filter(preferences, level, factories, api_client):
|
||||
preferences["common__api_authentication_required"] = False
|
||||
user = factories["users.User"](privacy_level=level)
|
||||
user = factories["users.User"]()
|
||||
user.create_actor(privacy_level=level)
|
||||
factories["history.Listening"](actor__user=user)
|
||||
url = reverse("api:v1:history:listenings-list")
|
||||
response = api_client.get(url)
|
||||
|
|
|
@ -89,6 +89,7 @@ def test_manage_actor_serializer(factories, now, to_api_date):
|
|||
"user": None,
|
||||
"instance_policy": None,
|
||||
"is_local": False,
|
||||
"privacy_level": "instance",
|
||||
}
|
||||
s = serializers.ManageActorSerializer(actor)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ from funkwhale_api.playlists import models
|
|||
|
||||
|
||||
def test_can_create_playlist_via_api(logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
url = reverse("api:v1:playlists-list")
|
||||
data = {"name": "test", "privacy_level": "everyone"}
|
||||
|
||||
|
@ -16,6 +17,7 @@ def test_can_create_playlist_via_api(logged_in_api_client):
|
|||
|
||||
|
||||
def test_serializer_includes_tracks_count(factories, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
playlist = factories["playlists.Playlist"]()
|
||||
factories["playlists.PlaylistTrack"](playlist=playlist)
|
||||
|
||||
|
@ -26,6 +28,7 @@ def test_serializer_includes_tracks_count(factories, logged_in_api_client):
|
|||
|
||||
|
||||
def test_serializer_includes_tracks_count_986(factories, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
playlist = factories["playlists.Playlist"]()
|
||||
plt = factories["playlists.PlaylistTrack"](playlist=playlist)
|
||||
factories["music.Upload"].create_batch(
|
||||
|
@ -38,6 +41,7 @@ def test_serializer_includes_tracks_count_986(factories, logged_in_api_client):
|
|||
|
||||
|
||||
def test_serializer_includes_is_playable(factories, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
playlist = factories["playlists.Playlist"]()
|
||||
factories["playlists.PlaylistTrack"](playlist=playlist)
|
||||
|
||||
|
@ -48,16 +52,17 @@ def test_serializer_includes_is_playable(factories, logged_in_api_client):
|
|||
|
||||
|
||||
def test_playlist_inherits_user_privacy(logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
url = reverse("api:v1:playlists-list")
|
||||
user = logged_in_api_client.user
|
||||
user.privacy_level = "me"
|
||||
user.actor.privacy_level = "me"
|
||||
user.save()
|
||||
|
||||
data = {"name": "test"}
|
||||
|
||||
logged_in_api_client.post(url, data)
|
||||
playlist = user.playlists.latest("id")
|
||||
assert playlist.privacy_level == user.privacy_level
|
||||
assert playlist.privacy_level == user.actor.privacy_level
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -73,6 +78,7 @@ def test_url_requires_login(name, method, factories, api_client):
|
|||
|
||||
|
||||
def test_only_can_add_track_on_own_playlist_via_api(factories, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
track = factories["music.Track"]()
|
||||
playlist = factories["playlists.Playlist"]()
|
||||
url = reverse("api:v1:playlists-add", kwargs={"pk": playlist.pk})
|
||||
|
@ -84,6 +90,7 @@ def test_only_can_add_track_on_own_playlist_via_api(factories, logged_in_api_cli
|
|||
|
||||
|
||||
def test_deleting_plt_updates_indexes(mocker, factories, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
remove = mocker.spy(models.Playlist, "remove")
|
||||
factories["music.Track"]()
|
||||
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
||||
|
@ -115,6 +122,7 @@ def test_playlist_privacy_respected_in_list_anon(
|
|||
|
||||
@pytest.mark.parametrize("method", ["PUT", "PATCH", "DELETE"])
|
||||
def test_only_owner_can_edit_playlist(method, factories, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
playlist = factories["playlists.Playlist"]()
|
||||
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
|
||||
response = getattr(logged_in_api_client, method.lower())(url)
|
||||
|
@ -125,6 +133,7 @@ def test_only_owner_can_edit_playlist(method, factories, logged_in_api_client):
|
|||
def test_can_add_multiple_tracks_at_once_via_api(
|
||||
factories, mocker, logged_in_api_client
|
||||
):
|
||||
logged_in_api_client.user.create_actor()
|
||||
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
||||
tracks = factories["music.Track"].create_batch(size=5)
|
||||
track_ids = [t.id for t in tracks]
|
||||
|
@ -141,6 +150,7 @@ def test_can_add_multiple_tracks_at_once_via_api(
|
|||
|
||||
|
||||
def test_honor_max_playlist_size(factories, mocker, logged_in_api_client, preferences):
|
||||
logged_in_api_client.user.create_actor()
|
||||
preferences["playlists__max_tracks"] = 3
|
||||
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
||||
tracks = factories["music.Track"].create_batch(
|
||||
|
@ -155,6 +165,7 @@ def test_honor_max_playlist_size(factories, mocker, logged_in_api_client, prefer
|
|||
|
||||
|
||||
def test_can_clear_playlist_from_api(factories, mocker, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
||||
factories["playlists.PlaylistTrack"].create_batch(size=5, playlist=playlist)
|
||||
url = reverse("api:v1:playlists-clear", kwargs={"pk": playlist.pk})
|
||||
|
@ -165,6 +176,7 @@ def test_can_clear_playlist_from_api(factories, mocker, logged_in_api_client):
|
|||
|
||||
|
||||
def test_update_playlist_from_api(factories, mocker, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
||||
factories["playlists.PlaylistTrack"].create_batch(size=5, playlist=playlist)
|
||||
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
|
||||
|
@ -176,6 +188,7 @@ def test_update_playlist_from_api(factories, mocker, logged_in_api_client):
|
|||
|
||||
|
||||
def test_move_plt_updates_indexes(mocker, factories, logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
||||
plt0 = factories["playlists.PlaylistTrack"](index=0, playlist=playlist)
|
||||
plt1 = factories["playlists.PlaylistTrack"](index=1, playlist=playlist)
|
||||
|
|
|
@ -633,6 +633,7 @@ def test_search3(f, db, logged_in_api_client, factories):
|
|||
|
||||
@pytest.mark.parametrize("f", ["json"])
|
||||
def test_get_playlists(f, db, logged_in_api_client, factories):
|
||||
logged_in_api_client.user.create_actor()
|
||||
url = reverse("api:subsonic:subsonic-get_playlists")
|
||||
assert url.endswith("getPlaylists") is True
|
||||
playlist1 = factories["playlists.PlaylistTrack"](
|
||||
|
@ -664,6 +665,7 @@ def test_get_playlists(f, db, logged_in_api_client, factories):
|
|||
|
||||
@pytest.mark.parametrize("f", ["json"])
|
||||
def test_get_playlist(f, db, logged_in_api_client, factories):
|
||||
logged_in_api_client.user.create_actor()
|
||||
url = reverse("api:subsonic:subsonic-get_playlist")
|
||||
assert url.endswith("getPlaylist") is True
|
||||
playlist = factories["playlists.PlaylistTrack"](
|
||||
|
|
|
@ -190,16 +190,16 @@ def test_can_request_password_reset(
|
|||
|
||||
|
||||
def test_user_can_patch_his_own_settings(logged_in_api_client):
|
||||
logged_in_api_client.user.create_actor()
|
||||
user = logged_in_api_client.user
|
||||
payload = {"privacy_level": "me"}
|
||||
url = reverse("api:v1:users:users-detail", kwargs={"username": user.username})
|
||||
|
||||
response = logged_in_api_client.patch(url, payload)
|
||||
|
||||
assert response.status_code == 200
|
||||
user.refresh_from_db()
|
||||
|
||||
assert user.privacy_level == "me"
|
||||
assert user.actor.privacy_level == "me"
|
||||
|
||||
|
||||
def test_user_can_patch_description(logged_in_api_client):
|
||||
|
@ -554,4 +554,4 @@ def test_user_change_email(logged_in_api_client, mocker, mailoutbox):
|
|||
|
||||
# assert response.status_code == 200
|
||||
# user.refresh_from_db()
|
||||
# assert user.privacy_level == "me"
|
||||
# assert user.actor.privacy_level == "me"
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
# to do : to delete
|
||||
# import pytest
|
||||
# from django.test import Client
|
||||
# from django.urls import reverse
|
||||
|
||||
# from funkwhale_api.common import serializers as common_serializers
|
||||
# from funkwhale_api.common import utils as common_utils
|
||||
# from funkwhale_api.moderation import tasks as moderation_tasks
|
||||
# from funkwhale_api.users.models import User
|
||||
|
||||
|
||||
# def test_can_follow_user(factories, logged_in_api_client, mocker):
|
||||
# followed_user = factories["users.User"]()
|
||||
# actor = factories["federation.Actor"]()
|
||||
# logged_in_api_client.user.actor = actor
|
||||
# url = reverse("api:v2:users:users-follow-requests", kwargs={"pk": followed_user.pk})
|
||||
# routes = mocker.patch("funkwhale_api.federation.api_views.routes.outbox.dispatch")
|
||||
# response = logged_in_api_client.post(url)
|
||||
# assert response.status_code == 204
|
||||
# assert routes.call_count == 1
|
||||
|
||||
|
||||
# def test_can_unfollow(factories, logged_in_api_client, mocker):
|
||||
# logged_in_api_client.user.create_actor()
|
||||
# followed_user = factories["users.User"](with_actor=True)
|
||||
# user_follow = factories["federation.UserFollow"](
|
||||
# target=followed_user, actor=logged_in_api_client.user.actor
|
||||
# )
|
||||
# url = reverse("api:v2:users:users-unfollow", kwargs={"pk": followed_user.pk})
|
||||
# routes = mocker.patch("funkwhale_api.federation.api_views.routes.outbox.dispatch")
|
||||
# response = logged_in_api_client.post(url)
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
# # /users/id/follow_requests/
|
||||
# # def test_can_patch_follow_user(factories, logged_in_api_client, mocker):
|
||||
# # logged_in_api_client.user.create_actor()
|
||||
# # following_user = factories["users.User"](with_actor=True)
|
||||
# # url = reverse(
|
||||
# # "api:v2:users:users-follow-requests",
|
||||
# # kwargs={"pk": logged_in_api_client.user.pk},
|
||||
# # )
|
||||
# # routes = mocker.patch("funkwhale_api.federation.api_views.routes.outbox.dispatch")
|
||||
# # data = {
|
||||
# # "approved": True,
|
||||
# # "actor": following_user.actor,
|
||||
# # "target": logged_in_api_client.user.pk,
|
||||
# # }
|
||||
# # response = logged_in_api_client.patch(url, data=data)
|
||||
# # assert response.status_code == 204
|
||||
# # assert routes.call_count == 1
|
||||
|
||||
|
||||
# def test_can_patch_follow_user_v2(factories, logged_in_api_client, mocker):
|
||||
# logged_in_api_client.user.create_actor()
|
||||
# following_user = factories["users.User"](with_actor=True)
|
||||
# user_follow = factories["federation.UserFollow"](
|
||||
# target=following_user, actor=following_user.actor
|
||||
# )
|
||||
# url = reverse(
|
||||
# "api:v2:users:follow_request_patch",
|
||||
# kwargs={
|
||||
# "user_pk": logged_in_api_client.user.pk,
|
||||
# "follow_pk": user_follow.pk,
|
||||
# },
|
||||
# )
|
||||
# routes = mocker.patch("funkwhale_api.federation.api_views.routes.outbox.dispatch")
|
||||
# data = {
|
||||
# "approved": True,
|
||||
# }
|
||||
# response = logged_in_api_client.patch(url, data=data, format="json")
|
||||
# assert response.status_code == 204
|
||||
# assert routes.call_count == 1
|
||||
|
||||
|
||||
# # def test_only_target_user_can_patch_follow_user(factories, logged_in_api_client):
|
||||
# # logged_in_api_client.user.create_actor()
|
||||
# # followed_user = factories["users.User"]()
|
||||
# # url = reverse("api:v2:users:users-follow-requests", kwargs={"pk": followed_user.pk})
|
||||
# # data = {
|
||||
# # "approved": True,
|
||||
# # "actor": logged_in_api_client.user.actor,
|
||||
# # "target": followed_user.pk,
|
||||
# # }
|
||||
# # response = logged_in_api_client.patch(url, data=data)
|
||||
# # assert response.status_code == 403
|
||||
|
||||
|
||||
# def test_can_get_my_userfollowings(factories, logged_in_api_client, mocker):
|
||||
# logged_in_api_client.user.create_actor()
|
||||
# followed_user = factories["users.User"](with_actor=True)
|
||||
# user_follow = factories["federation.UserFollow"](
|
||||
# actor=logged_in_api_client.user.actor, target=followed_user
|
||||
# )
|
||||
# url = reverse(
|
||||
# "api:v2:users:users-followings", kwargs={"pk": logged_in_api_client.user.pk}
|
||||
# )
|
||||
# response = logged_in_api_client.get(url)
|
||||
# assert response.status_code == 200
|
||||
# assert str(user_follow.uuid) == response.data["results"][0]["uuid"]
|
||||
|
||||
|
||||
# def test_can_get_user_public_profile_userfollowings(
|
||||
# factories, logged_in_api_client, mocker
|
||||
# ):
|
||||
# logged_in_api_client.user.create_actor()
|
||||
# following_user = factories["users.User"](with_actor=True, privacy_level="public")
|
||||
# user_follow = factories["federation.UserFollow"](actor=following_user.actor)
|
||||
# url = reverse("api:v2:users:users-followings", kwargs={"pk": following_user.pk})
|
||||
# response = logged_in_api_client.get(url)
|
||||
# assert response.status_code == 200
|
||||
# assert str(user_follow.uuid) == response.data["results"][0]["uuid"]
|
||||
|
||||
|
||||
# def test_cannot_get_user_private_profile_userfollowings(
|
||||
# factories, logged_in_api_client, mocker
|
||||
# ):
|
||||
# logged_in_api_client.user.create_actor()
|
||||
# following_user = factories["users.User"](with_actor=True, privacy_level="private")
|
||||
# user_follow = factories["federation.UserFollow"](actor=following_user.actor)
|
||||
# url = reverse("api:v2:users:users-followings", kwargs={"pk": following_user.pk})
|
||||
# response = logged_in_api_client.get(url)
|
||||
# assert response.status_code == 403
|
||||
|
||||
|
||||
# def test_can_get_user_pod_profile_userfollowings(
|
||||
# factories, logged_in_api_client, mocker
|
||||
# ):
|
||||
# logged_in_api_client.user.create_actor()
|
||||
# following_user = factories["users.User"](with_actor=True, privacy_level="pod")
|
||||
# user_follow = factories["federation.UserFollow"](actor=following_user.actor)
|
||||
# url = reverse("api:v2:users:users-followings", kwargs={"pk": following_user.pk})
|
||||
# response = logged_in_api_client.get(url)
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
# def test_cannot_get_user_pod_profile_userfollowings(
|
||||
# factories, logged_in_api_client, mocker
|
||||
# ):
|
||||
# factories["federation.Domain"](name="notatalllocal")
|
||||
# logged_in_api_client.user.create_actor(domain_id="notatalllocal")
|
||||
# following_user = factories["users.User"](with_actor=True, privacy_level="pod")
|
||||
# user_follow = factories["federation.UserFollow"](actor=following_user.actor)
|
||||
# url = reverse("api:v2:users:users-followings", kwargs={"pk": following_user.pk})
|
||||
# response = logged_in_api_client.get(url)
|
||||
# assert response.status_code == 403
|
||||
|
||||
|
||||
# def test_can_get_my_followers(factories, logged_in_api_client, mocker):
|
||||
# following_user = factories["users.User"](with_actor=True)
|
||||
# user_follow = factories["federation.UserFollow"](
|
||||
# target=logged_in_api_client.user, actor=following_user.actor
|
||||
# )
|
||||
# url = reverse(
|
||||
# "api:v2:users:users-followers", kwargs={"pk": logged_in_api_client.user.pk}
|
||||
# )
|
||||
# response = logged_in_api_client.get(url)
|
||||
# assert response.status_code == 200
|
||||
# assert str(user_follow.uuid) == response.data["results"][0]["uuid"]
|
||||
|
||||
|
||||
# # to do : do this through should_autoapprove_follow autoapprove = serializer.validated_data["object"].should_autoapprove_follow(
|
||||
# def test_follow_public_user_autoapprove(factories, logged_in_api_client, mocker):
|
||||
# logged_in_api_client.user.create_actor()
|
||||
# followed_user = factories["users.User"](with_actor=True, privacy_level="public")
|
||||
# url = reverse("api:v2:users:users-follow-requests", kwargs={"pk": followed_user.pk})
|
||||
# routes = mocker.patch("funkwhale_api.federation.api_views.routes.outbox.dispatch")
|
||||
# response = logged_in_api_client.post(url)
|
||||
# assert response.status_code == 204
|
||||
# assert routes.call_count == 1
|
Ładowanie…
Reference in New Issue