Multi-platform: support multiple files per version #201

Merged
Oleg-Komarov merged 43 commits from multi-os into main 2024-07-09 16:27:46 +02:00
2 changed files with 80 additions and 39 deletions
Showing only changes of commit 84fc9b30a4 - Show all commits

View File

@ -5,8 +5,9 @@ from django.urls import reverse
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase, APIClient from rest_framework.test import APITestCase, APIClient
from common.tests.factories.users import UserFactory
from common.tests.factories.extensions import create_approved_version, create_version from common.tests.factories.extensions import create_approved_version, create_version
from common.tests.factories.files import FileFactory
from common.tests.factories.users import UserFactory
from common.tests.utils import create_user_token from common.tests.utils import create_user_token
from extensions.models import Extension, Version from extensions.models import Extension, Version
@ -176,6 +177,39 @@ class FiltersTest(APITestCase):
).json() ).json()
self.assertEqual(len(json['data']), 1) self.assertEqual(len(json['data']), 1)
def test_platform_filter_same_version(self):
version = create_approved_version(
metadata__id='filter_test',
metadata__platforms=['linux-x64'],
metadata__version='1.0.0',
)
version = version.add_file(
FileFactory(
metadata__id='filter_test',
metadata__platforms=['windows-x64'],
metadata__version='1.0.0',
)
)
url = reverse('extensions:api')
json = self.client.get(
url + '?platform=linux-x64',
HTTP_ACCEPT='application/json',
).json()
self.assertEqual(len(json['data']), 1)
json = self.client.get(
url + '?platform=windows-x64',
HTTP_ACCEPT='application/json',
).json()
self.assertEqual(len(json['data']), 1)
json = self.client.get(
url,
HTTP_ACCEPT='application/json',
).json()
self.assertEqual(len(json['data']), 2)
def test_blender_version_filter_latest_not_max_version(self): def test_blender_version_filter_latest_not_max_version(self):
version = create_approved_version(metadata__blender_version_min='4.0.1') version = create_approved_version(metadata__blender_version_min='4.0.1')
date_created = version.date_created date_created = version.date_created

View File

@ -54,7 +54,7 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
except Platform.DoesNotExist: except Platform.DoesNotExist:
self.platform = self.UNKNOWN_PLATFORM self.platform = self.UNKNOWN_PLATFORM
def find_matching_file_and_version(self, instance): def find_matching_files_and_version(self, instance):
# avoid triggering additional db queries, reuse the prefetched queryset # avoid triggering additional db queries, reuse the prefetched queryset
versions = sorted( versions = sorted(
[v for v in instance.versions.all() if v.is_listed], [v for v in instance.versions.all() if v.is_listed],
@ -62,7 +62,7 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
reverse=True, reverse=True,
) )
if not versions: if not versions:
return (None, None) return ([], None)
for v in versions: for v in versions:
if self.blender_version and not is_in_version_range( if self.blender_version and not is_in_version_range(
@ -71,24 +71,26 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
v.blender_version_max, v.blender_version_max,
): ):
continue continue
if self.platform:
if file := v.get_file_for_platform(self.platform): if file := v.get_file_for_platform(self.platform):
return (file, v) return ([file], v)
else:
return (v.files.all(), v)
return (None, None) return ([], None)
def to_representation(self, instance): def to_representation(self, instance):
# TODO? return all matching files (when no self.platform is passed)? matching_files, matching_version = self.find_matching_files_and_version(instance)
matching_file, matching_version = self.find_matching_file_and_version(instance) result = []
if not matching_file: for file in matching_files:
return None
data = { data = {
'id': instance.extension_id, 'id': instance.extension_id,
'schema_version': matching_version.schema_version, 'schema_version': matching_version.schema_version,
'name': instance.name, 'name': instance.name,
'version': matching_version.version, 'version': matching_version.version,
'tagline': matching_version.tagline, 'tagline': matching_version.tagline,
'archive_hash': matching_file.original_hash, 'archive_hash': file.original_hash,
'archive_size': matching_file.size_bytes, 'archive_size': file.size_bytes,
'archive_url': self.request.build_absolute_uri( 'archive_url': self.request.build_absolute_uri(
matching_version.get_download_url( matching_version.get_download_url(
platform=self.platform, platform=self.platform,
@ -100,15 +102,17 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
'blender_version_max': matching_version.blender_version_max, 'blender_version_max': matching_version.blender_version_max,
'website': self.request.build_absolute_uri(instance.get_absolute_url()), 'website': self.request.build_absolute_uri(instance.get_absolute_url()),
# avoid triggering additional db queries, reuse the prefetched queryset # avoid triggering additional db queries, reuse the prefetched queryset
'maintainer': instance.team and instance.team.name or str(instance.authors.all()[0]), 'maintainer': (
instance.team and instance.team.name or str(instance.authors.all()[0])
),
'license': [license_iter.slug for license_iter in matching_version.licenses.all()], 'license': [license_iter.slug for license_iter in matching_version.licenses.all()],
'permissions': matching_file.metadata.get('permissions'), 'permissions': file.metadata.get('permissions'),
# listing all platforms for matching_version, not matching_file (see TODO? above) 'platforms': file.get_platforms(),
'platforms': [platform.slug for platform in matching_version.platforms.all()],
# TODO: handle copyright # TODO: handle copyright
'tags': [str(tag) for tag in matching_version.tags.all()], 'tags': [str(tag) for tag in matching_version.tags.all()],
} }
return clean_json_dictionary_from_optional_fields(data) result.append(clean_json_dictionary_from_optional_fields(data))
return result
class ExtensionsAPIView(APIView): class ExtensionsAPIView(APIView):
@ -153,7 +157,10 @@ class ExtensionsAPIView(APIView):
request=request, request=request,
many=True, many=True,
) )
data = [e for e in serializer.data if e is not None] data = []
for entry in serializer.data:
data.extend(entry)
return Response( return Response(
{ {
'blocklist': Extension.objects.blocklisted.values_list('extension_id', flat=True), 'blocklist': Extension.objects.blocklisted.values_list('extension_id', flat=True),