From cc9300ae1e6d1ed7de45242f27529081d4b39044 Mon Sep 17 00:00:00 2001 From: Jeffrey Meadows Date: Wed, 1 Apr 2015 13:54:50 -0700 Subject: [PATCH 1/2] Improve search endpoint. Include support for type and content_types fields. --- boxsdk/object/search.py | 36 ++++++++++++++---- test/unit/object/test_search.py | 66 +++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 24 deletions(-) diff --git a/boxsdk/object/search.py b/boxsdk/object/search.py index 3366d2905..143c9f509 100644 --- a/boxsdk/object/search.py +++ b/boxsdk/object/search.py @@ -155,7 +155,18 @@ def make_single_metadata_filter(template_key, scope): """ return MetadataSearchFilter(template_key, scope) - def search(self, query, limit, offset, ancestor_folders=None, file_extensions=None, metadata_filters=None): + def search( + self, + query, + limit=100, + offset=0, + ancestor_folders=None, + file_extensions=None, + metadata_filters=None, + result_type=None, + content_types=None, + **kwargs + ): """ Search Box for items matching the given query. @@ -174,7 +185,7 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No :param ancestor_folders: Folder ids to limit the search to. :type ancestor_folders: - `iterable` of :class:`Folder` + `Iterable` of :class:`Folder` :param file_extensions: File extensions to limit the search to. :type file_extensions: @@ -183,6 +194,14 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No Filters used for metadata search :type metadata_filters: :class:`MetadataSearchFilters` + :param result_type: + Which type of result you want. Can be file or folder. + :type result_type: + `unicode` + :param content_types: + Which content types to search. Valid types include name, description, file_content, comments, and tags. + :type content_types: + `Iterable` of `unicode` :return: A list of items that match the search query. :rtype: @@ -199,13 +218,14 @@ def search(self, query, limit, offset, ancestor_folders=None, file_extensions=No 'ancestor_folder_ids': ','.join([folder.object_id for folder in ancestor_folders]) }) if file_extensions: - params.update({ - 'file_extensions': ','.join(file_extensions) - }) + params.update({'file_extensions': ','.join(file_extensions)}) if metadata_filters: - params.update({ - 'mdfilters': json.dumps(metadata_filters.as_list()) - }) + params.update({'mdfilters': json.dumps(metadata_filters.as_list())}) + if content_types: + params.update({'content_types': ','.join(content_types)}) + if result_type: + params.update({'type': result_type}) + params.update(kwargs) box_response = self._session.get(url, params=params) response = box_response.json() return [Translator().translate(item['type'])(self._session, item['id'], item) for item in response['entries']] diff --git a/test/unit/object/test_search.py b/test/unit/object/test_search.py index 9b6873885..034510811 100644 --- a/test/unit/object/test_search.py +++ b/test/unit/object/test_search.py @@ -12,14 +12,24 @@ def search_query(): return 'myquery' -@pytest.fixture -def search_limit(): - return 20 +@pytest.fixture(params=(1, 20, 100)) +def search_limit(request): + return request.param -@pytest.fixture -def search_offset(): - return 0 +@pytest.fixture(params=(0, 10)) +def search_offset(request): + return request.param + + +@pytest.fixture(params=(None, 'file', 'folder')) +def search_result_type(request): + return request.param + + +@pytest.fixture(params=(None, ('name',), ('name', 'description'))) +def search_content_types(request): + return request.param @pytest.fixture @@ -81,6 +91,8 @@ def compare_params(self, other): if json.loads(self['mdfilters']) != json.loads(other['mdfilters']): return False # For other keys, just ensure that they are equal + elif key in ('type', 'content_types'): + return self[key] is None or self[key] == other[key] else: if self[key] != other[key]: return False @@ -96,20 +108,31 @@ def test_search_with_value_based_filters( search_offset, search_value_based_filters, search_response, - search_entries + search_entries, + search_result_type, + search_content_types, ): # pylint:disable=redefined-outer-name mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response) - response = test_search.search(search_query, limit=search_limit, offset=search_offset, metadata_filters=search_value_based_filters) + response = test_search.search( + search_query, + limit=search_limit, + offset=search_offset, + metadata_filters=search_value_based_filters, + result_type=search_result_type, + content_types=search_content_types, + ) assert response == [File(mock_box_session, search_entry['id'], search_entry) for search_entry in search_entries] mock_box_session.get.assert_called_once_with( test_search.get_url(), params=Matcher(compare_params, { - 'query': 'myquery', - 'limit': 20, + 'query': search_query, + 'limit': search_limit, 'mdfilters': json.dumps(search_value_based_filters.as_list()), - 'offset': 0 + 'offset': search_offset, + 'type': search_result_type, + 'content_types': ','.join(search_content_types) if search_content_types else search_content_types, }) ) @@ -123,20 +146,31 @@ def test_search_with_range_filters( search_offset, search_range_filters, search_response, - search_entries + search_entries, + search_result_type, + search_content_types, ): # pylint:disable=redefined-outer-name mock_box_session.get.return_value, _ = make_mock_box_request(response=search_response) - response = test_search.search(search_query, limit=search_limit, offset=search_offset, metadata_filters=search_range_filters) + response = test_search.search( + search_query, + limit=search_limit, + offset=search_offset, + metadata_filters=search_range_filters, + result_type=search_result_type, + content_types=search_content_types, + ) assert response == [File(mock_box_session, search_entry['id'], search_entry) for search_entry in search_entries] mock_box_session.get.assert_called_once_with( test_search.get_url(), params=Matcher(compare_params, { - 'query': 'myquery', - 'limit': 20, + 'query': search_query, + 'limit': search_limit, 'mdfilters': json.dumps(search_range_filters.as_list()), - 'offset': 0 + 'offset': search_offset, + 'type': search_result_type, + 'content_types': ','.join(search_content_types) if search_content_types else search_content_types, }) ) From 4285510a35bd8c290c21d3be94d9bf09def245f0 Mon Sep 17 00:00:00 2001 From: Jeffrey Meadows Date: Wed, 1 Apr 2015 14:58:27 -0700 Subject: [PATCH 2/2] Bump version to 1.1.4. --- .travis.yml | 1 + HISTORY.rst | 6 ++++++ setup.py | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c1151ee92..84a1feb13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python +python: 2.7 env: - TOX_ENV=py26 - TOX_ENV=py27 diff --git a/HISTORY.rst b/HISTORY.rst index 0024a4f51..95ab9070d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,12 @@ Release History --------------- +1.1.4 (2015-04-01) +++++++++++++++++++ + +- Added support to the search endpoint for metadata filters. +- Added support to the search endpoint for filtering based on result type and content types. + 1.1.3 (2015-03-26) ++++++++++++++++++ diff --git a/setup.py b/setup.py index 783cda147..c784287e3 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ def main(): install_requires.append('ordereddict>=1.1') setup( name='boxsdk', - version='1.1.3', + version='1.1.4', description='Official Box Python SDK', long_description=open(join(base_dir, 'README.rst')).read(), author='Box',