Repository: box/box-python-sdk
Pull request #340 (2018-08-27 20:10:01)

added support and test for weblink endpoint

boxcla: Hi @carycheng, thanks for the pull request. Before we can merge it, we need you to sign our Contributor License Agreement. You can do so electronically here: http://opensource.box.com/cla

Once you have signed, just add a comment to this pull request saying, "CLA signed". Thanks!

Patch:
diff --git a/boxsdk/client/client.py b/boxsdk/client/client.py
index e95534e..2779e8e 100644
--- a/boxsdk/client/client.py
+++ b/boxsdk/client/client.py
@@ -401,6 +401,20 @@ def create_group(self, name):
             response_object=response,
         )
 
+    def web_link(self, web_link_id):
+        """
+        Initialize a :class: `WebLink` object, whose box id is web_link_id.
+        :param web_link_id:
+            The box ID of the :class:`WebLink` object.
+        :type web_link_id:
+            `unicode`
+        :return:
+            A :class:`WebLink` object with the given entry ID.
+        :rtype:
+            :class:`WebLink`
+        """
+        return self.translator.translate('web_link')(session=self._session, object_id=web_link_id)
+
     @api_call
     def get_recent_items(self, limit=None, marker=None, fields=None, **collection_kwargs):
         """
diff --git a/boxsdk/object/__init__.py b/boxsdk/object/__init__.py
index c0e58e5..697bb0d 100644
--- a/boxsdk/object/__init__.py
+++ b/boxsdk/object/__init__.py
@@ -19,4 +19,5 @@
     'recent_item',
     'search',
     'user',
+    'web_link',
 ]))
diff --git a/boxsdk/object/folder.py b/boxsdk/object/folder.py
index 49d8272..777a5e5 100644
--- a/boxsdk/object/folder.py
+++ b/boxsdk/object/folder.py
@@ -416,6 +416,45 @@ def add_collaborator(self, collaborator, role, notify=False, can_view_path=False
             response_object=collaboration_response,
         )
 
+    def create_web_link(self, target_url, name=None, description=None):
+        """
+        Create a WebLink with a given url.
+
+        :param target_url:
+            The url the web link points to.
+        :type target_url:
+            `unicode`
+        :param name:
+            The name of the web link. Optional, the API will give it a default if not specified.
+        :type name:
+            `unicode` or None
+        :param description:
+            Description of the web link
+        :type name:
+            `unicode` or None
+        :return:
+            A :class:`WebLink` object.
+        :rtype:
+            :class:`WebLink`
+        """
+        url = self._session.get_url('web_links')
+        web_link_attributes = {
+            'url': target_url,
+            'parent': {
+                'id': self.object_id
+            }
+        }
+        if name is not None:
+            web_link_attributes['name'] = name
+        if description is not None:
+            web_link_attributes['description'] = description
+        response = self._session.post(url, data=json.dumps(web_link_attributes)).json()
+        return self.translator.translate(response['type'])(
+            session=self._session,
+            object_id=response['id'],
+            response_object=response
+        )
+
     @api_call
     def delete(self, recursive=True, etag=None):
         """Base class override. Delete the folder.
diff --git a/boxsdk/object/web_link.py b/boxsdk/object/web_link.py
new file mode 100644
index 0000000..c9e7afb
--- /dev/null
+++ b/boxsdk/object/web_link.py
@@ -0,0 +1,10 @@
+# coding: utf-8
+from __future__ import unicode_literals, absolute_import
+
+from .base_object import BaseObject
+
+
+class WebLink(BaseObject):
+    """Box API endpoint for interacting with WebLinks."""
+
+    _item_type = 'web_link'
diff --git a/docs/web_link.md b/docs/web_link.md
new file mode 100644
index 0000000..eac7ac9
--- /dev/null
+++ b/docs/web_link.md
@@ -0,0 +1,60 @@
+Web Links
+=========
+
+Web links are objects that point to URLs. These objects are also known as
+bookmarks within the Box web application. Web link objects are treated
+similarly to file objects.
+
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Create Web Link](#create-web-link)
+- [Get Web Link](#get-web-link)
+- [Update Web Link](#update-web-link)
+- [Delete Web Link](#delete-web-link)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+Create Web Link
+---------------
+
+Calling `folder.create_web_link(target_url, name=None, description=None)` will let you create a new web link with a specified name and description.
+
+```python
+folder_id = '1234'
+target_url = 'https://example.com'
+link_name = 'Example Link'
+link_description = 'This is the description'
+web_link = client.folder(folder_id).create_web_link(target_url, link_name, link_description)
+```
+
+Get Web Link
+------------
+
+Calling `web_link.get()` can be used to retrieve information regarding a specific weblink.
+
+```python
+web_link_id = '1234'
+web_link = client.web_link(web_link_id).get()
+```
+
+Update Web Link
+---------------
+
+Calling `web_link.update_info()` can be used to update the weblink.
+
+```python
+web_link_update = {'url': 'https://newurl.com'}
+updated_web_link = client.web_link.update_info(web_link_update)
+```
+
+Delete Web Link
+---------------
+
+Calling `web_link.delete()` can be used to delete a specified weblink.
+
+```python
+web_link_id = '1234'
+client.web_link(web_link_id).delete()
+```


Test patch:
diff --git a/test/unit/object/conftest.py b/test/unit/object/conftest.py
index a1ec752..cd371c3 100644
--- a/test/unit/object/conftest.py
+++ b/test/unit/object/conftest.py
@@ -15,6 +15,7 @@
 from boxsdk.object.group import Group
 from boxsdk.object.user import User
 from boxsdk.object.search import Search
+from boxsdk.object.web_link import WebLink
 
 
 # pylint:disable=redefined-outer-name
@@ -97,6 +98,11 @@ def test_search(mock_box_session):
     return Search(mock_box_session)
 
 
+@pytest.fixture()
+def test_web_link(mock_box_session, mock_object_id):
+    return WebLink(mock_box_session, mock_object_id)
+
+
 @pytest.fixture()
 def test_device_pin(mock_box_session, mock_object_id):
     return DevicePinner(mock_box_session, mock_object_id)
diff --git a/test/unit/object/test_folder.py b/test/unit/object/test_folder.py
index 98f357d..2b72491 100644
--- a/test/unit/object/test_folder.py
+++ b/test/unit/object/test_folder.py
@@ -11,6 +11,7 @@
 from boxsdk.exception import BoxAPIException
 from boxsdk.network.default_network import DefaultNetworkResponse
 from boxsdk.object.file import File
+from boxsdk.object.web_link import WebLink
 from boxsdk.object.collaboration import Collaboration, CollaborationRole
 from boxsdk.object.folder import Folder, FolderSyncState
 from boxsdk.session.box_response import BoxResponse
@@ -285,3 +286,35 @@ def test_preflight(test_folder, mock_object_id, mock_box_session):
             }
         ),
     )
+
+
+def test_create_web_link_returns_the_correct_web_link_object(test_folder, mock_box_session):
+    expected_url = "{0}/web_links".format(API.BASE_API_URL)
+    expected_name = 'Test WebLink'
+    description = 'Test Description'
+    test_web_link_url = 'https://test.com'
+    mock_box_session.post.return_value.json.return_value = {
+        'type': 'web_link',
+        'id': '42',
+        'url': test_web_link_url,
+        'name': expected_name,
+        'description': description
+    }
+    new_web_link = test_folder.create_web_link(test_web_link_url, expected_name, description)
+    data = {
+        'url': test_web_link_url,
+        'parent': {
+            'id': '42',
+        },
+        'name': expected_name,
+        'description': description,
+    }
+    mock_box_session.post.assert_called_once_with(
+        expected_url,
+        data=json.dumps(data),
+    )
+    assert isinstance(new_web_link, WebLink)
+    assert new_web_link.object_id == '42'
+    assert new_web_link.url == test_web_link_url
+    assert new_web_link.name == expected_name
+    assert new_web_link.description == description
diff --git a/test/unit/object/test_web_link.py b/test/unit/object/test_web_link.py
new file mode 100644
index 0000000..776ec85
--- /dev/null
+++ b/test/unit/object/test_web_link.py
@@ -0,0 +1,55 @@
+# coding: utf-8
+from __future__ import unicode_literals
+
+import json
+
+from boxsdk.config import API
+from boxsdk.object.web_link import WebLink
+
+
+def test_get(mock_box_session, test_web_link):
+    # pylint:disable=redefined-outer-name, protected-access
+    web_link_id = test_web_link.object_id
+    expected_url = '{0}/{1}/{2}'.format(API.BASE_API_URL, 'web_links', web_link_id)
+    mock_web_link = {
+        'type': 'web_link',
+        'url': 'https://test/com',
+        'id': 1234,
+        'created_at': '2015-05-07T15:00:01-07:00',
+    }
+    mock_box_session.get.return_value.json.return_value = mock_web_link
+    web_link = test_web_link.get()
+    mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=None)
+    assert web_link.id == mock_web_link['id']
+    assert web_link.type == mock_web_link['type']
+    assert web_link.url == mock_web_link['url']
+    assert isinstance(web_link, WebLink)
+
+
+def test_update(mock_box_session, test_web_link):
+    # pylint:disable=redefined-outer-name, protected-access
+    web_link_id = test_web_link.object_id
+    expected_url = '{0}/{1}/{2}'.format(API.BASE_API_URL, 'web_links', web_link_id)
+    mock_web_link = {
+        'type': 'web_link',
+        'url': 'https://newtest.com',
+        'id': 1234,
+        'created_at': '2015-05-07T15:00:01-07:00',
+    }
+    data = {
+        'url': 'https://newtest.com',
+    }
+    mock_box_session.put.return_value.json.return_value = mock_web_link
+    web_link = test_web_link.update_info(data)
+    mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(data), headers=None, params=None)
+    assert web_link.id == mock_web_link['id']
+    assert web_link.type == mock_web_link['type']
+    assert web_link.url == mock_web_link['url']
+    assert isinstance(web_link, WebLink)
+
+
+def test_delete(mock_box_session, test_web_link):
+    web_link_id = test_web_link.object_id
+    expected_url = '{0}/{1}/{2}'.format(API.BASE_API_URL, 'web_links', web_link_id)
+    test_web_link.delete()
+    mock_box_session.delete.assert_called_once_with(expected_url, expect_json_response=False, headers=None, params={})