Repository: box/box-python-sdk

Issue:
Add ability to set expiration date on shared links
`get_shared_link()` only takes access and etag as parameters. The create shared link API supports many other parameters, including unshared_at, permissions, can_download, can_preview.
https://box-content.readme.io/#page-create-a-shared-link-for-a-file

Discussion:

Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index b38b2b1..c2dfaf0 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,13 @@
 Release History
 ---------------
 
+Upcoming
+++++++++
+
+- Added support for setting shared link expiration dates.
+- Added support for setting shared link permissions.
+
+
 1.1.6 (2015-04-17)
 ++++++++++++++++++
 
@@ -19,13 +26,6 @@ Release History
 - 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)
-++++++++++++++++++
-
-- Added support for the /shared_items endpoint. `client.get_shared_item` can be used to get information about
-  a shared link. See https://developers.box.com/docs/#shared-items
-
-
 1.1.3 (2015-03-26)
 ++++++++++++++++++
 
diff --git a/boxsdk/object/item.py b/boxsdk/object/item.py
index e507e86..26fb391 100644
--- a/boxsdk/object/item.py
+++ b/boxsdk/object/item.py
@@ -159,7 +159,7 @@ def move(self, parent_folder):
         }
         return self.update_info(data)
 
-    def get_shared_link(self, access=None, etag=None):
+    def get_shared_link(self, access=None, etag=None, unshared_at=None, allow_download=None, allow_preview=None):
         """Get a shared link for the item with the given access permissions.
 
         :param access:
@@ -171,6 +171,21 @@ def get_shared_link(self, access=None, etag=None):
             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
         :returns:
             The URL of the shared link.
         :rtype:
@@ -182,6 +197,17 @@ def get_shared_link(self, access=None, etag=None):
                 'access': access
             }
         }
+
+        if unshared_at is not None:
+            data['shared_link']['unshared_at'] = unshared_at.isoformat()
+
+        if allow_download is not None or allow_preview is not None:
+            data['shared_link']['permissions'] = permissions = {}
+            if allow_download is not None:
+                permissions['can_download'] = allow_download
+            if allow_preview is not None:
+                permissions['can_preview'] = allow_preview
+
         item = self.update_info(data, etag=etag)
         return item.shared_link['url']
 
diff --git a/tox.ini b/tox.ini
index 211ff3d..8eb5920 100644
--- a/tox.ini
+++ b/tox.ini
@@ -28,6 +28,7 @@ deps =
 commands =
   rst2html.py --strict README.rst
   rst2html.py --strict HISTORY.rst
+  rst2html.py --strict CONTRIBUTING.rst
 
 [testenv:pep8]
 commands = 


Test patch:
diff --git a/test/unit/object/test_item.py b/test/unit/object/test_item.py
index 1bfa2f1..32721fc 100644
--- a/test/unit/object/test_item.py
+++ b/test/unit/object/test_item.py
@@ -1,6 +1,7 @@
 # coding: utf-8
 
 from __future__ import unicode_literals
+from datetime import date
 import json
 import pytest
 
@@ -55,12 +56,33 @@ def test_move_item(test_item_and_response, mock_box_session, test_folder, mock_o
     assert isinstance(move_response, test_item.__class__)
 
 
-@pytest.mark.parametrize('access,expected_access_data', [(None, {}), ('open', {'access': 'open'})])
+@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=(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,
-        access,
-        expected_access_data,
+        shared_link_access,
+        shared_link_unshared_at,
+        shared_link_can_download,
+        shared_link_can_preview,
         test_url,
         etag,
         if_match_header,
@@ -69,10 +91,27 @@ def test_get_shared_link(
     test_item, _ = test_item_and_response
     expected_url = test_item.get_url()
     mock_box_session.put.return_value.json.return_value = {'shared_link': {'url': test_url}}
-    url = test_item.get_shared_link(access, etag=etag)
+    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_download is not None or shared_link_can_preview is not None:
+        expected_data['shared_link']['permissions'] = permissions = {}
+        if shared_link_can_download is not None:
+            permissions['can_download'] = shared_link_can_download
+        if shared_link_can_preview is not None:
+            permissions['can_preview'] = shared_link_can_preview
+    url = test_item.get_shared_link(
+        etag=etag,
+        access=shared_link_access,
+        unshared_at=shared_link_unshared_at,
+        allow_download=shared_link_can_download,
+        allow_preview=shared_link_can_preview,
+    )
     mock_box_session.put.assert_called_once_with(
         expected_url,
-        data=json.dumps({'shared_link': expected_access_data}),
+        data=json.dumps(expected_data),
         headers=if_match_header,
         params=None,
     )