Skip to content

Commit 7160ac8

Browse files
committed
fix: Changed some functions signarure to fix liskov substitution violations
BREAKING CHANGE: Changed function signatures: - `get_url()` in `base_endpoint.py` - `copy()` in 'base_item.py' 'file.py' - `create_shared_link()` in 'base_item.py', 'item.py' - `get_shared_link()` in 'base_item.py', 'item.py' - `remove_shared_link()` in 'base_item.py', 'item.py' - `get()` in `base_object.py`, 'item.py' - `update_info()` in `base_object.py`, `collaboration.py`, 'item.py', `metadata_template.py` - `delete()` in `base_object.py`, `folder.py`, 'item.py', `user.py` Closes: SDK-1904
1 parent e8abbb5 commit 7160ac8

16 files changed

+246
-118
lines changed

boxsdk/object/base_endpoint.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,14 @@ def translator(self) -> 'Translator':
3434
"""
3535
return self._session.translator
3636

37-
def get_url(self, endpoint: str, *args: Any) -> str:
37+
def get_url(self, *args: Any) -> str:
3838
"""
3939
Return the URL used to access the endpoint.
4040
41-
:param endpoint:
42-
The name of the endpoint.
4341
:param args:
44-
Additional parts of the endpoint URL.
42+
Parts of the endpoint URL.
4543
"""
46-
# pylint:disable=no-self-use
47-
return self._session.get_url(endpoint, *args)
44+
return self._session.get_url(*args)
4845

4946
def clone(self, session: 'Session' = None) -> 'BaseEndpoint':
5047
"""

boxsdk/object/base_item.py

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22

3-
from typing import TYPE_CHECKING, Any, Union
3+
from typing import TYPE_CHECKING, Any, Union, Optional
44

55
from .base_object import BaseObject
66
from ..exception import BoxValueError
@@ -15,7 +15,7 @@
1515
class BaseItem(BaseObject):
1616

1717
@api_call
18-
def copy(self, parent_folder: 'Folder', name: str = None) -> 'BaseItem':
18+
def copy(self, parent_folder: 'Folder', name: str = None, **_kwargs) -> 'BaseItem':
1919
"""Copy the item to the given folder.
2020
2121
:param parent_folder:
@@ -68,23 +68,50 @@ def rename(self, name: str) -> 'BaseItem':
6868
return self.update_info(data)
6969

7070
@api_call
71-
def create_shared_link(self, **kwargs: Any) -> Any:
71+
def create_shared_link(
72+
self,
73+
access: Optional[str] = None,
74+
unshared_at: Optional[str] = SDK_VALUE_NOT_SET,
75+
password: Optional[str] = None,
76+
vanity_name: Optional[str] = None,
77+
**kwargs: Any) -> Any:
7278
"""
7379
Create a shared link for the item with the given access permissions.
7480
81+
:param access:
82+
Determines who can access the shared link. May be open, company, or collaborators. If no access is
83+
specified, the default access will be used.
84+
:param unshared_at:
85+
The date on which this link should be disabled. May only be set if the current user is not a free user
86+
and has permission to set expiration dates. Takes an RFC3339-formatted string, e.g.
87+
'2018-10-31T23:59:59-07:00' for 11:59:59 PM on October 31, 2018 in the America/Los_Angeles timezone.
88+
The time portion can be omitted, which defaults to midnight (00:00:00) on that date.
89+
:param password:
90+
The password required to view this link. If no password is specified then no password will be set.
91+
Please notice that this is a premium feature, which might not be available to your app.
92+
:param vanity_name:
93+
Defines a custom vanity name to use in the shared link URL, eg. https://app.box.com/v/my-custom-vanity-name.
94+
If this parameter is None, the standard shared link URL will be used.
7595
:param kwargs:
7696
Keyword arguments passed from overriding method used to build request properties.
7797
:return:
7898
The updated object with shared link.
7999
Returns a new object of the same type, without modifying the original object passed as self.
80100
"""
101+
81102
shared_link = {}
82103

83-
if kwargs.get('access') is not None:
84-
shared_link['access'] = kwargs.get('access')
104+
if access is not None:
105+
shared_link['access'] = access
106+
107+
if unshared_at is not SDK_VALUE_NOT_SET:
108+
shared_link['unshared_at'] = unshared_at
109+
110+
if password is not None:
111+
shared_link['password'] = password
85112

86-
if kwargs.get('unshared_at') is not SDK_VALUE_NOT_SET:
87-
shared_link['unshared_at'] = kwargs.get('unshared_at')
113+
if vanity_name is not None:
114+
shared_link['vanity_name'] = vanity_name
88115

89116
if kwargs.get('allow_download') is not None or kwargs.get('allow_preview') is not None:
90117
shared_link['permissions'] = {}
@@ -93,29 +120,45 @@ def create_shared_link(self, **kwargs: Any) -> Any:
93120
if kwargs.get('allow_preview') is not None:
94121
shared_link['permissions']['can_preview'] = kwargs.get('allow_preview')
95122

96-
if kwargs.get('password') is not None:
97-
shared_link['password'] = kwargs.get('password')
98-
99-
if kwargs.get('vanity_name') is not None:
100-
shared_link['vanity_name'] = kwargs.get('vanity_name')
101-
102123
data = {'shared_link': shared_link}
103124
update_info_kwargs = {'etag': kwargs.get('etag')} if kwargs.get('etag') is not None else {}
104125

105126
return self.update_info(data, **update_info_kwargs)
106127

107128
@api_call
108-
def get_shared_link(self, **kwargs: Any) -> str:
129+
def get_shared_link(
130+
self,
131+
access: Optional[str] = None,
132+
unshared_at: Optional[str] = SDK_VALUE_NOT_SET,
133+
password: Optional[str] = None,
134+
vanity_name: Optional[str] = None,
135+
**kwargs: Any) -> str:
109136
"""
110137
Get a shared link for the item with the given access permissions.
111138
This url leads to a Box.com shared link page, where the item can be previewed, downloaded, etc.
112139
140+
:param access:
141+
Determines who can access the shared link. May be open, company, or collaborators. If no access is
142+
specified, the default access will be used.
143+
:param unshared_at:
144+
The date on which this link should be disabled. May only be set if the current user is not a free user
145+
and has permission to set expiration dates. Takes an RFC3339-formatted string, e.g.
146+
'2018-10-31T23:59:59-07:00' for 11:59:59 PM on October 31, 2018 in the America/Los_Angeles timezone.
147+
The time portion can be omitted, which defaults to midnight (00:00:00) on that date.
148+
:param password:
149+
The password required to view this link. If no password is specified then no password will be set.
150+
Please notice that this is a premium feature, which might not be available to your app.
151+
:param vanity_name:
152+
Defines a custom vanity name to use in the shared link URL, eg. https://app.box.com/v/my-custom-vanity-name.
153+
If this parameter is None, the standard shared link URL will be used.
113154
:param kwargs:
114155
Keyword arguments passed from overriding method used to create a new shared link.
115156
:returns:
116157
The URL of the shared link.
117158
"""
118-
item = self.create_shared_link(**kwargs)
159+
item = self.create_shared_link(
160+
access=access, unshared_at=unshared_at, password=password, vanity_name=vanity_name, **kwargs
161+
)
119162
return item.shared_link['url'] # pylint:disable=no-member
120163

121164
@api_call

boxsdk/object/base_object.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# coding: utf-8
22

33
import json
4-
from typing import TYPE_CHECKING, Any, Iterable, Optional
4+
from typing import TYPE_CHECKING, Any, Iterable, Optional, Union, List
55

66
from .base_endpoint import BaseEndpoint
77
from .base_api_json_object import BaseAPIJSONObject
@@ -40,7 +40,6 @@ def get_url(self, *args: Any) -> str:
4040
Base class override.
4141
Return the given object's URL, appending any optional parts as specified by args.
4242
"""
43-
# pylint:disable=arguments-differ
4443
return super().get_url(f'{self._item_type}s', self._object_id, *args)
4544

4645
def get_type_url(self) -> str:
@@ -57,7 +56,7 @@ def object_id(self) -> str:
5756
return self._object_id
5857

5958
@api_call
60-
def get(self, fields: Iterable[str] = None, headers: dict = None) -> Any:
59+
def get(self, fields: Iterable[str] = None, headers: dict = None, **_kwargs) -> Any:
6160
"""
6261
Get information about the object, specified by fields. If fields is None, return the default fields.
6362
@@ -79,7 +78,7 @@ def get(self, fields: Iterable[str] = None, headers: dict = None) -> Any:
7978
@api_call
8079
def update_info(
8180
self,
82-
data: dict,
81+
data: Union[dict, List[dict]],
8382
params: Optional[dict] = None,
8483
headers: Optional[dict] = None,
8584
**kwargs: Any
@@ -109,19 +108,18 @@ def update_info(
109108
Construct the new object with all the default attributes that are
110109
returned from the endpoint.
111110
"""
112-
# pylint:disable=no-else-return
113111
url = self.get_url()
114112
box_response = self._session.put(url, data=json.dumps(data), params=params, headers=headers, **kwargs)
115113
if 'expect_json_response' in kwargs and not kwargs['expect_json_response']:
116114
return box_response.ok
117-
else:
118-
return self.translator.translate(
119-
session=self._session,
120-
response_object=box_response.json(),
121-
)
115+
116+
return self.translator.translate(
117+
session=self._session,
118+
response_object=box_response.json(),
119+
)
122120

123121
@api_call
124-
def delete(self, params: Optional[dict] = None, headers: Optional[dict] = None) -> bool:
122+
def delete(self, params: Optional[dict] = None, headers: Optional[dict] = None, **_kwargs) -> bool:
125123
""" Delete the object.
126124
127125
:param params:
@@ -134,8 +132,7 @@ def delete(self, params: Optional[dict] = None, headers: Optional[dict] = None)
134132
:class:`BoxAPIException` in case of unexpected errors.
135133
"""
136134
url = self.get_url()
137-
# ??? There's a question about why params forces a default to {}, while headers doesn't. Looking for
138-
# confirmation that the below is correct.
135+
139136
box_response = self._session.delete(url, expect_json_response=False, params=params or {}, headers=headers)
140137
return box_response.ok
141138

boxsdk/object/collaboration.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# coding: utf-8
2-
from typing import Optional
2+
from typing import Optional, Any
33

44
from boxsdk.object.base_object import BaseObject
55
from boxsdk.util.text_enum import TextEnum
@@ -34,11 +34,27 @@ class Collaboration(BaseObject):
3434
@api_call
3535
def update_info(
3636
self,
37+
data: Optional[dict] = None,
38+
params: Optional[dict] = None,
39+
headers: Optional[dict] = None,
40+
*,
3741
role: Optional[CollaborationRole] = None,
38-
status: Optional[CollaborationStatus] = None
42+
status: Optional[CollaborationStatus] = None,
43+
**kwargs: Any
3944
) -> 'BaseObject':
40-
"""Edit an existing collaboration on Box
45+
"""
4146
47+
:param data:
48+
(optional) The updated information about this object.
49+
Must be JSON serializable.
50+
Update the object attributes in data.keys(). The semantics of the
51+
values depends on the the type and attributes of the object being
52+
updated. For details on particular semantics, refer to the Box
53+
developer API documentation <https://developer.box.com/>.
54+
:param params:
55+
(optional) Query string parameters for the request.
56+
:param headers:
57+
(optional) Extra HTTP headers for the request.
4258
:param role:
4359
The new role for this collaboration or None to leave unchanged
4460
:param status:
@@ -49,16 +65,17 @@ def update_info(
4965
:raises:
5066
:class:`BoxAPIException` if current user doesn't have permissions to edit the collaboration.
5167
"""
52-
# pylint:disable=arguments-differ
53-
data = {}
68+
# pylint: disable=arguments-differ
69+
data_to_update = data if data else {}
5470
if role:
55-
data['role'] = role
71+
data_to_update['role'] = role
5672
if status:
57-
data['status'] = status
73+
data_to_update['status'] = status
74+
5875
if role == CollaborationRole.OWNER:
59-
return super().update_info(data=data, expect_json_response=False)
76+
return super().update_info(data=data_to_update, params=params, headers=headers, expect_json_response=False, **kwargs)
6077

61-
return super().update_info(data=data)
78+
return super().update_info(data=data_to_update, params=params, headers=headers, **kwargs)
6279

6380
@api_call
6481
def accept(self) -> 'BaseObject':

boxsdk/object/events.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ class Events(BaseEndpoint):
6060

6161
def get_url(self, *args: Any) -> str:
6262
"""Base class override."""
63-
# pylint:disable=arguments-differ
6463
return super().get_url('events', *args)
6564

6665
@api_call

boxsdk/object/file.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,8 +681,14 @@ def get_thumbnail_representation(self, dimensions: str, extension: str = 'png')
681681
return b''
682682

683683
@api_call
684-
def copy(self, parent_folder: 'Folder', name: Optional[str] = None, file_version: 'FileVersion' = None) -> 'File':
685-
# pylint: disable=arguments-differ
684+
def copy(
685+
self,
686+
parent_folder: 'Folder',
687+
name: Optional[str] = None,
688+
*,
689+
file_version: 'FileVersion' = None,
690+
**_kwargs
691+
) -> 'File':
686692
"""Copy the item to the given folder.
687693
688694
:param parent_folder:
@@ -694,6 +700,7 @@ def copy(self, parent_folder: 'Folder', name: Optional[str] = None, file_version
694700
:returns:
695701
The copy of the file
696702
"""
703+
# pylint: disable=arguments-differ
697704
url = self.get_url('copy')
698705
data = {
699706
'parent': {'id': parent_folder.object_id}

boxsdk/object/folder.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,20 @@ def create_web_link(
503503
)
504504

505505
@api_call
506-
def delete(self, recursive: bool = True, etag: Optional[str] = None) -> bool:
506+
def delete(
507+
self,
508+
params: Optional[dict] = None,
509+
headers: Optional[dict] = None,
510+
*,
511+
recursive: bool = True,
512+
etag: Optional[str] = None,
513+
**kwargs
514+
) -> bool:
507515
"""Base class override. Delete the folder.
508-
516+
:param params:
517+
Additional parameters to send with the request.
518+
:param headers:
519+
Additional headers to send with the request.
509520
:param recursive:
510521
Whether or not the folder should be deleted if it isn't empty.
511522
:param etag:
@@ -514,8 +525,11 @@ def delete(self, recursive: bool = True, etag: Optional[str] = None) -> bool:
514525
Whether or not the update was successful.
515526
:raises: :class:`BoxAPIException` if the specified etag doesn't match the latest version of the folder.
516527
"""
517-
# pylint:disable=arguments-differ,arguments-renamed
518-
return super().delete({'recursive': recursive}, etag)
528+
# pylint: disable=arguments-differ
529+
params_to_send = params if params else {}
530+
params_to_send.update({'recursive': recursive})
531+
532+
return super().delete(params=params_to_send, headers=headers, etag=etag, **kwargs)
519533

520534
@api_call
521535
def get_metadata_cascade_policies(

0 commit comments

Comments
 (0)