Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metadata flag for asset listing #1216

Merged
merged 4 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions dandiapi/api/tests/test_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
[
Expand Down
18 changes: 11 additions & 7 deletions dandiapi/api/views/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,14 +511,18 @@ 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')
# Don't include metadata field if not asked for
queryset: QuerySet[Asset] = self.filter_queryset(self.get_queryset().select_related('blob'))
jjnesbitt marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -527,13 +531,13 @@ def list(self, request, *args, **kwargs):
glob_pattern = re.escape(glob_pattern)
queryset = queryset.filter(path__iregex=glob_pattern.replace('\\*', '.*'))

# Paginate if necessary
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
queryset = page
jjnesbitt marked this conversation as resolved.
Show resolved Hide resolved

serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
serializer = self.get_serializer(queryset, many=True, metadata=include_metadata)
return self.get_paginated_response(serializer.data)

@swagger_auto_schema(
manual_parameters=[PATH_PREFIX_PARAM],
Expand Down
10 changes: 10 additions & 0 deletions dandiapi/api/views/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -258,3 +267,4 @@ class AssetPathsQueryParameterSerializer(serializers.Serializer):

class AssetListSerializer(serializers.Serializer):
glob = serializers.CharField(required=False)
metadata = serializers.BooleanField(required=False, default=False)