Skip to content

Commit 9f83c61

Browse files
committed
Exceptions now expose X-Dropbox-Request-Id header.
1 parent 002e6df commit 9f83c61

File tree

5 files changed

+44
-34
lines changed

5 files changed

+44
-34
lines changed

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@
5959
# built documents.
6060
#
6161
# The short X.Y version.
62-
version = '4.0'
62+
version = '4.0.1'
6363
# The full version, including alpha/beta/rc tags.
64-
release = '4.0'
64+
release = '4.0.1'
6565

6666
# The language for content autogenerated by Sphinx. Refer to documentation
6767
# for a list of supported languages.

dropbox/dropbox.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
]
44

55
# TODO(kelkabany): We need to auto populate this as done in the v1 SDK.
6-
__version__ = '4.0'
6+
__version__ = '4.0.1'
77

88
import contextlib
99
import json
@@ -49,11 +49,14 @@ def __init__(self, obj_result, http_resp=None):
4949
class RouteErrorResult(object):
5050
"""The error result of a call to a route."""
5151

52-
def __init__(self, obj_result):
52+
def __init__(self, request_id, obj_result):
5353
"""
54+
:param str request_id: A request_id can be shared with Dropbox Support
55+
to pinpoint the exact request that returns an error.
5456
:param str obj_result: The result of a route not including the binary
5557
payload portion, if one exists.
5658
"""
59+
self.request_id = request_id
5760
self.obj_result = obj_result
5861

5962
class Dropbox(DropboxBase):
@@ -192,7 +195,8 @@ def request(self,
192195
returned_data_type, obj, strict=False)
193196

194197
if isinstance(res, RouteErrorResult):
195-
raise ApiError(deserialized_result,
198+
raise ApiError(res.request_id,
199+
deserialized_result,
196200
user_message_text,
197201
user_message_locale)
198202
elif route_style == self.ROUTE_STYLE_DOWNLOAD:
@@ -316,18 +320,19 @@ def request_json_string(self,
316320
verify=True,
317321
)
318322

323+
request_id = r.headers.get('x-dropbox-request-id')
319324
if r.status_code >= 500:
320-
raise InternalServerError(r.status_code, r.text)
325+
raise InternalServerError(request_id, r.status_code, r.text)
321326
elif r.status_code == 400:
322-
raise BadInputError(r.text)
327+
raise BadInputError(request_id, r.text)
323328
elif r.status_code == 401:
324329
assert r.headers.get('content-type') == 'application/json', (
325330
'Expected content-type to be application/json, got %r' %
326331
r.headers.get('content-type'))
327-
raise AuthError(r.json())
332+
raise AuthError(request_id, r.json())
328333
elif r.status_code == 429:
329334
# TODO(kelkabany): Use backoff if provided in response.
330-
raise RateLimitError()
335+
raise RateLimitError(request_id)
331336
elif 200 <= r.status_code <= 299:
332337
if route_style == self.ROUTE_STYLE_DOWNLOAD:
333338
raw_resp = r.headers['dropbox-api-result']
@@ -342,9 +347,9 @@ def request_json_string(self,
342347
return RouteResult(raw_resp)
343348
elif r.status_code in (403, 404, 409):
344349
raw_resp = r.content.decode('utf-8')
345-
return RouteErrorResult(raw_resp)
350+
return RouteErrorResult(request_id, raw_resp)
346351
else:
347-
raise HttpError(r.status_code, r.text)
352+
raise HttpError(request_id, r.status_code, r.text)
348353

349354
def _get_route_url(self, hostname, route_name):
350355
"""Returns the URL of the route.

dropbox/exceptions.py

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,84 @@
11
class DropboxException(Exception):
22
"""All errors related to making an API request extend this."""
3-
pass
3+
4+
def __init__(self, request_id, *args, **kwargs):
5+
# A request_id can be shared with Dropbox Support to pinpoint the exact
6+
# request that returns an error.
7+
super(DropboxException, self).__init__(request_id, *args, **kwargs)
8+
self.request_id = request_id
49

510

611
class ApiError(DropboxException):
712
"""Errors produced by the Dropbox API."""
813

9-
def __init__(self, error, user_message_text, user_message_locale):
14+
def __init__(self, request_id, error, user_message_text, user_message_locale):
1015
"""
16+
:param (str) request_id: A request_id can be shared with Dropbox
17+
Support to pinpoint the exact request that returns an error.
1118
:param error: An instance of the error data type for the route.
1219
:param (str) user_message_text: A human-readable message that can be
1320
displayed to the end user. Is None, if unavailable.
1421
:param (str) user_message_locale: The locale of ``user_message_text``,
1522
if present.
1623
"""
17-
super(ApiError, self).__init__(error)
24+
super(ApiError, self).__init__(request_id, error)
1825
self.error = error
1926
self.user_message_text = user_message_text
2027
self.user_message_locale = user_message_locale
2128

2229
def __repr__(self):
23-
return 'ApiError({})'.format(self.error)
30+
return 'ApiError({!r}, {})'.format(self.request_id, self.error)
2431

2532

2633
class HttpError(DropboxException):
2734
"""Errors produced at the HTTP layer."""
2835

29-
def __init__(self, status_code, body):
30-
super(HttpError, self).__init__(status_code, body)
36+
def __init__(self, request_id, status_code, body):
37+
super(HttpError, self).__init__(request_id, status_code, body)
3138
self.status_code = status_code
3239
self.body = body
3340

3441
def __repr__(self):
35-
return 'HttpError({}, {!r})'.format(self.status_code, self.body)
42+
return 'HttpError({!r}, {}, {!r})'.format(
43+
self.request_id, self.status_code, self.body)
3644

3745

3846
class BadInputError(HttpError):
3947
"""Errors due to bad input parameters to an API Operation."""
4048

41-
def __init__(self, message):
42-
super(BadInputError, self).__init__(400, message)
49+
def __init__(self, request_id, message):
50+
super(BadInputError, self).__init__(request_id, 400, message)
4351
self.message = message
4452

4553
def __repr__(self):
46-
return 'BadInputError({!r})'.format(self.message)
54+
return 'BadInputError({!r}, {!r})'.format(self.request_id, self.message)
4755

4856

4957
class AuthError(HttpError):
5058
"""Errors due to invalid authentication credentials."""
5159

52-
def __init__(self, error):
53-
super(AuthError, self).__init__(401, None)
60+
def __init__(self, request_id, error):
61+
super(AuthError, self).__init__(request_id, 401, None)
5462
self.error = error
5563

5664
def __repr__(self):
57-
return 'AuthError({!r})'.format(self.error)
65+
return 'AuthError({!r}, {!r})'.format(self.request_id, self.error)
5866

5967

6068
class RateLimitError(HttpError):
6169
"""Error caused by rate limiting."""
6270

63-
def __init__(self, backoff=None):
64-
super(RateLimitError, self).__init__(429, None)
71+
def __init__(self, request_id, backoff=None):
72+
super(RateLimitError, self).__init__(request_id, 429, None)
6573
self.backoff = backoff
6674

6775
def __repr__(self):
68-
return 'RateLimitError({!r})'.format(self.backoff)
76+
return 'RateLimitError({!r}, {!r})'.format(self.request_id, self.backoff)
6977

7078

7179
class InternalServerError(HttpError):
7280
"""Errors due to a problem on Dropbox."""
7381

74-
def __init__(self, status_code, message):
75-
self.status_code = status_code
76-
self.message = message
77-
7882
def __repr__(self):
79-
return 'InternalServerError({}, {!r})'.format(self.status_code, self.message)
83+
return 'InternalServerError({!r}, {}, {!r})'.format(
84+
self.request_id, self.status_code, self.body)

dropbox/rest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
else:
3232
url_encode = urllib.urlencode
3333

34-
SDK_VERSION = "4.0"
34+
SDK_VERSION = "4.0.1"
3535

3636
TRUSTED_CERT_FILE = pkg_resources.resource_filename(__name__, 'trusted-certs.crt')
3737

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
dist = setup(
2727
name='dropbox',
28-
version='4.0',
28+
version='4.0.1',
2929
description='Official Dropbox API Client',
3030
author='Dropbox',
3131
author_email='[email protected]',

0 commit comments

Comments
 (0)