diff --git a/HISTORY.rst b/HISTORY.rst index 49389356..81bde186 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,7 @@ Next release - Add `admin_logs_streaming` support for events stream (`#623 `_) - Add `vanity_name` param for creating shared link to a file or folder (`#637 `_) - Add getting files and file versions under retention for a retention policy assignment (`#633 `_) +- Support base item operations for WebLink class (`#639 `_) **Bug Fixes:** - Limit cryptography to version <3.5.0 (`#636 `_) diff --git a/boxsdk/object/base_item.py b/boxsdk/object/base_item.py new file mode 100644 index 00000000..218c675c --- /dev/null +++ b/boxsdk/object/base_item.py @@ -0,0 +1,204 @@ +import json + +from .base_object import BaseObject +from ..exception import BoxValueError +from ..util.api_call_decorator import api_call +from ..util.default_arg_value import SDK_VALUE_NOT_SET + + +class BaseItem(BaseObject): + + @api_call + def copy(self, parent_folder, name=None): + """Copy the item to the given folder. + + :param parent_folder: + The folder to which the item should be copied. + :type parent_folder: + :class:`Folder` + :param name: + A new name for the item, in case there is already another item in the new parent folder with the same name. + :type name: + `unicode` or None + """ + self.validate_item_id(self._object_id) + url = self.get_url('copy') + data = { + 'parent': {'id': parent_folder.object_id} + } + if name is not None: + data['name'] = name + box_response = self._session.post(url, data=json.dumps(data)) + response = box_response.json() + return self.translator.translate( + session=self._session, + response_object=response, + ) + + @api_call + def move(self, parent_folder, name=None): + """ + Move the item to the given folder. + + :param parent_folder: + The parent `Folder` object, where the item will be moved to. + :type parent_folder: + :class:`Folder` + :param name: + A new name for the item, in case there is already another item in the new parent folder with the same name. + :type name: + `unicode` or None + """ + data = { + 'parent': {'id': parent_folder.object_id} + } + if name is not None: + data['name'] = name + return self.update_info(data) + + @api_call + def rename(self, name): + """ + Rename the item to a new name. + + :param name: + The new name, you want the item to be renamed to. + :type name: + `unicode` + """ + data = { + 'name': name, + } + return self.update_info(data) + + @api_call + def create_shared_link(self, **kwargs): + """ + Create a shared link for the item with the given access permissions. + + :param kwargs: + Keyword arguments passed from overriding method used to build request properties. + :return: + The updated object with shared link. + Returns a new object of the same type, without modifying the original object passed as self. + :rtype: + :class:`BaseItem` + """ + shared_link = {} + + if kwargs.get('access') is not None: + shared_link['access'] = kwargs.get('access') + + if kwargs.get('unshared_at') is not SDK_VALUE_NOT_SET: + shared_link['unshared_at'] = kwargs.get('unshared_at') + + if kwargs.get('allow_download') is not None or kwargs.get('allow_preview') is not None: + shared_link['permissions'] = {} + if kwargs.get('allow_download') is not None: + shared_link['permissions']['can_download'] = kwargs.get('allow_download') + if kwargs.get('allow_preview') is not None: + shared_link['permissions']['can_preview'] = kwargs.get('allow_preview') + + if kwargs.get('password') is not None: + shared_link['password'] = kwargs.get('password') + + if kwargs.get('vanity_name') is not None: + shared_link['vanity_name'] = kwargs.get('vanity_name') + + data = {'shared_link': shared_link} + update_info_kwargs = {'etag': kwargs.get('etag')} if kwargs.get('etag') is not None else {} + + return self.update_info(data, **update_info_kwargs) + + @api_call + def get_shared_link(self, **kwargs): + """ + 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 kwargs: + Keyword arguments passed from overriding method used to create a new shared link. + :returns: + The URL of the shared link. + :rtype: + `unicode` + """ + item = self.create_shared_link(**kwargs) + return item.shared_link['url'] # pylint:disable=no-member + + @api_call + def remove_shared_link(self, **kwargs): + """ + Delete the shared link for the item. + + :param kwargs: + Keyword arguments passed from overriding method used to build request properties. + :returns: + Whether or not the update was successful. + :rtype: + `bool` + """ + data = {'shared_link': None} + update_info_kwargs = {'etag': kwargs.get('etag')} if kwargs.get('etag') is not None else {} + + item = self.update_info(data, **update_info_kwargs) + return item.shared_link is None # pylint:disable=no-member + + @api_call + def add_to_collection(self, collection): + """ + Add the item to a collection. This method is not currently safe from race conditions. + + :param collection: + The collection to add the item to. + :type collection: + :class:`Collection` + :return: + This item. + :rtype: + :class:`Item` + """ + collections = self.get(fields=['collections']).collections # pylint:disable=no-member + collections.append({'id': collection.object_id}) + data = { + 'collections': collections + } + return self.update_info(data) + + @api_call + def remove_from_collection(self, collection): + """ + Remove the item from a collection. This method is not currently safe from race conditions. + + :param collection: + The collection to remove the item from. + :type collection: + :class:`Collection` + :return: + This item. + :rtype: + :class:`Item` + """ + collections = self.get(fields=['collections']).collections # pylint:disable=no-member + updated_collections = [c for c in collections if c['id'] != collection.object_id] + data = { + 'collections': updated_collections + } + return self.update_info(data) + + @staticmethod + def validate_item_id(item_id): + """ + Validates an item ID is numeric + + :param item_id: + :type item_id: + `str` or `int` + :raises: + BoxException: if item_id is not numeric + :returns: + :rtype: + None + """ + if not isinstance(item_id, int) and not item_id.isdigit(): + raise BoxValueError("Invalid item ID") diff --git a/boxsdk/object/folder.py b/boxsdk/object/folder.py index fbfaacb4..a0737fd6 100644 --- a/boxsdk/object/folder.py +++ b/boxsdk/object/folder.py @@ -201,7 +201,7 @@ def get_items(self, limit=None, offset=0, marker=None, use_marker=False, sort=No `unicode` or None :param sort: Item field to sort results on: 'id', 'name', or 'date'. - :type sort': + :type sort: `unicode` or None :param direction: Sort direction for the items returned. diff --git a/boxsdk/object/item.py b/boxsdk/object/item.py index 18e607e5..8fc0cc2f 100644 --- a/boxsdk/object/item.py +++ b/boxsdk/object/item.py @@ -4,11 +4,8 @@ import json from boxsdk.util.text_enum import TextEnum -from .base_object import BaseObject -from ..exception import ( - BoxAPIException, - BoxValueError -) +from .base_item import BaseItem +from ..exception import BoxAPIException from .metadata import Metadata from ..util.api_call_decorator import api_call from ..util.default_arg_value import SDK_VALUE_NOT_SET @@ -24,7 +21,7 @@ class ClassificationType(TextEnum): NONE = 'None' -class Item(BaseObject): +class Item(BaseItem): """Box API endpoint for interacting with files and folders.""" _classification_template_key = 'securityClassification-6VMVochwUWo' @@ -105,8 +102,17 @@ def _preflight_check(self, size, name=None, file_id=None, parent_id=None): @api_call def update_info(self, data, etag=None): - """Baseclass override. - + """ + Baseclass override. + :param data: + The updated information about this object. + Must be JSON serializable. + Update the object attributes in data.keys(). The semantics of the + values depends on the the type and attributes of the object being + updated. For details on particular semantics, refer to the Box + developer API documentation . + :type data: + `dict` :param etag: If specified, instruct the Box API to perform the update only if the current version's etag matches. @@ -124,24 +130,10 @@ def update_info(self, data, etag=None): headers = {'If-Match': etag} if etag is not None else None return super(Item, self).update_info(data, headers=headers) - @api_call - def rename(self, name): - """ - Rename the item to a new name. - - :param name: - The new name, you want the item to be renamed to. - :type name: - `unicode` - """ - data = { - 'name': name, - } - return self.update_info(data) - @api_call def get(self, fields=None, etag=None): - """Base class override. + """ + Base class override. :param fields: List of fields to request. @@ -162,54 +154,6 @@ def get(self, fields=None, etag=None): headers = {'If-None-Match': etag} if etag is not None else None return super(Item, self).get(fields=fields, headers=headers) - @api_call - def copy(self, parent_folder, name=None): - """Copy the item to the given folder. - - :param parent_folder: - The folder to which the item should be copied. - :type parent_folder: - :class:`Folder` - :param name: - A new name for the item, in case there is already another item in the new parent folder with the same name. - :type name: - `unicode` or None - """ - self.validate_item_id(self._object_id) - url = self.get_url('copy') - data = { - 'parent': {'id': parent_folder.object_id} - } - if name is not None: - data['name'] = name - box_response = self._session.post(url, data=json.dumps(data)) - response = box_response.json() - return self.translator.translate( - session=self._session, - response_object=response, - ) - - @api_call - def move(self, parent_folder, name=None): - """ - Move the item to the given folder. - - :param parent_folder: - The parent `Folder` object, where the item will be moved to. - :type parent_folder: - :class:`Folder` - :param name: - A new name for the item, in case there is already another item in the new parent folder with the same name. - :type name: - `unicode` or None - """ - data = { - 'parent': {'id': parent_folder.object_id} - } - if name is not None: - data['name'] = name - return self.update_info(data) - @api_call def create_shared_link( self, @@ -219,10 +163,11 @@ def create_shared_link( allow_download=None, allow_preview=None, password=None, - vanity_name=None + vanity_name=None, + **kwargs ): """ - Create a shared link for the item with the given access permissions. + Baseclass override. :param access: Determines who can access the shared link. May be open, company, or collaborators. If no access is @@ -260,36 +205,25 @@ def create_shared_link( If this parameter is None, the standard shared link URL will be used. :type vanity_name: `unicode` or None + :param kwargs: + Used to fulfill the contract of overriden method :return: - The updated object with s shared link. + The updated object with shared link. Returns a new object of the same type, without modifying the original object passed as self. :rtype: :class:`Item` :raises: :class:`BoxAPIException` if the specified etag doesn't match the latest version of the item. """ - data = { - 'shared_link': {} if not access else { - 'access': access - } - } - - if unshared_at is not SDK_VALUE_NOT_SET: - data['shared_link']['unshared_at'] = unshared_at - - 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 - - if password is not None: - data['shared_link']['password'] = password - - if vanity_name is not None: - data['shared_link']['vanity_name'] = vanity_name - - return self.update_info(data, etag=etag) + # pylint:disable=arguments-differ + return super(Item, self).create_shared_link( + access=access, + etag=etag, + unshared_at=unshared_at, + allow_download=allow_download, + allow_preview=allow_preview, + password=password, + vanity_name=vanity_name + ) @api_call def get_shared_link( @@ -300,11 +234,11 @@ def get_shared_link( allow_download=None, allow_preview=None, password=None, - vanity_name=None + vanity_name=None, + **kwargs ): """ - 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. + Baseclass override. :param access: Determines who can access the shared link. May be open, company, or collaborators. If no access is @@ -340,13 +274,16 @@ def get_shared_link( If this parameter is None, the standard shared link URL will be used. :type vanity_name: `unicode` or None + :param kwargs: + Used to fulfill the contract of overriden method :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( + # pylint:disable=arguments-differ + return super(Item, self).get_shared_link( access=access, etag=etag, unshared_at=unshared_at, @@ -355,25 +292,26 @@ def get_shared_link( password=password, vanity_name=vanity_name ) - return item.shared_link['url'] # pylint:disable=no-member @api_call - def remove_shared_link(self, etag=None): - """Delete the shared link for the item. + def remove_shared_link(self, etag=None, **kwargs): + """ + Baseclass override. :param etag: If specified, instruct the Box API to delete the link only if the current version's etag matches. :type etag: `unicode` or None + :param kwargs: + Used to fulfill the contract of overriden method :returns: Whether or not the update was successful. :rtype: `bool` :raises: :class:`BoxAPIException` if the specified etag doesn't match the latest version of the item. """ - data = {'shared_link': None} - item = self.update_info(data, etag=etag) - return item.shared_link is None # pylint:disable=no-member + # pylint:disable=arguments-differ + return super(Item, self).remove_shared_link(etag=etag) @api_call def delete(self, params=None, etag=None): @@ -484,48 +422,6 @@ def delete_watermark(self): box_response = self._session.delete(url, expect_json_response=False) return box_response.ok - @api_call - def add_to_collection(self, collection): - """ - Add the item to a collection. This method is not currently safe from race conditions. - - :param collection: - The collection to add the item to. - :type collection: - :class:`Collection` - :return: - This item. - :rtype: - :class:`Item` - """ - collections = self.get(fields=['collections']).collections # pylint:disable=no-member - collections.append({'id': collection.object_id}) - data = { - 'collections': collections - } - return self.update_info(data) - - @api_call - def remove_from_collection(self, collection): - """ - Remove the item from a collection. This method is not currently safe from race conditions. - - :param collection: - The collection to remove the item from. - :type collection: - :class:`Collection` - :return: - This item. - :rtype: - :class:`Item` - """ - collections = self.get(fields=['collections']).collections # pylint:disable=no-member - updated_collections = [c for c in collections if c['id'] != collection.object_id] - data = { - 'collections': updated_collections - } - return self.update_info(data) - @api_call def collaborate(self, accessible_by, role, can_view_path=None, notify=None, fields=None): """Collaborate user or group onto a Box item. @@ -760,20 +656,3 @@ def remove_classification(self): `bool` """ return self.metadata('enterprise', self._classification_template_key).delete() - - @staticmethod - def validate_item_id(item_id): - """ - Validates an item ID is numeric - - :param item_id: - :type item_id: - `str` or `int` - :raises: - BoxException: if item_id is not numeric - :returns: - :rtype: - None - """ - if not isinstance(item_id, int) and not item_id.isdigit(): - raise BoxValueError("Invalid item ID") diff --git a/boxsdk/object/web_link.py b/boxsdk/object/web_link.py index c9e7afbf..49f1582f 100644 --- a/boxsdk/object/web_link.py +++ b/boxsdk/object/web_link.py @@ -1,10 +1,125 @@ # coding: utf-8 from __future__ import unicode_literals, absolute_import -from .base_object import BaseObject +from ..util.api_call_decorator import api_call +from ..util.default_arg_value import SDK_VALUE_NOT_SET +from .base_item import BaseItem -class WebLink(BaseObject): +class WebLink(BaseItem): """Box API endpoint for interacting with WebLinks.""" _item_type = 'web_link' + + @api_call + def create_shared_link( + self, + access=None, + unshared_at=SDK_VALUE_NOT_SET, + password=None, + vanity_name=None, + **kwargs + ): + """ + Baseclass override. + + :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 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. Takes an RFC3339-formatted string, e.g. + '2018-10-31T23:59:59-07:00' for 11:59:59 PM on October 31, 2018 in the America/Los_Angeles timezone. + The time portion can be omitted, which defaults to midnight (00:00:00) on that date. + :type unshared_at: + `unicode` 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 + :param vanity_name: + Defines a custom vanity name to use in the shared link URL, eg. https://app.box.com/v/my-custom-vanity-name. + If this parameter is None, the standard shared link URL will be used. + :type vanity_name: + `unicode` or None + :param kwargs: + Used to fulfill the contract of overriden method + :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: + :class:`WebLink` + """ + # pylint:disable=arguments-differ + return super(WebLink, self).create_shared_link( + access=access, + unshared_at=unshared_at, + password=password, + vanity_name=vanity_name + ) + + @api_call + def get_shared_link( + self, + access=None, + unshared_at=SDK_VALUE_NOT_SET, + password=None, + vanity_name=None, + **kwargs + ): + """ + Baseclass override. + + :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 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: + `unicode` 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 + :param vanity_name: + Defines a custom vanity name to use in the shared link URL, eg. https://app.box.com/v/my-custom-vanity-name. + If this parameter is None, the standard shared link URL will be used. + :type vanity_name: + `unicode` or None + :param kwargs: + Used to fulfill the contract of overriden method + :param kwargs: + Passed to the superclass + :returns: + The URL of the shared link. + :rtype: + `unicode` + """ + # pylint:disable=arguments-differ + return super(WebLink, self).get_shared_link( + access=access, + unshared_at=unshared_at, + password=password, + vanity_name=vanity_name + ) + + @api_call + def remove_shared_link(self, **kwargs): + """ + Baseclass override. + + :param kwargs: + Used to fulfill the contract of overriden method + :returns: + Whether or not the update was successful. + :rtype: + `bool` + """ + return super(WebLink, self).remove_shared_link() diff --git a/docs/usage/collections.md b/docs/usage/collections.md index 146976cd..e7a58630 100644 --- a/docs/usage/collections.md +++ b/docs/usage/collections.md @@ -1,16 +1,15 @@ Collections =========== -Collections allow users to mark specific files and folders to make it easier to find them. +Collections allow users to mark specific files, folders and web links to make it easier to find them. - - [Get a User's Collections](#get-a-users-collections) - [Get the Items in a Collection](#get-the-items-in-a-collection) -- [Add Item to Collection](#add-item-to-collection) -- [Remove Item from Collection](#remove-item-from-collection) +- [Add an Item to a Collection](#add-an-item-to-a-collection) +- [Remove an Item from a Collection](#remove-an-item-from-a-collection) @@ -36,8 +35,8 @@ Get the Items in a Collection To retrieve a list of items contained in a collection, call [`collection.get_items(limit=None, offset=0, fields=None)`][get_items]. This method returns a -`BoxObjectCollection` that you can use to iterate over all the [`Item`][item_class] objects in -the collection. +`BoxObjectCollection` that you can use to iterate over all the [`BaseItem`][base_item_class] objects in +the collection. [`BaseItem`][base_item_class] is a super class for files, folders and web links. ```python @@ -47,13 +46,13 @@ for item in items: ``` [get_items]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.collection.Collection.get_items -[item_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item +[base_item_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem -Add Item to Collection +Add an Item to a Collection ---------------------- -To add an [`Item`][item_class] to a collection, call [`item.add_to_collection(collection)`][add_to_collection] with the -[`Collection`][collection_class] you want to add the item to. This method returns the updated [`Item`][item_class] +To add an [`BaseItem`][base_item_class] to a collection, call [`item.add_to_collection(collection)`][add_to_collection] with the +[`Collection`][collection_class] you want to add the item to. This method returns the updated [`BaseItem`][base_item_class] object. @@ -63,14 +62,14 @@ updated_file = client.file(file_id='11111').add_to_collection(collection) print('File "{0}" added to collection!'.format(updated_file.name)) ``` -[add_to_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.add_to_collection +[add_to_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.add_to_collection -Remove Item from Collection +Remove an Item from a Collection --------------------------- -To remove an [`Item`][item_class] from a collection, call +To remove an [`BaseItem`][base_item_class] from a collection, call [`item.remove_from_collection(collection)`][remove_from_collection] with the [`Collection`][collection_class] you want -to remove the item from. This method returns the updated [`Item`][item_class] object. +to remove the item from. This method returns the updated [`BaseItem`][base_item_class] object. ```python @@ -79,4 +78,4 @@ updated_file = client.file(file_id='11111').remove_from_collection(collection) print('File "{0}" removed from collection!'.format(updated_file.name)) ``` -[remove_from_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_from_collection +[remove_from_collection]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.remove_from_collection diff --git a/docs/usage/files.md b/docs/usage/files.md index 406b6f19..127019df 100644 --- a/docs/usage/files.md +++ b/docs/usage/files.md @@ -8,7 +8,6 @@ file's contents, upload new versions, and perform other common file operations - - [Get a File's Information](#get-a-files-information) - [Update a File's Information](#update-a-files-information) - [Download a File](#download-a-file) @@ -27,6 +26,7 @@ file's contents, upload new versions, and perform other common file operations - [List Upload Parts](#list-upload-parts) - [Move a File](#move-a-file) - [Copy a File](#copy-a-file) +- [Rename a File](#rename-a-file) - [Delete a File](#delete-a-file) - [Get Previous Versions of a File](#get-previous-versions-of-a-file) - [Upload a New Version of a File](#upload-a-new-version-of-a-file) @@ -42,6 +42,7 @@ file's contents, upload new versions, and perform other common file operations - [Remove a Shared Link](#remove-a-shared-link) - [Get an Embed Link](#get-an-embed-link) - [Get File Representations](#get-file-representations) +- [Get Thumbnail (Deprecated)](#get-thumbnail-deprecated) - [Get Thumbnail](#get-thumbnail) - [Set Metadata](#set-metadata) - [Get Metadata](#get-metadata) @@ -458,7 +459,7 @@ moved_file = file_to_move.move(destination_folder) print('File "{0}" has been moved into folder "{1}"'.format(moved_file.name, moved_file.parent.name)) ``` -[move]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.move +[move]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.move Copy a File ----------- @@ -479,7 +480,22 @@ file_copy = file_to_copy.copy(destination_folder) print('File "{0}" has been copied into folder "{1}"'.format(file_copy.name, file_copy.parent.name)) ``` -[copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.copy +[copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.copy + +Rename a File +----------- + +A file can be renamed by calling [`file.rebane(name)`][rename]. This method returns the updated +[`File`][file_class] object with a new name. Remeber to provide also extention of the file along with the new name. + +```python +file = client.file(file_id='11111') + +renamed_file = file.rename("new-name.pdf") +print('File was renamed to "{0}"'.format(renamed_file.name)) +``` + +[rename]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.rename Delete a File ------------- @@ -639,7 +655,7 @@ This method returns a `unicode` string containing the shared link URL. ```python file_id = '11111' -url = client.file(file_id).get_shared_link_download_url(access='open', vanity_name="my-unique-vanity-name") +url = client.file(file_id).get_shared_link_download_url(access='collaborators', vanity_name="my-unique-vanity-name") print('The file shared link download URL is: {0}'.format(url)) ``` @@ -663,7 +679,7 @@ Create a Shared Link A shared link for a file can be generated by calling [`file.get_shared_link(access=None, etag=None, unshared_at=None, allow_download=None, allow_preview=None, -password=None, vanity_name=None)`][get_shared_link]. +password=None, vanity_name=None, **kwargs)`][get_shared_link]. This method returns a `unicode` string containing the shared link URL. @@ -681,7 +697,7 @@ Update a Shared Link A shared link for a file can be updated by calling [`file.get_shared_link(access=None, etag=None, unshared_at=None, allow_download=None, allow_preview=None, -password=None, vanity_name=None)`][update_shared_link] +password=None, vanity_name=None, **kwargs)`][update_shared_link] with an updated list of properties. This method returns a `unicode` string containing the shared link URL. @@ -709,16 +725,13 @@ This method returns a `unicode` string containing the shared link URL. ```python file_id = '11111' shared_link = client.file(file_id).get().shared_link -url = shared_link['download_url'] +url = shared_link['url'] ``` -[remove_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link - Remove a Shared Link -------------------- -A shared link for a file can be removed calling -[`file.remove_shared_link(etag=None)`][remove_shared_link]. +A shared link for a file can be removed by calling [`file.remove_shared_link(etag=None, **kwargs)`][remove_shared_link]. ```python @@ -726,7 +739,8 @@ file_id = '11111' client.file(file_id).remove_shared_link() ``` -[remove_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link +[remove_shared_link]: +https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link Get an Embed Link diff --git a/docs/usage/folders.md b/docs/usage/folders.md index 3685d29c..9c436cc3 100644 --- a/docs/usage/folders.md +++ b/docs/usage/folders.md @@ -8,7 +8,6 @@ group, and perform other common folder operations (move, copy, delete, etc.). - - [Get Information About a Folder](#get-information-about-a-folder) - [Get the User's Root Folder](#get-the-users-root-folder) - [Get the Items in a Folder](#get-the-items-in-a-folder) @@ -16,6 +15,7 @@ group, and perform other common folder operations (move, copy, delete, etc.). - [Create a Folder](#create-a-folder) - [Copy a Folder](#copy-a-folder) - [Move a Folder](#move-a-folder) +- [Rename a File](#rename-a-file) - [Delete a Folder](#delete-a-folder) - [Find a Folder for a Shared Link](#find-a-folder-for-a-shared-link) - [Create a Shared Link](#create-a-shared-link) @@ -30,6 +30,9 @@ group, and perform other common folder operations (move, copy, delete, etc.). - [Set a Classification](#set-a-classification) - [Retrieve a Classification](#retrieve-a-classification) - [Remove a Classification](#remove-a-classification) +- [Create a Folder Lock](#create-a-folder-lock) +- [Get Folder Locks](#get-folder-locks) +- [Delete a Folder Lock](#delete-a-folder-lock) @@ -139,7 +142,7 @@ folder_copy = folder_to_copy.copy(destination_folder) print('Folder "{0}" has been copied into folder "{1}"'.format(folder_copy.name, folder_copy.parent.name)) ``` -[copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.copy +[copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.copy Move a Folder ------------- @@ -160,7 +163,22 @@ moved_folder = folder_to_move.move(destination_folder) print('Folder "{0}" has been moved into folder "{1}"'.format(moved_folder.name, moved_folder.parent.name)) ``` -[move]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.move +[move]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.move + +Rename a File +----------- + +A folder can be renamed by calling [`folder.rebane(name)`][rename]. This method returns the updated +[`Folder`][folder_class] object with a new name. + +```python +folder = client.folder(folder_id='11111') + +renamed_folder = folder.rename("new-name") +print('Folder was renamed to "{0}"'.format(renamed_folder.name)) +``` + +[rename]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.rename Delete a Folder --------------- @@ -197,7 +215,7 @@ Create a Shared Link A shared link for a folder can be generated by calling [`folder.get_shared_link(access=None, etag=None, unshared_at=None, allow_download=None, allow_preview=None, -password=None, vanity_name=None)`][get_shared_link]. +password=None, vanity_name=None, **kwargs)`][get_shared_link]. This method returns a `unicode` string containing the shared link URL. @@ -215,7 +233,7 @@ Update a Shared Link A shared link for a folder can be updated by calling [`folder.get_shared_link(access=None, etag=None, unshared_at=None, allow_download=None, allow_preview=None, -password=None, vanity_name=None)`][update_shared_link] +password=None, vanity_name=None, **kwargs)`][update_shared_link] with an updated list of properties. This method returns a `unicode` string containing the shared link URL. @@ -243,16 +261,14 @@ This method returns a `unicode` string containing the shared link URL. ```python folder_id = '11111' shared_link = client.folder(folder_id).get().shared_link -url = shared_link['download_url'] +url = shared_link['url'] ``` -[remove_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link - Remove a Shared Link -------------------- -A shared link for a folder can be removed calling -[`folder.remove_shared_link(etag=None)`][remove_shared_link]. +A shared link for a folder can be removed by calling +[`folder.remove_shared_link(etag=None, **kwargs)`][remove_shared_link]. ```python @@ -260,7 +276,8 @@ folder_id = '11111' client.folder(folder_id).remove_shared_link() ``` -[remove_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link +[remove_shared_link]: +https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.item.Item.remove_shared_link Set Metadata diff --git a/docs/usage/web_link.md b/docs/usage/web_link.md index 6f406936..cc9e2dd5 100644 --- a/docs/usage/web_link.md +++ b/docs/usage/web_link.md @@ -12,7 +12,14 @@ similarly to file objects. - [Create Web Link](#create-web-link) - [Get Web Link](#get-web-link) - [Update Web Link](#update-web-link) +- [Move a Web Link](#move-a-web-link) +- [Copy a Web Link](#copy-a-web-link) +- [Rename a Web Link](#rename-a-web-link) - [Delete Web Link](#delete-web-link) +- [Create a Shared Link](#create-a-shared-link) +- [Update a Shared Link](#update-a-shared-link) +- [Get a Shared Link](#get-a-shared-link) +- [Remove a Shared Link](#remove-a-shared-link) @@ -56,7 +63,6 @@ To update a web link object, call [`web_link.update_info(data)`][update_info] wi properties to update on the web link. This method returns a newly updated ['WebLink'][web_link_class] object, leaving the original object unmodified. - ```python updated_web_link = client.web_link(web_link_id='12345').update_info({'url': 'https://newurl.com'}) ``` @@ -64,6 +70,60 @@ updated_web_link = client.web_link(web_link_id='12345').update_info({'url': 'htt [update_info]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.update_info [web_link_class]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink +Move a Web Link +----------- + +To move a web link from one folder into another, call [`web_link.move(parent_folder, name=None)`][move] with the destination +folder to move the web link into. You can optionally provide a `name` parameter to automatically rename the web link +in case of a name conflict in the destination folder. This method returns the updated [`WebLink`][web_link_class] +object in the new folder. + +```python +web_link_id = '11111' +destination_folder_id = '44444' + +web_link_to_move = client.web_link(web_link_id) +destination_folder = client.folder(destination_folder_id) + +moved_web_link = web_link_to_move.move(destination_folder) +print('Web link "{0}" has been moved into folder "{1}"'.format(moved_web_link.name, moved_web_link.parent.name)) +``` + +[move]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.move + +Copy a Web Link +----------- + +A web link can be copied to a new folder by calling [`web_link.copy(parent_folder, name=None)`][copy] with the destination +folder and an optional new name for the web link in case there is a name conflict in the destination folder. This method +returns a [`WebLink`][web_link_class] object representing the copy of the web link in the destination folder. + + +```python +web_link = client.web_link(web_link_id='12345') + +web_link_copy = web_link_to_copy.copy(destination_folder) +print('Web link "{0}" has been copied into folder "{1}"'.format(web_link_copy.name, web_link_copy.parent.name)) +``` + +[copy]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.copy + +Rename a Web Link +----------- + +A web link can be renamed by calling [`web_link.rebane(name)`][rename]. This method returns the updated +[`WebLink`][web_link_class] object with a new name. + +```python +web_link = client.web_link(web_link_id='12345') + +renamed_web_link = web_link.rename("new-name") +print('Web link was renamed to "{0}"'.format(renamed_web_link.name)) +``` + +[rename]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_item.BaseItem.rename + + Delete Web Link --------------- @@ -77,3 +137,61 @@ print('The web link was deleted!') ``` [delete]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.base_object.BaseObject.delete + +Create a Shared Link +-------------------- + +A shared link for a web link can be generated by calling +[`web_link.get_shared_link(access=None, unshared_at=SDK_VALUE_NOT_SET, password=None, +vanity_name=None, **kwargs)`][get_shared_link]. This method returns a `unicode` string containing the shared link URL. + + +```python +url = client.web_link('12345').get_shared_link(access='open') +print('The web link shared link URL is: {0}'.format(url)) +``` + +[get_shared_link]: https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink.get_shared_link + +Update a Shared Link +-------------------- + +A shared link for a web link can be updated by calling +[`web_link.get_shared_link(access=None, unshared_at=SDK_VALUE_NOT_SET, password=None, +vanity_name=None, **kwargs)`][update_shared_link] with an updated list of properties. + +This method returns a `unicode` string containing the shared link URL. + + +```python +url = client.web_link('12345').get_shared_link(access='open', password='letmein') +print('The web link shared link URL is: {0}'.format(url)) +``` + +[update_shared_link]: +https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink.get_shared_link + +Get a Shared Link +-------------------- + +To check for an existing shared link on a web link, simply call `web_link.shared_link` +This method returns a `unicode` string containing the shared link URL. + + +```python +shared_link = client.web_link('12345').get().shared_link +url = shared_link['url'] +``` + +Remove a Shared Link +-------------------- + +A shared link for a web link can be removed by calling [`web_link.remove_shared_link(**kwargs)`][remove_shared_link]. + + +```python +client.web_link('12345').remove_shared_link() +``` + +[remove_shared_link]: +https://box-python-sdk.readthedocs.io/en/latest/boxsdk.object.html#boxsdk.object.web_link.WebLink.remove_shared_link diff --git a/test/unit/object/test_base_item.py b/test/unit/object/test_base_item.py new file mode 100644 index 00000000..c9cbff91 --- /dev/null +++ b/test/unit/object/test_base_item.py @@ -0,0 +1,143 @@ +# coding: utf-8 + +from __future__ import unicode_literals + +import json +import pytest + + +@pytest.fixture(params=('file', 'folder', 'web_link')) +def test_base_item_and_response( + test_file, test_folder, test_web_link, mock_file_response, mock_folder_response, mock_web_link_response, request): + if request.param == 'file': + return test_file, mock_file_response + elif request.param == 'folder': + return test_folder, mock_folder_response + + return test_web_link, mock_web_link_response + + +@pytest.fixture(params=('empty', 'same', 'other')) +def test_collections_for_addition(mock_collection_id, request): + """Fixture returning a tuple of the expected collections values before and after addition""" + other_collection_id = mock_collection_id + '2' + if request.param == 'empty': + return [], [{'id': mock_collection_id}] + elif request.param == 'same': + # Adding a second instance of the same collection is handled correctly by the API, + # so for simplicity we do not check for an existing copy of the collection and just append + return [{'id': mock_collection_id}], [{'id': mock_collection_id}, {'id': mock_collection_id}] + elif request.param == 'other': + return [{'id': other_collection_id}], [{'id': other_collection_id}, {'id': mock_collection_id}] + + raise NotImplementedError("Forgot to implement {}".format(request.param)) + + +@pytest.fixture(params=('empty', 'only_removed', 'only_other', 'other_and_removed')) +def test_collections_for_removal(mock_collection_id, request): + """Fixture returning a tuple of the expected collections values before and after removal""" + other_collection_id = mock_collection_id + '2' + if request.param == 'empty': + return [], [] + elif request.param == 'only_removed': + return [{'id': mock_collection_id}], [] + elif request.param == 'only_other': + return [{'id': other_collection_id}], [{'id': other_collection_id}] + elif request.param == 'other_and_removed': + return [{'id': mock_collection_id}, {'id': other_collection_id}], [{'id': other_collection_id}] + + raise NotImplementedError("Forgot to implement {}".format(request.param)) + + +@pytest.mark.parametrize('params, expected_data', [ + ({}, {}), + ({'name': 'New name.pdf'}, {'name': 'New name.pdf'}) +]) +def test_copy_base_item(test_base_item_and_response, mock_box_session, test_folder, mock_object_id, params, expected_data): + # pylint:disable=redefined-outer-name, protected-access + test_item, mock_item_response = test_base_item_and_response + expected_url = test_item.get_url('copy') + expected_body = { + 'parent': {'id': mock_object_id}, + } + expected_body.update(expected_data) + mock_box_session.post.return_value = mock_item_response + copy_response = test_item.copy(test_folder, **params) + mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) + assert isinstance(copy_response, test_item.__class__) + + +@pytest.mark.parametrize( + 'params, expected_data', [ + ({}, {}), + ({'name': 'New name.pdf'}, {'name': 'New name.pdf'}) + ] +) +def test_move_base_item(test_base_item_and_response, mock_box_session, test_folder, mock_object_id, params, expected_data): + # pylint:disable=redefined-outer-name, protected-access + test_item, mock_item_response = test_base_item_and_response + expected_url = test_item.get_url() + expected_body = { + 'parent': {'id': mock_object_id}, + } + expected_body.update(expected_data) + mock_box_session.put.return_value = mock_item_response + move_response = test_item.move(test_folder, **params) + mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_body), params=None, headers=None) + assert isinstance(move_response, test_item.__class__) + + +def test_rename_base_item(test_base_item_and_response, mock_box_session): + # pylint:disable=redefined-outer-name, protected-access + test_item, mock_item_response = test_base_item_and_response + expected_url = test_item.get_url() + mock_box_session.put.return_value = mock_item_response + rename_response = test_item.rename('new name') + mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps({'name': 'new name'}), params=None, headers=None) + assert isinstance(rename_response, test_item.__class__) + + +def test_add_to_collection(test_base_item_and_response, mock_box_session, mock_collection, test_collections_for_addition): + # pylint:disable=redefined-outer-name, protected-access + test_item, mock_item_response = test_base_item_and_response + current_collections, expected_collections = test_collections_for_addition + expected_url = test_item.get_url() + expected_params = {'fields': 'collections'} + expected_data = { + 'collections': expected_collections + } + mock_response = { + 'type': test_item.object_type, + 'id': test_item.object_id, + 'collections': current_collections, + } + mock_box_session.get.return_value.json.return_value = mock_response + mock_box_session.put.return_value = mock_item_response + + test_item.add_to_collection(mock_collection) + + mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=expected_params) + mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_data), headers=None, params=None) + + +def test_remove_from_collection(test_base_item_and_response, mock_box_session, mock_collection, test_collections_for_removal): + # pylint:disable=redefined-outer-name, protected-access + test_item, mock_item_response = test_base_item_and_response + current_collections, expected_collections = test_collections_for_removal + expected_url = test_item.get_url() + expected_params = {'fields': 'collections'} + expected_data = { + 'collections': expected_collections + } + mock_response = { + 'type': test_item.object_type, + 'id': test_item.object_id, + 'collections': current_collections, + } + mock_box_session.get.return_value.json.return_value = mock_response + mock_box_session.put.return_value = mock_item_response + + test_item.remove_from_collection(mock_collection) + + mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=expected_params) + mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_data), headers=None, params=None) diff --git a/test/unit/object/test_item.py b/test/unit/object/test_item.py index 04390a87..6bf364b4 100644 --- a/test/unit/object/test_item.py +++ b/test/unit/object/test_item.py @@ -20,38 +20,6 @@ def test_item_and_response(test_file, test_folder, mock_file_response, mock_fold return test_folder, mock_folder_response -@pytest.fixture(params=('empty', 'same', 'other')) -def test_collections_for_addition(mock_collection_id, request): - """Fixture returning a tuple of the expected collections values before and after addition""" - other_collection_id = mock_collection_id + '2' - if request.param == 'empty': - return [], [{'id': mock_collection_id}] - elif request.param == 'same': - # Adding a second instance of the same collection is handled correctly by the API, - # so for simplicity we do not check for an existing copy of the collection and just append - return [{'id': mock_collection_id}], [{'id': mock_collection_id}, {'id': mock_collection_id}] - elif request.param == 'other': - return [{'id': other_collection_id}], [{'id': other_collection_id}, {'id': mock_collection_id}] - - raise NotImplementedError("Forgot to implement {}".format(request.param)) - - -@pytest.fixture(params=('empty', 'only_removed', 'only_other', 'other_and_removed')) -def test_collections_for_removal(mock_collection_id, request): - """Fixture returning a tuple of the expected collections values before and after removal""" - other_collection_id = mock_collection_id + '2' - if request.param == 'empty': - return [], [] - elif request.param == 'only_removed': - return [{'id': mock_collection_id}], [] - elif request.param == 'only_other': - return [{'id': other_collection_id}], [{'id': other_collection_id}] - elif request.param == 'other_and_removed': - return [{'id': mock_collection_id}, {'id': other_collection_id}], [{'id': other_collection_id}] - - raise NotImplementedError("Forgot to implement {}".format(request.param)) - - def test_update_info(test_item_and_response, mock_box_session, etag, if_match_header): # pylint:disable=redefined-outer-name, protected-access test_item, mock_item_response = test_item_and_response @@ -79,52 +47,6 @@ def test_update_info_with_default_request_kwargs(test_item_and_response, mock_bo assert update_response.object_id == test_item.object_id -def test_rename_item(test_item_and_response, mock_box_session): - # pylint:disable=redefined-outer-name, protected-access - test_item, mock_item_response = test_item_and_response - expected_url = test_item.get_url() - mock_box_session.put.return_value = mock_item_response - rename_response = test_item.rename('new name') - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps({'name': 'new name'}), params=None, headers=None) - assert isinstance(rename_response, test_item.__class__) - - -@pytest.mark.parametrize('params, expected_data', [ - ({}, {}), - ({'name': 'New name.pdf'}, {'name': 'New name.pdf'}) -]) -def test_copy_item(test_item_and_response, mock_box_session, test_folder, mock_object_id, params, expected_data): - # pylint:disable=redefined-outer-name, protected-access - test_item, mock_item_response = test_item_and_response - expected_url = test_item.get_url('copy') - expected_body = { - 'parent': {'id': mock_object_id}, - } - expected_body.update(expected_data) - mock_box_session.post.return_value = mock_item_response - copy_response = test_item.copy(test_folder, **params) - mock_box_session.post.assert_called_once_with(expected_url, data=json.dumps(expected_body)) - assert isinstance(copy_response, test_item.__class__) - - -@pytest.mark.parametrize('params, expected_data', [ - ({}, {}), - ({'name': 'New name.pdf'}, {'name': 'New name.pdf'}) -]) -def test_move_item(test_item_and_response, mock_box_session, test_folder, mock_object_id, params, expected_data): - # pylint:disable=redefined-outer-name, protected-access - test_item, mock_item_response = test_item_and_response - expected_url = test_item.get_url() - expected_body = { - 'parent': {'id': mock_object_id}, - } - expected_body.update(expected_data) - mock_box_session.put.return_value = mock_item_response - move_response = test_item.move(test_folder, **params) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_body), params=None, headers=None) - assert isinstance(move_response, test_item.__class__) - - def test_get_shared_link( test_item_and_response, mock_box_session, @@ -240,52 +162,6 @@ def test_get(test_item_and_response, mock_box_session, fields, mock_object_id, e assert info.id == mock_object_id -def test_add_to_collection(test_item_and_response, mock_box_session, mock_collection, test_collections_for_addition): - # pylint:disable=redefined-outer-name, protected-access - test_item, mock_item_response = test_item_and_response - current_collections, expected_collections = test_collections_for_addition - expected_url = test_item.get_url() - expected_params = {'fields': 'collections'} - expected_data = { - 'collections': expected_collections - } - mock_response = { - 'type': test_item.object_type, - 'id': test_item.object_id, - 'collections': current_collections, - } - mock_box_session.get.return_value.json.return_value = mock_response - mock_box_session.put.return_value = mock_item_response - - test_item.add_to_collection(mock_collection) - - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=expected_params) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_data), headers=None, params=None) - - -def test_remove_from_collection(test_item_and_response, mock_box_session, mock_collection, test_collections_for_removal): - # pylint:disable=redefined-outer-name, protected-access - test_item, mock_item_response = test_item_and_response - current_collections, expected_collections = test_collections_for_removal - expected_url = test_item.get_url() - expected_params = {'fields': 'collections'} - expected_data = { - 'collections': expected_collections - } - mock_response = { - 'type': test_item.object_type, - 'id': test_item.object_id, - 'collections': current_collections, - } - mock_box_session.get.return_value.json.return_value = mock_response - mock_box_session.put.return_value = mock_item_response - - test_item.remove_from_collection(mock_collection) - - mock_box_session.get.assert_called_once_with(expected_url, headers=None, params=expected_params) - mock_box_session.put.assert_called_once_with(expected_url, data=json.dumps(expected_data), headers=None, params=None) - - def test_get_watermark(test_item_and_response, mock_box_session): test_item, _ = test_item_and_response created_at = '2016-10-31T15:33:33-07:00' diff --git a/test/unit/object/test_web_link.py b/test/unit/object/test_web_link.py index 776ec85f..7e57f4f2 100644 --- a/test/unit/object/test_web_link.py +++ b/test/unit/object/test_web_link.py @@ -5,6 +5,7 @@ from boxsdk.config import API from boxsdk.object.web_link import WebLink +from boxsdk.util.default_arg_value import SDK_VALUE_NOT_SET def test_get(mock_box_session, test_web_link): @@ -53,3 +54,89 @@ def test_delete(mock_box_session, test_web_link): 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={}) + + +def test_get_shared_link( + test_web_link, + mock_box_session, + shared_link_access, + shared_link_unshared_at, + shared_link_password, + shared_link_vanity_name, + test_url, +): + # pylint:disable=redefined-outer-name, protected-access + expected_url = test_web_link.get_url() + mock_box_session.put.return_value.json.return_value = { + 'type': test_web_link.object_type, + 'id': test_web_link.object_id, + 'shared_link': { + '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 SDK_VALUE_NOT_SET: + expected_data['shared_link']['unshared_at'] = shared_link_unshared_at + if shared_link_password is not None: + expected_data['shared_link']['password'] = shared_link_password + if shared_link_vanity_name is not None: + expected_data['shared_link']['vanity_name'] = shared_link_vanity_name + + url = test_web_link.get_shared_link( + access=shared_link_access, + unshared_at=shared_link_unshared_at, + password=shared_link_password, + vanity_name=shared_link_vanity_name, + ) + mock_box_session.put.assert_called_once_with( + expected_url, + data=json.dumps(expected_data), + headers=None, + params=None, + ) + assert url == test_url + + +def test_clear_unshared_at_for_shared_link( + test_web_link, + mock_box_session, + test_url, +): + expected_url = test_web_link.get_url() + mock_box_session.put.return_value.json.return_value = { + 'type': test_web_link.object_type, + 'id': test_web_link.object_id, + 'shared_link': { + 'url': test_url, + 'unshared_at': None, + }, + } + expected_data = {'shared_link': {'unshared_at': None, }, } + shared_link = test_web_link.get_shared_link(unshared_at=None) + mock_box_session.put.assert_called_once_with( + expected_url, + data=json.dumps(expected_data), + headers=None, + params=None, + ) + assert shared_link is test_url + + +def test_remove_shared_link(test_web_link, mock_box_session): + # pylint:disable=redefined-outer-name, protected-access + expected_url = test_web_link.get_url() + mock_box_session.put.return_value.json.return_value = { + 'type': test_web_link.object_type, + 'id': test_web_link.object_id, + 'shared_link': None, + } + removed = test_web_link.remove_shared_link() + mock_box_session.put.assert_called_once_with( + expected_url, + data=json.dumps({'shared_link': None}), + headers=None, + params=None, + ) + assert removed is True