Verified Commit 37b6dd40 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch 'release/0.6'

parents 4530e4f4 6011cf20
from django.conf import settings
from rest_framework import serializers
from funkwhale_api.activity import serializers as activity_serializers
from funkwhale_api.music.serializers import TrackSerializerNested
from funkwhale_api.music.serializers import TrackActivitySerializer
from funkwhale_api.users.serializers import UserActivitySerializer
from . import models
class TrackFavoriteActivitySerializer(activity_serializers.ModelSerializer):
type = serializers.SerializerMethodField()
object = TrackActivitySerializer(source='track')
actor = UserActivitySerializer(source='user')
published = serializers.DateTimeField(source='creation_date')
class Meta:
model = models.TrackFavorite
fields = [
'id',
'local_id',
'object',
'type',
'actor',
'published'
]
def get_actor(self, obj):
return UserActivitySerializer(obj.user).data
def get_type(self, obj):
return 'Like'
class UserTrackFavoriteSerializer(serializers.ModelSerializer):
# track = TrackSerializerNested(read_only=True)
class Meta:
......
......@@ -4,6 +4,7 @@ from rest_framework.response import Response
from rest_framework import pagination
from rest_framework.decorators import list_route
from funkwhale_api.activity import record
from funkwhale_api.music.models import Track
from funkwhale_api.common.permissions import ConditionalAuthentication
......@@ -33,6 +34,7 @@ class TrackFavoriteViewSet(mixins.CreateModelMixin,
instance = self.perform_create(serializer)
serializer = self.get_serializer(instance=instance)
headers = self.get_success_headers(serializer.data)
record.send(instance)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def get_queryset(self):
......
from funkwhale_api.common import channels
from funkwhale_api.activity import record
from . import serializers
record.registry.register_serializer(
serializers.ListeningActivitySerializer)
@record.registry.register_consumer('history.Listening')
def broadcast_listening_to_instance_activity(data, obj):
if obj.user.privacy_level not in ['instance', 'everyone']:
return
channels.group_send('instance_activity', {
'type': 'event.send',
'text': '',
'data': data
})
......@@ -25,3 +25,8 @@ class Listening(models.Model):
raise ValidationError('Cannot have both session_key and user empty for listening')
super().save(**kwargs)
def get_activity_url(self):
return '{}/listenings/tracks/{}'.format(
self.user.get_activity_url(), self.pk)
from rest_framework import serializers
from funkwhale_api.activity import serializers as activity_serializers
from funkwhale_api.music.serializers import TrackSerializerNested
from funkwhale_api.music.serializers import TrackActivitySerializer
from funkwhale_api.users.serializers import UserActivitySerializer
from . import models
class ListeningActivitySerializer(activity_serializers.ModelSerializer):
type = serializers.SerializerMethodField()
object = TrackActivitySerializer(source='track')
actor = UserActivitySerializer(source='user')
published = serializers.DateTimeField(source='end_date')
class Meta:
model = models.Listening
fields = [
'id',
'local_id',
'object',
'type',
'actor',
'published'
]
def get_actor(self, obj):
return UserActivitySerializer(obj.user).data
def get_type(self, obj):
return 'Listen'
class ListeningSerializer(serializers.ModelSerializer):
class Meta:
......
......@@ -3,8 +3,9 @@ from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import detail_route
from funkwhale_api.music.serializers import TrackSerializerNested
from funkwhale_api.activity import record
from funkwhale_api.common.permissions import ConditionalAuthentication
from funkwhale_api.music.serializers import TrackSerializerNested
from . import models
from . import serializers
......@@ -17,6 +18,12 @@ class ListeningViewSet(mixins.CreateModelMixin,
queryset = models.Listening.objects.all()
permission_classes = [ConditionalAuthentication]
def perform_create(self, serializer):
r = super().perform_create(serializer)
if self.request.user.is_authenticated:
record.send(serializer.instance)
return r
def get_queryset(self):
queryset = super().get_queryset()
if self.request.user.is_authenticated:
......
from funkwhale_api.common.consumers import JsonAuthConsumer
class InstanceActivityConsumer(JsonAuthConsumer):
groups = ["instance_activity"]
def event_send(self, message):
self.send_json(message['data'])
......@@ -360,6 +360,12 @@ class Track(APIModelMixin):
self.title,
)
def get_activity_url(self):
if self.mbid:
return 'https://musicbrainz.org/recording/{}'.format(
self.mbid)
return settings.FUNKWHALE_URL + '/tracks/{}'.format(self.pk)
class TrackFile(models.Model):
track = models.ForeignKey(
......
from rest_framework import serializers
from taggit.models import Tag
from funkwhale_api.activity import serializers as activity_serializers
from . import models
......@@ -127,3 +129,24 @@ class ImportBatchSerializer(serializers.ModelSerializer):
model = models.ImportBatch
fields = ('id', 'jobs', 'status', 'creation_date', 'import_request')
read_only_fields = ('creation_date',)
class TrackActivitySerializer(activity_serializers.ModelSerializer):
type = serializers.SerializerMethodField()
name = serializers.CharField(source='title')
artist = serializers.CharField(source='artist.name')
album = serializers.CharField(source='album.title')
class Meta:
model = models.Track
fields = [
'id',
'local_id',
'name',
'type',
'artist',
'album',
]
def get_type(self, obj):
return 'Audio'
# Generated by Django 2.0.2 on 2018-03-01 19:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0003_auto_20171226_1357'),
]
operations = [
migrations.AddField(
model_name='user',
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),
),
]
......@@ -3,6 +3,7 @@ from __future__ import unicode_literals, absolute_import
import uuid
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.urls import reverse
from django.db import models
......@@ -10,6 +11,14 @@ from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
PRIVACY_LEVEL_CHOICES = [
('me', 'Only me'),
('followers', 'Me and my followers'),
('instance', 'Everyone on my instance, and my followers'),
('everyone', 'Everyone, including people on other instances'),
]
@python_2_unicode_compatible
class User(AbstractUser):
......@@ -30,6 +39,9 @@ class User(AbstractUser):
},
}
privacy_level = models.CharField(
max_length=30, choices=PRIVACY_LEVEL_CHOICES, default='instance')
def __str__(self):
return self.username
......@@ -43,3 +55,6 @@ class User(AbstractUser):
def set_password(self, raw_password):
super().set_password(raw_password)
self.update_secret_key()
def get_activity_url(self):
return settings.FUNKWHALE_URL + '/@{}'.format(self.username)
from rest_framework import serializers
from funkwhale_api.activity import serializers as activity_serializers
from . import models
class UserActivitySerializer(activity_serializers.ModelSerializer):
type = serializers.SerializerMethodField()
name = serializers.CharField(source='username')
local_id = serializers.CharField(source='username')
class Meta:
model = models.User
fields = [
'id',
'local_id',
'name',
'type'
]
def get_type(self, obj):
return 'Person'
class UserBasicSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ['id', 'username', 'name', 'date_joined']
class UserSerializer(serializers.ModelSerializer):
class UserWriteSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = [
'name',
'privacy_level'
]
class UserReadSerializer(serializers.ModelSerializer):
permissions = serializers.SerializerMethodField()
......@@ -24,6 +53,7 @@ class UserSerializer(serializers.ModelSerializer):
'is_superuser',
'permissions',
'date_joined',
'privacy_level'
]
def get_permissions(self, o):
......
from rest_framework.response import Response
from rest_framework import mixins
from rest_framework import viewsets
from rest_framework.decorators import list_route
......@@ -23,12 +24,25 @@ class RegisterView(BaseRegisterView):
return get_adapter().is_open_for_signup(request)
class UserViewSet(viewsets.GenericViewSet):
class UserViewSet(
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
queryset = models.User.objects.all()
serializer_class = serializers.UserSerializer
serializer_class = serializers.UserWriteSerializer
lookup_field = 'username'
@list_route(methods=['get'])
def me(self, request, *args, **kwargs):
"""Return information about the current user"""
serializer = self.serializer_class(request.user)
serializer = serializers.UserReadSerializer(request.user)
return Response(serializer.data)
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)
build-essential
gettext
zlib1g-dev
curl
ffmpeg
libjpeg-dev
zlib1g-dev
libmagic-dev
libpq-dev
postgresql-client
libmagic-dev
ffmpeg
python3-dev
curl
......@@ -59,3 +59,5 @@ pyacoustid>=1.1.5,<1.2
raven>=6.5,<7
python-magic==0.4.15
ffmpeg-python==0.1.10
channels>=2,<2.1
channels_redis>=2.1,<2.2
......@@ -4,7 +4,4 @@
# WSGI Handler
# ------------------------------------------------
# there's no python 3 support in stable, have to use the latest release candidate for gevent
gevent==1.1rc1
gunicorn==19.4.1
daphne==2.0.4
......@@ -12,5 +12,6 @@ services:
environment:
- "DJANGO_ALLOWED_HOSTS=localhost"
- "DATABASE_URL=postgresql://postgres@postgres/postgres"
- "FUNKWHALE_URL=https://funkwhale.test"
postgres:
image: postgres
import pytest
from django.db import models
from rest_framework import serializers
from funkwhale_api.activity import record
class FakeModel(models.Model):
class Meta:
app_label = 'tests'
class FakeSerializer(serializers.ModelSerializer):
class Meta:
model = FakeModel
fields = ['id']
def test_can_bind_serializer_to_model(activity_registry):
activity_registry.register_serializer(FakeSerializer)
assert activity_registry['tests.FakeModel']['serializer'] == FakeSerializer
def test_can_bind_consumer_to_model(activity_registry):
activity_registry.register_serializer(FakeSerializer)
@activity_registry.register_consumer('tests.FakeModel')
def propagate(data, obj):
return True
assert activity_registry['tests.FakeModel']['consumers'] == [propagate]
def test_record_object_calls_consumer(activity_registry, mocker):
activity_registry.register_serializer(FakeSerializer)
stub = mocker.stub()
activity_registry.register_consumer('tests.FakeModel')(stub)
o = FakeModel(id=1)
data = FakeSerializer(o).data
record.send(o)
stub.assert_called_once_with(data=data, obj=o)
import pytest
from rest_framework_jwt.settings import api_settings
from funkwhale_api.common.auth import TokenAuthMiddleware
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
@pytest.mark.parametrize('query_string', [
b'token=wrong',
b'',
])
def test_header_anonymous(query_string, factories):
def callback(scope):
assert scope['user'].is_anonymous
scope = {
'query_string': query_string
}
consumer = TokenAuthMiddleware(callback)
consumer(scope)
def test_header_correct_token(factories):
user = factories['users.User']()
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
def callback(scope):
assert scope['user'] == user
scope = {
'query_string': 'token={}'.format(token).encode('utf-8')
}
consumer = TokenAuthMiddleware(callback)
consumer(scope)
from funkwhale_api.common import consumers
def test_auth_consumer_requires_valid_user(mocker):
m = mocker.patch('funkwhale_api.common.consumers.JsonAuthConsumer.close')
scope = {'user': None}
consumer = consumers.JsonAuthConsumer(scope=scope)
consumer.connect()
m.assert_called_once_with()
def test_auth_consumer_requires_user_in_scope(mocker):
m = mocker.patch('funkwhale_api.common.consumers.JsonAuthConsumer.close')
scope = {}
consumer = consumers.JsonAuthConsumer(scope=scope)
consumer.connect()
m.assert_called_once_with()
def test_auth_consumer_accepts_connection(mocker, factories):
user = factories['users.User']()
m = mocker.patch('funkwhale_api.common.consumers.JsonAuthConsumer.accept')
scope = {'user': user}
consumer = consumers.JsonAuthConsumer(scope=scope)
consumer.connect()
m.assert_called_once_with()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment