Skip to content

Commit

Permalink
Merge pull request #63 from Souleymane-T/feature-add-filter-against-uid
Browse files Browse the repository at this point in the history
Add filtering against uid
  • Loading branch information
lcognat committed Jun 3, 2021
2 parents 08b0cdb + b15f6eb commit f089483
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 13 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Added

- nothing added
- Objects can be filtered with the uid

### Changed

Expand Down
42 changes: 33 additions & 9 deletions concrete_datastore/api/v1/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
import uuid
import functools

from django.db.models import Q, CharField, TextField, ForeignKey, BooleanField
from django.db.models import (
Q,
CharField,
TextField,
ForeignKey,
BooleanField,
UUIDField,
)
from django.contrib.gis.db.models import PointField
from django.contrib.gis.measure import D
from django.db.models.fields.related import ManyToManyField
Expand All @@ -22,6 +29,15 @@
)


def ensure_uuid_valid(value, version=None):
try:
uuid.UUID(value, version=version)
except ValueError:
return False
else:
return True


def convert_type(string, field_type, close_period=True):
if string == '':
raise ValidationError(
Expand Down Expand Up @@ -203,8 +219,20 @@ def filter_queryset(self, request, queryset, view):
continue
if param.replace('__in', '') not in filterset_fields:
continue

values = query_params.get(param).split(',')
if isinstance(
queryset.model._meta.get_field(param.replace('__in', '')),
(UUIDField, ForeignKey),
):
for value in values:
if not ensure_uuid_valid(value):
raise ValidationError(
{
"message": (
f"{param}: '{value}' is not a valid UUID"
)
}
)
if isinstance(
queryset.model._meta.get_field(param.replace('__in', '')),
BooleanField,
Expand Down Expand Up @@ -523,10 +551,8 @@ def filter_queryset(self, request, queryset, view):
value = query_params.get(param)
custom_filter = {cleaned_param: value}
#: "value" must be a valid UUID4, otherwise raise ValidationError
try:
#: raises ValueError if not UUID4
uuid.UUID(value, version=4)
except ValueError:
#: raises ValueError if not UUID4
if not ensure_uuid_valid(value, version=4):
raise ValidationError(
{"message": f'{param}: « {value} » is not a valid UUID'}
)
Expand Down Expand Up @@ -623,10 +649,8 @@ def filter_queryset(self, request, queryset, view):

#: "value" must be a valid UUID4, otherwise raise ValidationError
for value in values:
try:
if not ensure_uuid_valid(value, version=4):
#: raises ValueError if not UUID4
uuid.UUID(value, version=4)
except ValueError:
raise ValidationError(
{
"message": f'{param}: « {value} » is not a valid UUID'
Expand Down
5 changes: 3 additions & 2 deletions concrete_datastore/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1987,12 +1987,13 @@ class GenericAttributesViewsetClass:
list_display = meta_model.get_property('m_list_display') or []
ordering_fields = tuple(meta_model.get_property('m_list_display', []))
search_fields = tuple(meta_model.get_property('m_search_fields', []))
filterset_fields = model_filterset_fields
filterset_fields = model_filterset_fields + ('uid',)
distance_filter_field = meta_model.get_property(
'm_distance_filter_field'
)

export_fields = tuple(meta_model.get_property('m_export_fields', []))
fields = [f for f, _ in meta_model.get_fields()]
fields = [f for f, _ in meta_model.get_fields()] + ['uid']
serializer_class = make_serializer_class_fct(
meta_model=meta_model, nested=False, api_namespace=api_namespace
)
Expand Down
79 changes: 79 additions & 0 deletions tests/tests_api_v1_1/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,3 +695,82 @@ def test_one_result_with_non_char_field(self):
self.assertEqual(resp.data['total_objects_count'], 2)
names = {project['name'] for project in resp.data['results']}
self.assertEqual(names, {'Project1', 'Project2'})


@override_settings(DEBUG=True)
class FilterObjectByUid(APITestCase):
def setUp(self):
self.user = User.objects.create_user('user@netsach.org')
self.user.set_password('plop')
self.user.set_level('superuser')
self.user.save()
confirmation = UserConfirmation.objects.create(user=self.user)
confirmation.confirmed = True
confirmation.save()
url = '/api/v1.1/auth/login/'
resp = self.client.post(
url, {"email": "user@netsach.org", "password": "plop"}
)
self.token = resp.data['token']

self.project1 = Project.objects.create(
name='Project1', description='text of description1'
)
self.project2 = Project.objects.create(
name='Project2', description='text of description2'
)
self.project3 = Project.objects.create(
name='Project3', description='text of description3'
)
self.category1 = Category.objects.create(name='category_test')
self.skill_1 = Skill.objects.create(
name='skill_test 1', category=self.category1, score=5
)

def test_filter_against_uid_field(self):
resp = self.client.get(
'/api/v1.1/project/',
data={'uid__in': f'{self.project1.uid},{self.project3.uid}'},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertIn('results', resp.data)
self.assertIn('total_objects_count', resp.data)
self.assertEqual(resp.data['total_objects_count'], 2)
names = {project['name'] for project in resp.data['results']}
self.assertEqual(names, {'Project1', 'Project3'})

def test_filter_against_uid_with_wrong_uid(self):
resp = self.client.get(
'/api/v1.1/project/',
data={
'uid__in': f'{self.project1.uid},{self.project3.uid},notanuid'
},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
self.assertDictEqual(
resp.data, {"message": "uid__in: 'notanuid' is not a valid UUID"}
)

def test_filter_against_uid_with_no_values(self):
resp = self.client.get(
'/api/v1.1/project/',
data={'uid__in': ''},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
self.assertDictEqual(
resp.data, {"message": "uid__in: '' is not a valid UUID"}
)

def test_filter_against_foreign_key_uid_with_no_values(self):
resp = self.client.get(
'/api/v1.1/skill/',
data={'category__in': ''},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
self.assertDictEqual(
resp.data, {"message": "category__in: '' is not a valid UUID"}
)
4 changes: 3 additions & 1 deletion tests/tests_api_v1_1/tests_list/test_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ def test_sets_on_users(self):
.get('sets')
)

self.assertDictEqual(users_set, {})
self.assertDictEqual(
users_set, {'uid': [str(self.user.uid)]}
)

def test_sets_simple_list(self):

Expand Down

0 comments on commit f089483

Please sign in to comment.