From 518ee265f4817b457ab669264311e91d29184684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20HUBSCHER?= Date: Mon, 18 Jul 2016 09:33:46 +0200 Subject: [PATCH 1/2] Merge pull request #718 from Kinto/fix-unknown-fields-resources Allow filtering and sorting by any attribute on buckets, collections and groups list endpoints --- CHANGELOG.rst | 5 ++++- kinto/core/storage/memory.py | 9 ++++++++- kinto/tests/core/test_storage.py | 13 +++++++++++++ kinto/tests/test_views_buckets.py | 10 ++++++++++ kinto/tests/test_views_collections.py | 11 +++++++++++ kinto/tests/test_views_groups.py | 11 +++++++++++ kinto/views/buckets.py | 4 ++++ kinto/views/collections.py | 4 ++++ kinto/views/groups.py | 4 ++++ 9 files changed, 69 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ae8e07abe..7b6477a69 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,7 +6,10 @@ This document describes changes between each past release. 3.2.3 (unreleased) ================== -- Nothing changed yet. +**Bug fixes** + +- Allow filtering and sorting by any attribute on buckets, collections and groups list endpoints +- Fix crash in memory backend with Python3 when filtering on unknown field 3.2.2 (2016-07-13) diff --git a/kinto/core/storage/memory.py b/kinto/core/storage/memory.py index 89f01fbdf..e2c9c13f2 100644 --- a/kinto/core/storage/memory.py +++ b/kinto/core/storage/memory.py @@ -100,7 +100,6 @@ def apply_filters(self, records, filters): COMPARISON.IN: operator.contains, COMPARISON.EXCLUDE: lambda x, y: not operator.contains(x, y), } - for record in records: matches = True for f in filters: @@ -109,6 +108,14 @@ def apply_filters(self, records, filters): if f.operator in (COMPARISON.IN, COMPARISON.EXCLUDE): right = left left = f.value + else: + # Python3 cannot compare None to other value. + if left is None: + if f.operator in (COMPARISON.GT, COMPARISON.MIN): + matches = False + continue + elif f.operator in (COMPARISON.LT, COMPARISON.MAX): + continue # matches = matches and True matches = matches and operators[f.operator](left, right) if matches: yield record diff --git a/kinto/tests/core/test_storage.py b/kinto/tests/core/test_storage.py index 664f33076..aced57fc1 100644 --- a/kinto/tests/core/test_storage.py +++ b/kinto/tests/core/test_storage.py @@ -418,6 +418,19 @@ def test_get_all_can_filter_with_strings(self): self.assertEqual(records[1]['name'], "Marie") self.assertEqual(len(records), 2) + def test_get_all_can_filter_with_none_values(self): + self.create_record({"name": "Alexis"}) + self.create_record({"title": "haha"}) + self.create_record({"name": "Mathieu"}) + filters = [Filter("name", "Fanny", utils.COMPARISON.GT)] + records, _ = self.storage.get_all(filters=filters, **self.storage_kw) + self.assertEqual(len(records), 1) # None is not greater than "Fanny" + self.assertEqual(records[0]["name"], "Mathieu") + + filters = [Filter("name", "Fanny", utils.COMPARISON.LT)] + records, _ = self.storage.get_all(filters=filters, **self.storage_kw) + self.assertEqual(len(records), 2) # None is less than "Fanny" + def test_get_all_can_filter_with_list_of_values_on_id(self): record1 = self.create_record({'code': 'a'}) record2 = self.create_record({'code': 'b'}) diff --git a/kinto/tests/test_views_buckets.py b/kinto/tests/test_views_buckets.py index 182f23009..64763167d 100644 --- a/kinto/tests/test_views_buckets.py +++ b/kinto/tests/test_views_buckets.py @@ -95,6 +95,16 @@ def test_buckets_can_handle_arbitrary_attributes(self): self.assertIn('public_key', data) self.assertEqual(data['public_key'], public_key) + def test_buckets_can_be_filtered_by_arbitrary_attribute(self): + bucket = MINIMALIST_BUCKET.copy() + bucket['data'] = {'size': 3} + self.app.put_json('/buckets/beers', + bucket, + headers=self.headers) + resp = self.app.get('/buckets?min_size=2', headers=self.headers) + data = resp.json['data'] + self.assertEqual(len(data), 1) + class BucketCreationTest(BaseWebTest, unittest.TestCase): def test_buckets_can_be_created_with_post(self): diff --git a/kinto/tests/test_views_collections.py b/kinto/tests/test_views_collections.py index d57490f0a..753251afe 100644 --- a/kinto/tests/test_views_collections.py +++ b/kinto/tests/test_views_collections.py @@ -81,6 +81,17 @@ def test_collections_can_handle_arbitrary_attributes(self): self.assertIn('fingerprint', data) self.assertEqual(data['fingerprint'], fingerprint) + def test_collections_can_be_filtered_by_arbitrary_attribute(self): + collection = MINIMALIST_COLLECTION.copy() + collection['data'] = {'size': 3} + self.app.put_json('/buckets/beers/collections/moderator', + collection, + headers=self.headers) + resp = self.app.get('/buckets/beers/collections?min_size=2', + headers=self.headers) + data = resp.json['data'] + self.assertEqual(len(data), 1) + class CollectionDeletionTest(BaseWebTest, unittest.TestCase): diff --git a/kinto/tests/test_views_groups.py b/kinto/tests/test_views_groups.py index 26e775b3a..67165a72c 100644 --- a/kinto/tests/test_views_groups.py +++ b/kinto/tests/test_views_groups.py @@ -50,6 +50,17 @@ def test_groups_can_have_arbitrary_attributes(self): self.assertIn('mailinglist', data) self.assertEqual(data['mailinglist'], mailinglist) + def test_groups_can_be_filtered_by_arbitrary_attribute(self): + group = MINIMALIST_GROUP.copy() + group['data']['size'] = 3 + self.app.put_json('/buckets/beers/groups/moderator', + group, + headers=self.headers) + resp = self.app.get('/buckets/beers/groups?min_size=2', + headers=self.headers) + data = resp.json['data'] + self.assertEqual(len(data), 1) + def test_groups_should_reject_unaccepted_request_content_type(self): headers = self.headers.copy() headers['Content-Type'] = 'text/plain' diff --git a/kinto/views/buckets.py b/kinto/views/buckets.py index 0b38cd865..c75d04380 100644 --- a/kinto/views/buckets.py +++ b/kinto/views/buckets.py @@ -26,6 +26,10 @@ def get_parent_id(self, request): # Buckets are not isolated by user, unlike Kinto-Core resources. return '' + def is_known_field(self, field_name): + """Without schema, any field is considered as known.""" + return True + @subscriber(ResourceChanged, for_resources=('bucket',), diff --git a/kinto/views/collections.py b/kinto/views/collections.py index 7e2b1914f..a06f24f14 100644 --- a/kinto/views/collections.py +++ b/kinto/views/collections.py @@ -51,6 +51,10 @@ def get_parent_id(self, request): parent_id = '/buckets/%s' % bucket_id return parent_id + def is_known_field(self, field_name): + """Without schema, any field is considered as known.""" + return True + @subscriber(ResourceChanged, for_resources=('collection',), diff --git a/kinto/views/groups.py b/kinto/views/groups.py index 4ed5fc29d..6dddb109a 100644 --- a/kinto/views/groups.py +++ b/kinto/views/groups.py @@ -31,6 +31,10 @@ def get_parent_id(self, request): parent_id = '/buckets/%s' % bucket_id return parent_id + def is_known_field(self, field_name): + """Without schema, any field is considered as known.""" + return True + @subscriber(ResourceChanged, for_resources=('group',), From 4aa867909a64ae7c7d7691f12e0c3771dc19721c Mon Sep 17 00:00:00 2001 From: Mathieu Leplatre Date: Mon, 18 Jul 2016 09:58:11 +0200 Subject: [PATCH 2/2] Preparing release 3.2.3 --- CHANGELOG.rst | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7b6477a69..cce3b1b67 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,7 +3,7 @@ Changelog This document describes changes between each past release. -3.2.3 (unreleased) +3.2.3 (2016-07-18) ================== **Bug fixes** diff --git a/setup.py b/setup.py index a8bea27b6..1ef7e51a2 100644 --- a/setup.py +++ b/setup.py @@ -71,7 +71,7 @@ def read_file(filename): setup(name='kinto', - version='3.2.3.dev0', + version='3.2.3', description='Kinto Web Service - Store, Sync, Share, and Self-Host.', long_description=README + "\n\n" + CHANGELOG + "\n\n" + CONTRIBUTORS, license='Apache License (2.0)',