Skip to content

Commit 6199de8

Browse files
kelseymorris95jmoldow
authored andcommitted
Add Cloneable interface and @api_call (#154)
New `@api_call` decorator can be used to allow methods that make an API call to accept an extra parameter `extra_network_parameters`, to be used for sending low level request parameters to Requests or whatever network layer is being used. When `extra_network_parameters` is provided, the underlying `BoxSession` is cloned, and the `extra_network_parameters` are attached to the new one. Decorate all existing API call methods with `@api_call`.
1 parent 30d82aa commit 6199de8

File tree

18 files changed

+296
-90
lines changed

18 files changed

+296
-90
lines changed

boxsdk/client/client.py

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
from ..config import API
77
from ..session.box_session import BoxSession
88
from ..network.default_network import DefaultNetwork
9+
from ..object.cloneable import Cloneable
10+
from ..util.api_call_decorator import api_call
911
from ..object.search import Search
1012
from ..object.events import Events
1113
from ..util.shared_link import get_shared_link_header
1214
from ..util.translator import Translator
1315

1416

15-
class Client(object):
17+
class Client(Cloneable):
1618

1719
def __init__(
1820
self,
@@ -34,6 +36,7 @@ def __init__(
3436
:type session:
3537
:class:`BoxSession`
3638
"""
39+
super(Client, self).__init__()
3740
network_layer = network_layer or DefaultNetwork()
3841
self._oauth = oauth
3942
self._network = network_layer
@@ -49,6 +52,16 @@ def auth(self):
4952
"""
5053
return self._oauth
5154

55+
@property
56+
def session(self):
57+
"""
58+
Get the :class:`BoxSession` instance the client is using.
59+
60+
:rtype:
61+
:class:`BoxSession`
62+
"""
63+
return self._session
64+
5265
def folder(self, folder_id):
5366
"""
5467
Initialize a :class:`Folder` object, whose box id is folder_id.
@@ -124,6 +137,7 @@ def collaboration(self, collab_id):
124137
"""
125138
return Translator().translate('collaboration')(session=self._session, object_id=collab_id)
126139

140+
@api_call
127141
def users(self, limit=None, offset=0, filter_term=None):
128142
"""
129143
Get a list of all users for the Enterprise along with their user_id, public_name, and login.
@@ -160,6 +174,7 @@ def users(self, limit=None, offset=0, filter_term=None):
160174
response_object=item,
161175
) for item in response['entries']]
162176

177+
@api_call
163178
def search(
164179
self,
165180
query,
@@ -246,6 +261,7 @@ def group_membership(self, group_membership_id):
246261
object_id=group_membership_id,
247262
)
248263

264+
@api_call
249265
def groups(self):
250266
"""
251267
Get a list of all groups for the current user.
@@ -265,6 +281,7 @@ def groups(self):
265281
response_object=item,
266282
) for item in response['entries']]
267283

284+
@api_call
268285
def create_group(self, name):
269286
"""
270287
Create a group with the given name.
@@ -292,6 +309,7 @@ def create_group(self, name):
292309
response_object=response,
293310
)
294311

312+
@api_call
295313
def get_shared_item(self, shared_link, password=None):
296314
"""
297315
Get information about a Box shared link. https://box-content.readme.io/reference#get-a-shared-item
@@ -322,6 +340,7 @@ def get_shared_item(self, shared_link, password=None):
322340
response_object=response,
323341
)
324342

343+
@api_call
325344
def make_request(self, method, url, **kwargs):
326345
"""
327346
Make an authenticated request to the Box API.
@@ -343,6 +362,7 @@ def make_request(self, method, url, **kwargs):
343362
"""
344363
return self._session.request(method, url, **kwargs)
345364

365+
@api_call
346366
def create_user(self, name, login=None, **user_attributes):
347367
"""
348368
Create a new user. Can only be used if the current user is an enterprise admin, or the current authorization
@@ -375,35 +395,9 @@ def create_user(self, name, login=None, **user_attributes):
375395
response_object=response,
376396
)
377397

378-
def as_user(self, user):
379-
"""
380-
Returns a new client object with default headers set up to make requests as the specified user.
381-
382-
:param user:
383-
The user to impersonate when making API requests.
384-
:type user:
385-
:class:`User`
386-
"""
387-
return self.__class__(self._oauth, self._network, self._session.as_user(user))
388-
389-
def with_shared_link(self, shared_link, shared_link_password):
390-
"""
391-
Returns a new client object with default headers set up to make requests using the shared link for auth.
392-
393-
:param shared_link:
394-
The shared link.
395-
:type shared_link:
396-
`unicode`
397-
:param shared_link_password:
398-
The password for the shared link.
399-
:type shared_link_password:
400-
`unicode`
401-
"""
402-
return self.__class__(
403-
self._oauth,
404-
self._network,
405-
self._session.with_shared_link(shared_link, shared_link_password),
406-
)
398+
def clone(self, session=None):
399+
"""Base class override."""
400+
return self.__class__(self._oauth, self._network, session or self._session)
407401

408402
def get_url(self, endpoint, *args):
409403
"""

boxsdk/object/base_endpoint.py

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
from __future__ import unicode_literals, absolute_import
44

5+
from .cloneable import Cloneable
56

6-
class BaseEndpoint(object):
7+
8+
class BaseEndpoint(Cloneable):
79
"""A Box API endpoint."""
810

911
def __init__(self, session, **kwargs):
@@ -20,6 +22,16 @@ def __init__(self, session, **kwargs):
2022
super(BaseEndpoint, self).__init__(**kwargs)
2123
self._session = session
2224

25+
@property
26+
def session(self):
27+
"""
28+
Get the :class:`BoxSession` instance the object is using.
29+
30+
:rtype:
31+
:class:`BoxSession`
32+
"""
33+
return self._session
34+
2335
def get_url(self, endpoint, *args):
2436
"""
2537
Return the URL used to access the endpoint.
@@ -38,28 +50,13 @@ def get_url(self, endpoint, *args):
3850
# pylint:disable=no-self-use
3951
return self._session.get_url(endpoint, *args)
4052

41-
def as_user(self, user):
42-
"""
43-
Returns a new endpoint object with default headers set up to make requests as the specified user.
44-
45-
:param user:
46-
The user to impersonate when making API requests.
47-
:type user:
48-
:class:`User`
53+
def clone(self, session=None):
4954
"""
50-
return self.__class__(self._session.as_user(user))
55+
Returns a copy of this cloneable object using the specified session.
5156
52-
def with_shared_link(self, shared_link, shared_link_password):
53-
"""
54-
Returns a new endpoint object with default headers set up to make requests using the shared link for auth.
55-
56-
:param shared_link:
57-
The shared link.
58-
:type shared_link:
59-
`unicode`
60-
:param shared_link_password:
61-
The password for the shared link.
62-
:type shared_link_password:
63-
`unicode`
57+
:param session:
58+
The Box session used to make requests.
59+
:type session:
60+
:class:`BoxSession`
6461
"""
65-
return self.__class__(self._session.with_shared_link(shared_link, shared_link_password))
62+
return self.__class__(session or self._session)

boxsdk/object/base_object.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .base_endpoint import BaseEndpoint
77
from .base_api_json_object import BaseAPIJSONObject
88
from ..util.translator import Translator
9+
from ..util.api_call_decorator import api_call
910

1011

1112
class BaseObject(BaseEndpoint, BaseAPIJSONObject):
@@ -62,6 +63,7 @@ def object_id(self):
6263
"""
6364
return self._object_id
6465

66+
@api_call
6567
def get(self, fields=None, headers=None):
6668
"""
6769
Get information about the object, specified by fields. If fields is None, return the default fields.
@@ -84,6 +86,7 @@ def get(self, fields=None, headers=None):
8486
box_response = self._session.get(url, params=params, headers=headers)
8587
return self.__class__(self._session, self._object_id, box_response.json())
8688

89+
@api_call
8790
def update_info(self, data, params=None, headers=None, **kwargs):
8891
"""Update information about this object.
8992
@@ -127,6 +130,7 @@ def update_info(self, data, params=None, headers=None, **kwargs):
127130
response_object=response,
128131
)
129132

133+
@api_call
130134
def delete(self, params=None, headers=None):
131135
""" Delete the object.
132136
@@ -212,14 +216,10 @@ def _paging_wrapper(self, url, starting_index, limit, factory=None):
212216
if current_index >= response['total_count']:
213217
break
214218

215-
def as_user(self, user):
216-
""" Base class override. """
217-
return self.__class__(self._session.as_user(user), self._object_id, self._response_object)
218-
219-
def with_shared_link(self, shared_link, shared_link_password):
220-
""" Base class override. """
219+
def clone(self, session=None):
220+
"""Base class override."""
221221
return self.__class__(
222-
self._session.with_shared_link(shared_link, shared_link_password),
222+
session or self._session,
223223
self._object_id,
224224
self._response_object,
225225
)

boxsdk/object/cloneable.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# coding: utf-8
2+
3+
from __future__ import unicode_literals, absolute_import
4+
5+
6+
class Cloneable(object):
7+
"""
8+
Cloneable interface to be implemented by endpoint objects that should have ability to be cloned, but with a
9+
different session member if desired.
10+
"""
11+
12+
def as_user(self, user):
13+
"""
14+
Returns a new endpoint object with default headers set up to make requests as the specified user.
15+
:param user:
16+
The user to impersonate when making API requests.
17+
:type user:
18+
:class:`User`
19+
"""
20+
return self.clone(self.session.as_user(user))
21+
22+
def with_shared_link(self, shared_link, shared_link_password):
23+
"""
24+
Returns a new endpoint object with default headers set up to make requests using the shared link for auth.
25+
:param shared_link:
26+
The shared link.
27+
:type shared_link:
28+
`unicode`
29+
:param shared_link_password:
30+
The password for the shared link.
31+
:type shared_link_password:
32+
`unicode`
33+
"""
34+
return self.clone(self.session.with_shared_link(shared_link, shared_link_password))
35+
36+
def clone(self, session=None):
37+
"""
38+
Returns a copy of this cloneable object using the specified session.
39+
:param session:
40+
The Box session used to make requests.
41+
:type session:
42+
:class:`BoxSession`
43+
"""
44+
raise NotImplementedError
45+
46+
@property
47+
def session(self):
48+
"""
49+
Return the Box session being used to make requests.
50+
51+
:rtype:
52+
:class:`BoxSession`
53+
"""
54+
raise NotImplementedError

boxsdk/object/collaboration.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# coding: utf-8
22

3-
from __future__ import unicode_literals
3+
from __future__ import unicode_literals, absolute_import
44

55
from boxsdk.object.base_object import BaseObject
66
from boxsdk.util.text_enum import TextEnum
7+
from ..util.api_call_decorator import api_call
78

89

910
class CollaborationRole(TextEnum):
@@ -32,6 +33,7 @@ class Collaboration(BaseObject):
3233
_item_type = 'collaboration'
3334

3435
# pylint:disable=arguments-differ
36+
@api_call
3537
def update_info(self, role=None, status=None):
3638
"""Edit an existing collaboration on Box
3739

boxsdk/object/events.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from six import with_metaclass
66

77
from .base_endpoint import BaseEndpoint
8+
from ..util.api_call_decorator import api_call
89
from ..util.enum import ExtendableEnumMeta
910
from ..util.lru_cache import LRUCache
1011
from ..util.text_enum import TextEnum
@@ -57,6 +58,7 @@ def get_url(self, *args):
5758
"""Base class override."""
5859
return super(Events, self).get_url('events', *args)
5960

61+
@api_call
6062
def get_events(self, limit=100, stream_position=0, stream_type=UserEventsStreamType.ALL):
6163
"""
6264
Get Box events from a given stream position for a given stream type.
@@ -95,6 +97,7 @@ def get_events(self, limit=100, stream_position=0, stream_type=UserEventsStreamT
9597
response['entries'] = [Translator().translate(item['type'])(item) for item in response['entries']]
9698
return response
9799

100+
@api_call
98101
def get_latest_stream_position(self, stream_type=UserEventsStreamType.ALL):
99102
"""
100103
Get the latest stream position. The return value can be used with :meth:`get_events` or
@@ -136,6 +139,7 @@ def _get_all_events_since(self, stream_position, stream_type=UserEventsStreamTyp
136139
if len(events) < 100:
137140
return
138141

142+
@api_call
139143
def long_poll(self, options, stream_position):
140144
"""
141145
Set up a long poll connection at the specified url.
@@ -163,6 +167,7 @@ def long_poll(self, options, stream_position):
163167
)
164168
return long_poll_response
165169

170+
@api_call
166171
def generate_events_with_long_polling(self, stream_position=None, stream_type=UserEventsStreamType.ALL):
167172
"""
168173
Subscribe to events from the given stream position.
@@ -212,6 +217,7 @@ def generate_events_with_long_polling(self, stream_position=None, stream_type=Us
212217
else:
213218
break
214219

220+
@api_call
215221
def get_long_poll_options(self, stream_type=UserEventsStreamType.ALL):
216222
"""
217223
Get the url and retry timeout for setting up a long polling connection.

0 commit comments

Comments
 (0)