Skip to content

Commit f8ff255

Browse files
authored
[fix] Fixed handling of non-existing device in DeviceLocationView
1 parent 957c4a3 commit f8ff255

File tree

3 files changed

+40
-14
lines changed

3 files changed

+40
-14
lines changed

openwisp_controller/geo/api/views.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def create_location(self, device):
105105

106106
class DeviceLocationView(
107107
RelatedDeviceProtectedAPIMixin,
108+
FilterByParentManaged,
108109
generics.RetrieveUpdateDestroyAPIView,
109110
):
110111
serializer_class = DeviceLocationSerializer
@@ -124,9 +125,7 @@ def get_queryset(self):
124125
return qs.none()
125126

126127
def get_parent_queryset(self):
127-
return Device.objects.filter(
128-
pk=self.kwargs['pk'],
129-
)
128+
return Device.objects.filter(pk=self.kwargs['pk'])
130129

131130
def get_serializer_context(self):
132131
context = super().get_serializer_context()

openwisp_controller/geo/tests/test_api.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,33 @@ def test_create_devicelocation_outdoor_location_with_floorplan(self):
655655
)
656656
self.assertEqual(self.object_location_model.objects.count(), 0)
657657

658+
def test_endpoints_for_non_existent_device(self):
659+
device_id = uuid.uuid4()
660+
floorplan = self._create_floorplan()
661+
location = floorplan.location
662+
url = reverse('geo_api:device_location', args=[device_id])
663+
664+
with self.subTest('Retrieve operation'):
665+
response = self.client.get(
666+
url,
667+
)
668+
self.assertEqual(response.status_code, 404)
669+
670+
with self.subTest('Update operation'):
671+
response = self.client.put(
672+
url,
673+
data={
674+
'location': str(location.id),
675+
'floorplan': str(floorplan.id),
676+
},
677+
content_type='application/json',
678+
)
679+
self.assertEqual(response.status_code, 404)
680+
681+
with self.subTest('Delete operation'):
682+
response = self.client.delete(url)
683+
self.assertEqual(response.status_code, 404)
684+
658685
def test_create_devicelocation_using_non_existing_related_ids(self):
659686
device = self._create_object()
660687
floorplan = self._create_floorplan()
@@ -695,7 +722,7 @@ def test_create_devicelocation_using_related_ids(self):
695722
floorplan = self._create_floorplan()
696723
location = floorplan.location
697724
url = reverse('geo_api:device_location', args=[device.id])
698-
with self.assertNumQueries(15):
725+
with self.assertNumQueries(18):
699726
response = self.client.put(
700727
url,
701728
data={
@@ -733,7 +760,7 @@ def test_create_devicelocation_location_floorplan(self):
733760
'floorplan.image': self._get_simpleuploadedfile(),
734761
'indoor': ['12.342,23.541'],
735762
}
736-
with self.assertNumQueries(29):
763+
with self.assertNumQueries(32):
737764
response = self.client.put(
738765
url, encode_multipart(BOUNDARY, data), content_type=MULTIPART_CONTENT
739766
)
@@ -800,7 +827,7 @@ def test_create_devicelocation_only_location(self):
800827
'type': 'indoor',
801828
}
802829
}
803-
with self.assertNumQueries(18):
830+
with self.assertNumQueries(21):
804831
response = self.client.put(url, data=data, content_type='application/json')
805832
self.assertEqual(response.status_code, 201)
806833
self.assertEqual(self.location_model.objects.count(), 1)
@@ -817,7 +844,7 @@ def test_create_devicelocation_only_floorplan(self):
817844
'floorplan.floor': 1,
818845
'floorplan.image': self._get_simpleuploadedfile(),
819846
}
820-
with self.assertNumQueries(5):
847+
with self.assertNumQueries(8):
821848
response = self.client.put(
822849
url, encode_multipart(BOUNDARY, data), content_type=MULTIPART_CONTENT
823850
)
@@ -840,7 +867,7 @@ def test_create_devicelocation_existing_location_new_floorplan(self):
840867
'floorplan.image': self._get_simpleuploadedfile(),
841868
'indoor': ['12.342,23.541'],
842869
}
843-
with self.assertNumQueries(23):
870+
with self.assertNumQueries(26):
844871
response = self.client.put(
845872
url, encode_multipart(BOUNDARY, data), content_type=MULTIPART_CONTENT
846873
)
@@ -863,7 +890,7 @@ def test_update_devicelocation_change_location_outdoor_to_indoor(self):
863890
}
864891
self.assertEqual(device_location.location.type, 'outdoor')
865892
self.assertEqual(device_location.floorplan, None)
866-
with self.assertNumQueries(21):
893+
with self.assertNumQueries(23):
867894
response = self.client.put(
868895
path, encode_multipart(BOUNDARY, data), content_type=MULTIPART_CONTENT
869896
)
@@ -882,7 +909,7 @@ def test_update_devicelocation_patch_indoor(self):
882909
'indoor': '0,0',
883910
}
884911
self.assertEqual(device_location.indoor, '-140.38620,40.369227')
885-
with self.assertNumQueries(10):
912+
with self.assertNumQueries(12):
886913
response = self.client.patch(path, data, content_type='application/json')
887914
self.assertEqual(response.status_code, 200)
888915
device_location.refresh_from_db()
@@ -899,7 +926,7 @@ def test_update_devicelocation_floorplan_related_id(self):
899926
data = {
900927
'floorplan': str(floor2.id),
901928
}
902-
with self.assertNumQueries(12):
929+
with self.assertNumQueries(14):
903930
response = self.client.patch(path, data, content_type='application/json')
904931
self.assertEqual(response.status_code, 200)
905932
device_location.refresh_from_db()
@@ -913,7 +940,7 @@ def test_update_devicelocation_location_related_id(self):
913940
data = {
914941
'location': str(location2.id),
915942
}
916-
with self.assertNumQueries(9):
943+
with self.assertNumQueries(11):
917944
response = self.client.patch(path, data, content_type='application/json')
918945
self.assertEqual(response.status_code, 200)
919946
device_location.refresh_from_db()

openwisp_controller/mixins.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ def _has_permissions(self, request, view, perm, obj=None):
1212
if obj:
1313
device = getattr(obj, self._device_field)
1414
else:
15-
device = view.get_parent_queryset()[0]
16-
return perm and not device.is_deactivated()
15+
device = view.get_parent_queryset().first()
16+
return perm and device and not device.is_deactivated()
1717

1818
def has_permission(self, request, view):
1919
perm = super().has_permission(request, view)

0 commit comments

Comments
 (0)