Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
language: python
python: 2.7
env:
- TOX_ENV=py26
- TOX_ENV=py27
Expand Down
6 changes: 6 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
++++++++++++++++++

Expand Down
36 changes: 28 additions & 8 deletions boxsdk/object/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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']]
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
66 changes: 50 additions & 16 deletions test/unit/object/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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,
})
)

Expand All @@ -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,
})
)

Expand Down