diff --git a/api/funkwhale_api/common/mutations.py b/api/funkwhale_api/common/mutations.py index 11624e9f6..c3e92c15b 100644 --- a/api/funkwhale_api/common/mutations.py +++ b/api/funkwhale_api/common/mutations.py @@ -114,7 +114,14 @@ class UpdateMutationSerializer(serializers.ModelSerializer, MutationSerializer): # to ensure we store ids instead of model instances in our json # payload for field, attr in self.serialized_relations.items(): - data[field] = getattr(data[field], attr) + try: + obj = data[field] + except KeyError: + continue + if obj is None: + data[field] = None + else: + data[field] = getattr(obj, attr) return data def create(self, validated_data): diff --git a/api/funkwhale_api/common/pagination.py b/api/funkwhale_api/common/pagination.py index e5068bce2..ec7c27dc4 100644 --- a/api/funkwhale_api/common/pagination.py +++ b/api/funkwhale_api/common/pagination.py @@ -1,6 +1,29 @@ -from rest_framework.pagination import PageNumberPagination +from rest_framework.pagination import PageNumberPagination, _positive_int class FunkwhalePagination(PageNumberPagination): page_size_query_param = "page_size" - max_page_size = 50 + default_max_page_size = 50 + default_page_size = None + view = None + + def paginate_queryset(self, queryset, request, view=None): + self.view = view + return super().paginate_queryset(queryset, request, view) + + def get_page_size(self, request): + max_page_size = ( + getattr(self.view, "max_page_size", 0) or self.default_max_page_size + ) + page_size = getattr(self.view, "default_page_size", 0) or max_page_size + if self.page_size_query_param: + try: + return _positive_int( + request.query_params[self.page_size_query_param], + strict=True, + cutoff=max_page_size, + ) + except (KeyError, ValueError): + pass + + return page_size diff --git a/api/funkwhale_api/music/mutations.py b/api/funkwhale_api/music/mutations.py index 51efa0ab8..4d78b8ea9 100644 --- a/api/funkwhale_api/music/mutations.py +++ b/api/funkwhale_api/music/mutations.py @@ -21,4 +21,4 @@ class TrackMutationSerializer(mutations.UpdateMutationSerializer): class Meta: model = models.Track - fields = ["license", "title", "position"] + fields = ["license", "title", "position", "copyright"] diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py index b5242eeb1..2f0e67cb9 100644 --- a/api/funkwhale_api/music/views.py +++ b/api/funkwhale_api/music/views.py @@ -524,6 +524,7 @@ class LicenseViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.LicenseSerializer queryset = models.License.objects.all().order_by("code") lookup_value_regex = ".*" + max_page_size = 1000 def get_queryset(self): # ensure our licenses are up to date in DB diff --git a/api/tests/common/test_pagination.py b/api/tests/common/test_pagination.py new file mode 100644 index 000000000..cacbe740c --- /dev/null +++ b/api/tests/common/test_pagination.py @@ -0,0 +1,29 @@ +import pytest + +from funkwhale_api.common import pagination + + +@pytest.mark.parametrize( + "view_max_page_size, view_default_page_size, request_page_size, expected", + [ + (50, 50, None, 50), + (50, 25, None, 25), + (25, None, None, 25), + (50, 25, 100, 50), + (50, None, 100, 50), + (50, 25, 33, 33), + ], +) +def test_funkwhale_pagination_uses_view_page_size( + view_max_page_size, view_default_page_size, request_page_size, expected, mocker +): + p = pagination.FunkwhalePagination() + + p.view = mocker.Mock( + max_page_size=view_max_page_size, default_page_size=view_default_page_size + ) + query = {} + if request_page_size: + query["page_size"] = request_page_size + request = mocker.Mock(query_params=query) + assert p.get_page_size(request) == expected diff --git a/api/tests/music/test_mutations.py b/api/tests/music/test_mutations.py index d6b8223d4..bc9e81f8e 100644 --- a/api/tests/music/test_mutations.py +++ b/api/tests/music/test_mutations.py @@ -13,6 +13,18 @@ def test_track_license_mutation(factories, now): assert track.license.code == "cc-by-sa-4.0" +def test_track_null_license_mutation(factories, now): + track = factories["music.Track"](license="cc-by-sa-4.0") + mutation = factories["common.Mutation"]( + type="update", target=track, payload={"license": None} + ) + licenses.load(licenses.LICENSES) + mutation.apply() + track.refresh_from_db() + + assert track.license is None + + def test_track_title_mutation(factories, now): track = factories["music.Track"](title="foo") mutation = factories["common.Mutation"]( @@ -24,6 +36,17 @@ def test_track_title_mutation(factories, now): assert track.title == "bar" +def test_track_copyright_mutation(factories, now): + track = factories["music.Track"](copyright="foo") + mutation = factories["common.Mutation"]( + type="update", target=track, payload={"copyright": "bar"} + ) + mutation.apply() + track.refresh_from_db() + + assert track.copyright == "bar" + + def test_track_position_mutation(factories): track = factories["music.Track"](position=4) mutation = factories["common.Mutation"]( diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py index 7b12c6c8f..2c3a61c05 100644 --- a/api/tests/music/test_views.py +++ b/api/tests/music/test_views.py @@ -612,7 +612,7 @@ def test_list_licenses(api_client, preferences, mocker): expected = [ serializers.LicenseSerializer(l.conf).data - for l in models.License.objects.order_by("code")[:25] + for l in models.License.objects.order_by("code") ] url = reverse("api:v1:licenses-list") diff --git a/front/src/components/library/EditForm.vue b/front/src/components/library/EditForm.vue index 000108119..a2df96c00 100644 --- a/front/src/components/library/EditForm.vue +++ b/front/src/components/library/EditForm.vue @@ -59,10 +59,28 @@ + + + + + + +