Skip to content

Commit

Permalink
Allow deleting variant, variantcall and variant experiment objects
Browse files Browse the repository at this point in the history
  • Loading branch information
gregorjerse committed Jan 21, 2025
1 parent 13a5d46 commit 30504fd
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 13 deletions.
2 changes: 2 additions & 0 deletions docs/CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Added
endpoint
- Allow staff users to create new ``VariantExperiment`` objects through the API
endpoint
- Allow staff users to delet ``Variant`` and ``VariantExperiment`` via API
- Allow users with permissions to delete ``VariantCall`` objects via the API

Changed
-------
Expand Down
153 changes: 143 additions & 10 deletions resolwe_bio/variants/tests/test_variant.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,11 +562,13 @@ def test_add_variant_annotations(self):

class VariantTest(PrepareDataMixin, TestCase):
def setUp(self) -> None:
self.view = VariantViewSet.as_view({"get": "list", "post": "create"})
self.view = VariantViewSet.as_view(
{"get": "list", "post": "create", "delete": "destroy"}
)
return super().setUp()

def test_create(self):
"""Test the Variant creation.
def test_create_destroy(self):
"""Test the Variant create and destroy.
Only users with staff status are allowed to create Variant objects.
"""
Expand Down Expand Up @@ -606,7 +608,45 @@ def test_create(self):
response = self.view(request)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# Check that the created object exists.
Variant.objects.get(**response.data)
created = Variant.objects.get(**response.data)

# Delete as anonymous user.
request = APIRequestFactory().delete(
"/variant", {"pk": created.pk}, format="json"
)
response = self.view(request, pk=created.pk)
self.assertContains(
response,
"Authentication credentials were not provided.",
status_code=status.HTTP_403_FORBIDDEN,
)

# Delete as non-staff user.
self.contributor.is_staff = False
self.contributor.save(update_fields=["is_staff"])
request = APIRequestFactory().delete(
"/variant", {"pk": created.pk}, format="json"
)
force_authenticate(request, self.contributor)
response = self.view(request, pk=created.pk)
self.assertContains(
response,
"You do not have permission to perform this action.",
status_code=status.HTTP_403_FORBIDDEN,
)

# Delete as staff user.
self.contributor.is_staff = True
self.contributor.save(update_fields=["is_staff"])
request = APIRequestFactory().delete(
"/variant", {"pk": created.pk}, format="json"
)
force_authenticate(request, self.contributor)
response = self.view(request, pk=created.pk)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
with self.assertRaises(Variant.DoesNotExist):
created.refresh_from_db()

self.contributor.is_staff = False
self.contributor.save(update_fields=["is_staff"])

Expand Down Expand Up @@ -1177,10 +1217,12 @@ def test_ordering(self):

class VariantCallTest(PrepareDataMixin, TestCase):
def setUp(self) -> None:
self.view = VariantCallViewSet.as_view({"get": "list", "post": "create"})
self.view = VariantCallViewSet.as_view(
{"get": "list", "post": "create", "delete": "destroy"}
)
return super().setUp()

def test_create(self):
def test_create_destroy(self):
"""Test the VariantCall creation."""
variant_call_data = {
"sample": self.sample.pk,
Expand Down Expand Up @@ -1273,6 +1315,57 @@ def test_create(self):
},
)

# Delete as anonymous user.
request = APIRequestFactory().delete(
"/variantcall", {"pk": created.pk}, format="json"
)
response = self.view(request, pk=created.pk)
self.assertContains(
response,
"Authentication credentials were not provided.",
status_code=status.HTTP_403_FORBIDDEN,
)

# Delete without required permission to data and sample.
self.sample.set_permission(Permission.VIEW, user)
data.set_permission(Permission.VIEW, user)
request = APIRequestFactory().delete(
"/variantcall", {"pk": created.pk}, format="json"
)
force_authenticate(request, user)
response = self.view(request, pk=created.pk)
self.assertContains(
response,
"You do not have permission to perform this action.",
status_code=status.HTTP_403_FORBIDDEN,
)

# Delete without required permission to data.
self.sample.set_permission(Permission.EDIT, user)
data.set_permission(Permission.VIEW, user)
request = APIRequestFactory().delete(
"/variantcall", {"pk": created.pk}, format="json"
)
force_authenticate(request, user)
response = self.view(request, pk=created.pk)
self.assertContains(
response,
"You do not have permission to perform this action.",
status_code=status.HTTP_403_FORBIDDEN,
)

# Delete with required permission to data.
self.sample.set_permission(Permission.EDIT, user)
data.set_permission(Permission.EDIT, user)
request = APIRequestFactory().delete(
"/variantcall", {"pk": created.pk}, format="json"
)
force_authenticate(request, user)
response = self.view(request, pk=created.pk)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
with self.assertRaises(VariantCall.DoesNotExist):
created.refresh_from_db()

def test_filter(self):
# No filter no permission for public.
request = APIRequestFactory().get("/variantcall")
Expand Down Expand Up @@ -1497,11 +1590,13 @@ def test_ordering(self):

class VariantExperimentTest(PrepareDataMixin, TestCase):
def setUp(self) -> None:
self.view = VariantExperimentViewSet.as_view({"get": "list", "post": "create"})
self.view = VariantExperimentViewSet.as_view(
{"get": "list", "post": "create", "delete": "destroy"}
)
return super().setUp()

def test_create(self):
"""Test the VariantExperiment creation.
def test_create_destroy(self):
"""Test the VariantExperiment creation and deletion.
Only users with staff status are allowed to create VariantExperiment objects.
"""
Expand Down Expand Up @@ -1543,7 +1638,45 @@ def test_create(self):
response = self.view(request)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# Check that the created object exists.
VariantExperiment.objects.get(**response.data)
created = VariantExperiment.objects.get(**response.data)
self.contributor.is_staff = False
self.contributor.save(update_fields=["is_staff"])

# Delete as anonymous user.
request = APIRequestFactory().delete(
"/variantexperiment", {"pk": created.pk}, format="json"
)
response = self.view(request, pk=created.pk)
self.assertContains(
response,
"Authentication credentials were not provided.",
status_code=status.HTTP_403_FORBIDDEN,
)

# Delete as non-staff user.
request = APIRequestFactory().delete(
"/variantexperiment", {"pk": created.pk}, format="json"
)
force_authenticate(request, self.contributor)
response = self.view(request, pk=created.pk)
self.assertContains(
response,
"You do not have permission to perform this action.",
status_code=status.HTTP_403_FORBIDDEN,
)

# Delete as staff user.
self.contributor.is_staff = True
self.contributor.save(update_fields=["is_staff"])
request = APIRequestFactory().delete(
"/variantexperiment", {"pk": created.pk}, format="json"
)
force_authenticate(request, self.contributor)
response = self.view(request, pk=created.pk)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
with self.assertRaises(VariantExperiment.DoesNotExist):
created.refresh_from_db()

self.contributor.is_staff = False
self.contributor.save(update_fields=["is_staff"])

Expand Down
25 changes: 22 additions & 3 deletions resolwe_bio/variants/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@


class VariantViewSet(
mixins.ListModelMixin, ResolweCreateModelMixin, viewsets.GenericViewSet
mixins.ListModelMixin,
ResolweCreateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
"""Variant endpoint."""

Expand Down Expand Up @@ -65,7 +68,10 @@ class VariantAnnotationViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):


class VariantCallViewSet(
mixins.ListModelMixin, ResolweCreateModelMixin, viewsets.GenericViewSet
mixins.ListModelMixin,
ResolweCreateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
"""VariantCall endpoint.
Expand Down Expand Up @@ -94,9 +100,22 @@ def perform_create(self, serializer: serializers.BaseSerializer) -> None:

return super().perform_create(serializer)

def perform_destroy(self, instance: VariantCall) -> None:
"""Check if user has EDIT permission on the sample."""
if not instance.sample.has_permission(Permission.EDIT, self.request.user):
raise exceptions.PermissionDenied()
if data := instance.data:
if not data.has_permission(Permission.EDIT, self.request.user):
raise exceptions.PermissionDenied()

return super().perform_destroy(instance)


class VariantExperimentViewSet(
mixins.ListModelMixin, ResolweCreateModelMixin, viewsets.GenericViewSet
mixins.ListModelMixin,
ResolweCreateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
"""VariantExperiment endpoint."""

Expand Down

0 comments on commit 30504fd

Please sign in to comment.