Example #1
0
def test_channel_serializer_external_representation(factories, to_api_date):
    content = factories["common.Content"]()
    channel = factories["audio.Channel"](artist__description=content,
                                         external=True)

    expected = {
        "artist":
        music_serializers.serialize_artist_simple(channel.artist),
        "uuid":
        str(channel.uuid),
        "creation_date":
        to_api_date(channel.creation_date),
        "actor":
        None,
        "attributed_to":
        federation_serializers.APIActorSerializer(channel.attributed_to).data,
        "metadata": {},
        "rss_url":
        channel.get_rss_url(),
        "url":
        channel.actor.url,
    }
    expected["artist"]["description"] = common_serializers.ContentSerializer(
        content).data

    assert serializers.ChannelSerializer(channel).data == expected
Example #2
0
def test_can_fetch_data_from_api(api_client, factories):
    url = reverse("api:v1:users:users-me")
    response = api_client.get(url)
    # login required
    assert response.status_code == 401

    user = factories["users.User"](permission_library=True, with_actor=True)
    summary = {"content_type": "text/plain", "text": "Hello"}
    summary_obj = common_utils.attach_content(user.actor, "summary_obj", summary)
    avatar = factories["common.Attachment"]()
    user.actor.attachment_icon = avatar
    user.actor.save()
    api_client.login(username=user.username, password="******")
    response = api_client.get(url)
    assert response.status_code == 200
    assert response.data["username"] == user.username
    assert response.data["is_staff"] == user.is_staff
    assert response.data["is_superuser"] == user.is_superuser
    assert response.data["email"] == user.email
    assert response.data["name"] == user.name
    assert response.data["permissions"] == user.get_permissions()
    assert (
        response.data["avatar"] == common_serializers.AttachmentSerializer(avatar).data
    )
    assert (
        response.data["summary"]
        == common_serializers.ContentSerializer(summary_obj).data
    )
Example #3
0
def serialize_artist_simple(artist):
    data = {
        "id": artist.id,
        "fid": artist.fid,
        "mbid": str(artist.mbid),
        "name": artist.name,
        "creation_date": DATETIME_FIELD.to_representation(artist.creation_date),
        "modification_date": DATETIME_FIELD.to_representation(artist.modification_date),
        "is_local": artist.is_local,
        "content_category": artist.content_category,
    }
    if "description" in artist._state.fields_cache:
        data["description"] = (
            common_serializers.ContentSerializer(artist.description).data
            if artist.description
            else None
        )

    if "attachment_cover" in artist._state.fields_cache:
        data["cover"] = (
            cover_field.to_representation(artist.attachment_cover)
            if artist.attachment_cover
            else None
        )

    if getattr(artist, "_tracks_count", None) is not None:
        data["tracks_count"] = artist._tracks_count

    if getattr(artist, "_prefetched_tagged_items", None) is not None:
        data["tags"] = [ti.tag.name for ti in artist._prefetched_tagged_items]

    return data
Example #4
0
def test_detail_serializers_with_description_description(
        factory_name, serializer_class, factories):
    content = factories["common.Content"]()
    obj = factories[factory_name](description=content)
    expected = common_serializers.ContentSerializer(content).data
    serializer = serializer_class(obj, context={"description": True})
    assert serializer.data["description"] == expected
Example #5
0
def test_api_full_actor_serializer(factories, to_api_date):
    summary = factories["common.Content"]()
    icon = factories["common.Attachment"]()
    user = factories["users.User"]()
    actor = user.create_actor(summary_obj=summary, attachment_icon=icon)
    expected = {
        "fid": actor.fid,
        "url": actor.url,
        "creation_date": to_api_date(actor.creation_date),
        "last_fetch_date": to_api_date(actor.last_fetch_date),
        "user": users_serializers.UserBasicSerializer(user).data,
        "is_channel": False,
        "domain": actor.domain_id,
        "type": actor.type,
        "manually_approves_followers": actor.manually_approves_followers,
        "full_username": actor.full_username,
        "name": actor.name,
        "preferred_username": actor.preferred_username,
        "is_local": actor.is_local,
        "summary": common_serializers.ContentSerializer(summary).data,
        "icon": common_serializers.AttachmentSerializer(icon).data,
    }

    serializer = api_serializers.FullActorSerializer(actor)

    assert serializer.data == expected
Example #6
0
    def to_representation(self, obj):
        repr = super().to_representation(obj)
        if self.context.get("description", False):
            description = obj.description
            repr["description"] = (
                common_serializers.ContentSerializer(description).data
                if description
                else None
            )

        return repr
Example #7
0
def test_content_serializer(factories):
    content = factories["common.Content"]()

    expected = {
        "text": content.text,
        "content_type": content.content_type,
        "html": utils.render_html(content.text, content.content_type),
    }

    serializer = serializers.ContentSerializer(content)

    assert serializer.data == expected
class CustomFormSerializer(serializers.Serializer):
    help_text = common_serializers.ContentSerializer(required=False,
                                                     allow_null=True)
    fields = serializers.ListField(child=CustomFieldSerializer(),
                                   min_length=0,
                                   max_length=10,
                                   required=False)

    def validate_help_text(self, v):
        if not v:
            return
        v["html"] = common_utils.render_html(v["text"],
                                             content_type=v["content_type"],
                                             permissive=True)
        return v
Example #9
0
class DescriptionMutation(mutations.UpdateMutationSerializer):
    description = common_serializers.ContentSerializer()

    def get_previous_state_handlers(self):
        handlers = super().get_previous_state_handlers()
        handlers["description"] = (lambda obj: common_serializers.
                                   ContentSerializer(obj.description).data
                                   if obj.description_id else None)
        return handlers

    def update(self, instance, validated_data):
        description = validated_data.pop("description", NOOP)
        r = super().update(instance, validated_data)
        if description != NOOP:
            common_utils.attach_content(instance, "description", description)
        return r
Example #10
0
class UserWriteSerializer(serializers.ModelSerializer):
    summary = common_serializers.ContentSerializer(required=False,
                                                   allow_null=True)
    avatar = common_serializers.RelatedField(
        "uuid",
        queryset=common_models.Attachment.objects.all().local().attached(
            False),
        serializer=None,
        queryset_filter=lambda qs, context: qs.filter(actor=context["request"].
                                                      user.actor),
        write_only=True,
    )

    class Meta:
        model = models.User
        fields = [
            "name",
            "privacy_level",
            "avatar",
            "instance_support_message_display_date",
            "funkwhale_support_message_display_date",
            "summary",
        ]

    def update(self, obj, validated_data):
        if not obj.actor:
            obj.create_actor()
        summary = validated_data.pop("summary", NOOP)
        avatar = validated_data.pop("avatar", NOOP)

        obj = super().update(obj, validated_data)

        if summary != NOOP:
            common_utils.attach_content(obj.actor, "summary_obj", summary)
        if avatar != NOOP:
            obj.actor.attachment_icon = avatar
            obj.actor.save(update_fields=["attachment_icon"])
        return obj

    def to_representation(self, instance):
        r = super().to_representation(instance)
        r["avatar"] = common_serializers.AttachmentSerializer(
            instance.get_avatar()).data
        return r
Example #11
0
class AlbumCreateSerializer(serializers.Serializer):
    title = serializers.CharField(required=True, max_length=255)
    cover = COVER_WRITE_FIELD
    release_date = serializers.DateField(required=False, allow_null=True)
    tags = tags_serializers.TagsListField(required=False)
    description = common_serializers.ContentSerializer(allow_null=True, required=False)

    artist = common_serializers.RelatedField(
        "id",
        queryset=models.Artist.objects.exclude(channel__isnull=True),
        required=True,
        serializer=None,
        filters=lambda context: {"attributed_to": context["user"].actor},
    )

    def validate(self, validated_data):
        duplicates = validated_data["artist"].albums.filter(
            title__iexact=validated_data["title"]
        )
        if duplicates.exists():
            raise serializers.ValidationError("An album with this title already exist")

        return super().validate(validated_data)

    def to_representation(self, obj):
        obj.artist.attachment_cover
        return AlbumSerializer(obj, context=self.context).data

    def create(self, validated_data):
        instance = models.Album.objects.create(
            attributed_to=self.context["user"].actor,
            artist=validated_data["artist"],
            release_date=validated_data.get("release_date"),
            title=validated_data["title"],
            attachment_cover=validated_data.get("cover"),
        )
        common_utils.attach_content(
            instance, "description", validated_data.get("description")
        )
        tag_models.set_tags(instance, *(validated_data.get("tags", []) or []))
        instance.artist.get_channel()
        return instance
Example #12
0
def test_album_mutation_description(factory_name, factories, mocker):
    mocker.patch("funkwhale_api.federation.routes.outbox.dispatch")
    content = factories["common.Content"]()
    obj = factories[factory_name](description=content)
    mutation = factories["common.Mutation"](
        type="update",
        target=obj,
        payload={
            "description": {
                "content_type": "text/plain",
                "text": "hello there"
            }
        },
    )
    mutation.apply()
    obj.refresh_from_db()

    assert obj.description.content_type == "text/plain"
    assert obj.description.text == "hello there"
    assert (mutation.previous_state["description"] ==
            common_serializers.ContentSerializer(content).data)
Example #13
0
class FullActorSerializer(serializers.Serializer):
    fid = serializers.URLField()
    url = serializers.URLField()
    domain = serializers.CharField(source="domain_id")
    creation_date = serializers.DateTimeField()
    last_fetch_date = serializers.DateTimeField()
    name = serializers.CharField()
    preferred_username = serializers.CharField()
    full_username = serializers.CharField()
    type = serializers.CharField()
    is_local = serializers.BooleanField()
    is_channel = serializers.SerializerMethodField()
    manually_approves_followers = serializers.BooleanField()
    user = users_serializers.UserBasicSerializer()
    summary = common_serializers.ContentSerializer(source="summary_obj")
    icon = common_serializers.AttachmentSerializer(source="attachment_icon")

    def get_is_channel(self, o):
        try:
            return bool(o.channel)
        except ObjectDoesNotExist:
            return False
Example #14
0
class ChannelCreateSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=music_models.MAX_LENGTHS["ARTIST_NAME"])
    username = serializers.CharField(
        max_length=music_models.MAX_LENGTHS["ARTIST_NAME"],
        validators=[users_serializers.ASCIIUsernameValidator()],
    )
    description = common_serializers.ContentSerializer(allow_null=True)
    tags = tags_serializers.TagsListField()
    content_category = serializers.ChoiceField(
        choices=music_models.ARTIST_CONTENT_CATEGORY_CHOICES
    )
    metadata = serializers.DictField(required=False)
    cover = music_serializers.COVER_WRITE_FIELD

    def validate(self, validated_data):
        existing_channels = self.context["actor"].owned_channels.count()
        if existing_channels >= preferences.get("audio__max_channels"):
            raise serializers.ValidationError(
                "You have reached the maximum amount of allowed channels"
            )
        validated_data = super().validate(validated_data)
        metadata = validated_data.pop("metadata", {})
        if validated_data["content_category"] == "podcast":
            metadata_serializer = ChannelMetadataSerializer(data=metadata)
            metadata_serializer.is_valid(raise_exception=True)
            metadata = metadata_serializer.validated_data
        validated_data["metadata"] = metadata
        return validated_data

    def validate_username(self, value):
        if value.lower() in [n.lower() for n in settings.ACCOUNT_USERNAME_BLACKLIST]:
            raise serializers.ValidationError("This username is already taken")

        matching = federation_models.Actor.objects.local().filter(
            preferred_username__iexact=value
        )
        if matching.exists():
            raise serializers.ValidationError("This username is already taken")
        return value

    @transaction.atomic
    def create(self, validated_data):
        from . import views

        cover = validated_data.pop("cover", None)
        description = validated_data.get("description")
        artist = music_models.Artist.objects.create(
            attributed_to=validated_data["attributed_to"],
            name=validated_data["name"],
            content_category=validated_data["content_category"],
            attachment_cover=cover,
        )
        common_utils.attach_content(artist, "description", description)

        if validated_data.get("tags", []):
            tags_models.set_tags(artist, *validated_data["tags"])

        channel = models.Channel(
            artist=artist,
            attributed_to=validated_data["attributed_to"],
            metadata=validated_data["metadata"],
        )
        channel.actor = models.generate_actor(
            validated_data["username"], name=validated_data["name"],
        )

        channel.library = music_models.Library.objects.create(
            name=channel.actor.preferred_username,
            privacy_level="everyone",
            actor=validated_data["attributed_to"],
        )
        channel.save()
        channel = views.ChannelViewSet.queryset.get(pk=channel.pk)
        return channel

    def to_representation(self, obj):
        return ChannelSerializer(obj, context=self.context).data
Example #15
0
class ChannelUpdateSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=music_models.MAX_LENGTHS["ARTIST_NAME"])
    description = common_serializers.ContentSerializer(allow_null=True)
    tags = tags_serializers.TagsListField()
    content_category = serializers.ChoiceField(
        choices=music_models.ARTIST_CONTENT_CATEGORY_CHOICES
    )
    metadata = serializers.DictField(required=False)
    cover = music_serializers.COVER_WRITE_FIELD

    def validate(self, validated_data):
        validated_data = super().validate(validated_data)
        require_metadata_validation = False
        new_content_category = validated_data.get("content_category")
        metadata = validated_data.pop("metadata", NOOP)
        if (
            new_content_category == "podcast"
            and self.instance.artist.content_category != "postcast"
        ):
            # updating channel, setting as podcast
            require_metadata_validation = True
        elif self.instance.artist.content_category == "postcast" and metadata != NOOP:
            # channel is podcast, and metadata was updated
            require_metadata_validation = True
        else:
            metadata = self.instance.metadata

        if require_metadata_validation:
            metadata_serializer = ChannelMetadataSerializer(data=metadata)
            metadata_serializer.is_valid(raise_exception=True)
            metadata = metadata_serializer.validated_data

        validated_data["metadata"] = metadata
        return validated_data

    @transaction.atomic
    def update(self, obj, validated_data):
        if validated_data.get("tags") is not None:
            tags_models.set_tags(obj.artist, *validated_data["tags"])
        actor_update_fields = []
        artist_update_fields = []

        obj.metadata = validated_data["metadata"]
        obj.save(update_fields=["metadata"])

        if "description" in validated_data:
            common_utils.attach_content(
                obj.artist, "description", validated_data["description"]
            )

        if "name" in validated_data:
            actor_update_fields.append(("name", validated_data["name"]))
            artist_update_fields.append(("name", validated_data["name"]))

        if "content_category" in validated_data:
            artist_update_fields.append(
                ("content_category", validated_data["content_category"])
            )

        if "cover" in validated_data:
            artist_update_fields.append(("attachment_cover", validated_data["cover"]))

        if actor_update_fields:
            for field, value in actor_update_fields:
                setattr(obj.actor, field, value)
            obj.actor.save(update_fields=[f for f, _ in actor_update_fields])

        if artist_update_fields:
            for field, value in artist_update_fields:
                setattr(obj.artist, field, value)
            obj.artist.save(update_fields=[f for f, _ in artist_update_fields])

        return obj

    def to_representation(self, obj):
        return ChannelSerializer(obj, context=self.context).data
Example #16
0
 def get_summary(self, o):
     if not o.actor or not o.actor.summary_obj:
         return
     return common_serializers.ContentSerializer(o.actor.summary_obj).data