diff --git a/dandiapi/api/tests/test_asset.py b/dandiapi/api/tests/test_asset.py index c59cf0f74..543f0a04e 100644 --- a/dandiapi/api/tests/test_asset.py +++ b/dandiapi/api/tests/test_asset.py @@ -329,6 +329,28 @@ def test_asset_rest_list(api_client, version, asset, asset_factory): } +@pytest.mark.django_db +def test_asset_rest_list_include_metadata(api_client, version, asset, asset_factory): + version.assets.add(asset) + + # Create an extra asset so that there are multiple assets to filter down + asset_factory() + + # Assert false has no effect + r = api_client.get( + f'/api/dandisets/{version.dandiset.identifier}/versions/{version.version}/assets/', + {'metadata': False}, + ) + assert 'metadata' not in r.json()['results'][0] + + # Test positive case + r = api_client.get( + f'/api/dandisets/{version.dandiset.identifier}/versions/{version.version}/assets/', + {'metadata': True}, + ) + assert r.json()['results'][0]['metadata'] == asset.metadata + + @pytest.mark.parametrize( 'path,result_indices', [ diff --git a/dandiapi/api/views/asset.py b/dandiapi/api/views/asset.py index 78d7c165f..a7ab5fe03 100644 --- a/dandiapi/api/views/asset.py +++ b/dandiapi/api/views/asset.py @@ -533,14 +533,22 @@ def destroy(self, request, versions__dandiset__pk, versions__version, **kwargs): return Response(None, status=status.HTTP_204_NO_CONTENT) - @swagger_auto_schema(query_serializer=AssetListSerializer) + @swagger_auto_schema(query_serializer=AssetListSerializer, responses={200: AssetSerializer()}) def list(self, request, *args, **kwargs): serializer = AssetListSerializer(data=request.query_params) serializer.is_valid(raise_exception=True) - queryset = self.filter_queryset(self.get_queryset()) - glob_pattern: str | None = serializer.validated_data.get('glob') + # Fetch initial queryset + queryset: QuerySet[Asset] = self.filter_queryset( + self.get_queryset().select_related('blob', 'embargoed_blob', 'zarr') + ) + # Don't include metadata field if not asked for + include_metadata = serializer.validated_data['metadata'] + if not include_metadata: + queryset = queryset.defer('metadata') + + glob_pattern: str | None = serializer.validated_data.get('glob') if glob_pattern is not None: # Escape special characters in the glob pattern. This is a security precaution taken # since we are using postgres' regex search. A malicious user who knows this could @@ -549,12 +557,13 @@ def list(self, request, *args, **kwargs): glob_pattern = re.escape(glob_pattern) queryset = queryset.filter(path__iregex=glob_pattern.replace('\\*', '.*')) + # Paginate and return page = self.paginate_queryset(queryset) if page is not None: - serializer = self.get_serializer(page, many=True) + serializer = self.get_serializer(page, many=True, metadata=include_metadata) return self.get_paginated_response(serializer.data) - serializer = self.get_serializer(queryset, many=True) + serializer = self.get_serializer(queryset, many=True, metadata=include_metadata) return Response(serializer.data) @swagger_auto_schema( diff --git a/dandiapi/api/views/serializers.py b/dandiapi/api/views/serializers.py index fde05a2ab..1a2c99a16 100644 --- a/dandiapi/api/views/serializers.py +++ b/dandiapi/api/views/serializers.py @@ -226,12 +226,21 @@ class Meta: 'size', 'created', 'modified', + 'metadata', ] read_only_fields = ['created'] blob = EmbargoedSlugRelatedField(slug_field='blob_id', read_only=True) zarr = serializers.SlugRelatedField(slug_field='zarr_id', read_only=True) + def __init__(self, *args, metadata=True, **kwargs): + # Instantiate the superclass normally + super().__init__(*args, **kwargs) + + # Don't include metadata unless specified + if not metadata: + self.fields.pop('metadata') + class AssetDetailSerializer(AssetSerializer): class Meta(AssetSerializer.Meta): @@ -258,3 +267,4 @@ class AssetPathsQueryParameterSerializer(serializers.Serializer): class AssetListSerializer(serializers.Serializer): glob = serializers.CharField(required=False) + metadata = serializers.BooleanField(required=False, default=False)