diff --git a/HISTORY.rst b/HISTORY.rst index 5c9f3737a..2194c18cb 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,6 +6,11 @@ Release History Upcoming ++++++++ +1.4.1 (2016-02-11) +++++++++++++++++++ + +- Files now support getting a direct download url. + 1.4.0 (2016-01-05) ++++++++++++++++++ diff --git a/boxsdk/object/file.py b/boxsdk/object/file.py index 96f607915..2b2805348 100644 --- a/boxsdk/object/file.py +++ b/boxsdk/object/file.py @@ -237,3 +237,54 @@ def metadata(self, scope='global', template='properties'): :class:`Metadata` """ return Metadata(self._session, self, scope, template) + + def get_shared_link_download_url( + self, + access=None, + etag=None, + unshared_at=None, + allow_preview=None, + password=None, + ): + """ + Get a shared link download url for the file with the given access permissions. + This url is a direct download url for the file. + + :param access: + Determines who can access the shared link. May be open, company, or collaborators. If no access is + specified, the default access will be used. + :type access: + `unicode` or None + :param etag: + If specified, instruct the Box API to create the link only if the current version's etag matches. + :type etag: + `unicode` or None + :param unshared_at: + The date on which this link should be disabled. May only be set if the current user is not a free user + and has permission to set expiration dates. + :type unshared_at: + :class:`datetime.date` or None + :param allow_preview: + Whether or not the item being shared can be previewed when accessed via the shared link. + If this parameter is None, the default setting will be used. + :type allow_preview: + `bool` or None + :param password: + The password required to view this link. If no password is specified then no password will be set. + Please notice that this is a premium feature, which might not be available to your app. + :type password: + `unicode` or None + :returns: + The URL of the shared link that allows direct download. + :rtype: + `unicode` + :raises: :class:`BoxAPIException` if the specified etag doesn't match the latest version of the item. + """ + item = self.create_shared_link( + access=access, + etag=etag, + unshared_at=unshared_at, + allow_preview=allow_preview, + password=password, + ) + return item.shared_link['download_url'] diff --git a/boxsdk/object/item.py b/boxsdk/object/item.py index 38835463a..8487761e6 100644 --- a/boxsdk/object/item.py +++ b/boxsdk/object/item.py @@ -159,8 +159,17 @@ def move(self, parent_folder): } return self.update_info(data) - def get_shared_link(self, access=None, etag=None, unshared_at=None, allow_download=None, allow_preview=None, password=None): - """Get a shared link for the item with the given access permissions. + def create_shared_link( + self, + access=None, + etag=None, + unshared_at=None, + allow_download=None, + allow_preview=None, + password=None, + ): + """ + Create a shared link for the item with the given access permissions. :param access: Determines who can access the shared link. May be open, company, or collaborators. If no access is @@ -191,10 +200,11 @@ def get_shared_link(self, access=None, etag=None, unshared_at=None, allow_downlo Please notice that this is a premium feature, which might not be available to your app. :type password: `unicode` or None - :returns: - The URL of the shared link. + :return: + The updated object with s shared link. + Returns a new object of the same type, without modifying the original object passed as self. :rtype: - `unicode` + :class:`Item` :raises: :class:`BoxAPIException` if the specified etag doesn't match the latest version of the item. """ data = { @@ -216,7 +226,64 @@ def get_shared_link(self, access=None, etag=None, unshared_at=None, allow_downlo if password is not None: data['shared_link']['password'] = password - item = self.update_info(data, etag=etag) + return self.update_info(data, etag=etag) + + def get_shared_link( + self, + access=None, + etag=None, + unshared_at=None, + allow_download=None, + allow_preview=None, + password=None, + ): + """ + Get a shared link for the item with the given access permissions. + This url leads to a Box.com shared link page, where the item can be previewed, downloaded, etc. + + :param access: + Determines who can access the shared link. May be open, company, or collaborators. If no access is + specified, the default access will be used. + :type access: + `unicode` or None + :param etag: + If specified, instruct the Box API to create the link only if the current version's etag matches. + :type etag: + `unicode` or None + :param unshared_at: + The date on which this link should be disabled. May only be set if the current user is not a free user + and has permission to set expiration dates. + :type unshared_at: + :class:`datetime.date` or None + :param allow_download: + Whether or not the item being shared can be downloaded when accessed via the shared link. + If this parameter is None, the default setting will be used. + :type allow_download: + `bool` or None + :param allow_preview: + Whether or not the item being shared can be previewed when accessed via the shared link. + If this parameter is None, the default setting will be used. + :type allow_preview: + `bool` or None + :param password: + The password required to view this link. If no password is specified then no password will be set. + Please notice that this is a premium feature, which might not be available to your app. + :type password: + `unicode` or None + :returns: + The URL of the shared link. + :rtype: + `unicode` + :raises: :class:`BoxAPIException` if the specified etag doesn't match the latest version of the item. + """ + item = self.create_shared_link( + access=access, + etag=etag, + unshared_at=unshared_at, + allow_download=allow_download, + allow_preview=allow_preview, + password=password, + ) return item.shared_link['url'] def remove_shared_link(self, etag=None): diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000..c34b498b2 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 \ No newline at end of file diff --git a/setup.py b/setup.py index 23b01bc80..b3dc95085 100644 --- a/setup.py +++ b/setup.py @@ -61,7 +61,7 @@ def main(): install_requires.append('ordereddict>=1.1') setup( name='boxsdk', - version='1.4.0', + version='1.4.1', description='Official Box Python SDK', long_description=open(join(base_dir, 'README.rst')).read(), author='Box', diff --git a/test/unit/object/conftest.py b/test/unit/object/conftest.py index 0d625afd3..65e38639f 100644 --- a/test/unit/object/conftest.py +++ b/test/unit/object/conftest.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import unicode_literals +from datetime import date import os from mock import Mock import pytest @@ -162,3 +163,28 @@ def file_size(request): def mock_group(mock_box_session, mock_group_id): group = Group(mock_box_session, mock_group_id) return group + + +@pytest.fixture(params=(True, False, None)) +def shared_link_can_download(request): + return request.param + + +@pytest.fixture(params=(True, False, None)) +def shared_link_can_preview(request): + return request.param + + +@pytest.fixture(params=('open', None)) +def shared_link_access(request): + return request.param + + +@pytest.fixture(params=('hunter2', None)) +def shared_link_password(request): + return request.param + + +@pytest.fixture(params=(date(2015, 5, 5), None)) +def shared_link_unshared_at(request): + return request.param diff --git a/test/unit/object/test_file.py b/test/unit/object/test_file.py index ecbd4b29e..44a8ec4a9 100644 --- a/test/unit/object/test_file.py +++ b/test/unit/object/test_file.py @@ -234,3 +234,43 @@ def test_preflight_check( expect_json_response=False, data=expected_data, ) + + +def test_get_shared_link_download_url( + test_file, + mock_box_session, + shared_link_access, + shared_link_unshared_at, + shared_link_password, + shared_link_can_preview, + test_url, + etag, + if_match_header, +): + # pylint:disable=redefined-outer-name, protected-access + expected_url = test_file.get_url() + mock_box_session.put.return_value.json.return_value = {'shared_link': {'url': None, 'download_url': test_url}} + expected_data = {'shared_link': {}} + if shared_link_access is not None: + expected_data['shared_link']['access'] = shared_link_access + if shared_link_unshared_at is not None: + expected_data['shared_link']['unshared_at'] = shared_link_unshared_at.isoformat() + if shared_link_can_preview is not None: + expected_data['shared_link']['permissions'] = permissions = {} + permissions['can_preview'] = shared_link_can_preview + if shared_link_password is not None: + expected_data['shared_link']['password'] = shared_link_password + url = test_file.get_shared_link_download_url( + etag=etag, + access=shared_link_access, + unshared_at=shared_link_unshared_at, + password=shared_link_password, + allow_preview=shared_link_can_preview, + ) + mock_box_session.put.assert_called_once_with( + expected_url, + data=json.dumps(expected_data), + headers=if_match_header, + params=None, + ) + assert url == test_url diff --git a/test/unit/object/test_item.py b/test/unit/object/test_item.py index abc080fb1..6226cefe8 100644 --- a/test/unit/object/test_item.py +++ b/test/unit/object/test_item.py @@ -1,7 +1,6 @@ # coding: utf-8 from __future__ import unicode_literals -from datetime import date import json import pytest @@ -56,31 +55,6 @@ def test_move_item(test_item_and_response, mock_box_session, test_folder, mock_o assert isinstance(move_response, test_item.__class__) -@pytest.fixture(params=(True, False, None)) -def shared_link_can_download(request): - return request.param - - -@pytest.fixture(params=(True, False, None)) -def shared_link_can_preview(request): - return request.param - - -@pytest.fixture(params=('open', None)) -def shared_link_access(request): - return request.param - - -@pytest.fixture(params=('hunter2', None)) -def shared_link_password(request): - return request.param - - -@pytest.fixture(params=(date(2015, 5, 5), None)) -def shared_link_unshared_at(request): - return request.param - - def test_get_shared_link( test_item_and_response, mock_box_session,