Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions zendesk/endpoints_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,15 @@
'method': 'PUT',
},
'make_user_identity_primary': {
'path': '/users/{{user_id}}/identities/{{identity_id}}/make_primary',
'path': '/users/{{user_id}}/identities/{{identity_id}}/make_primary.json',
'method': 'PUT',
},
'verify_user_identity': {
'path': '/users/{{user_id}}/identities/{{identity_id}}/verify',
'path': '/users/{{user_id}}/identities/{{identity_id}}/verify.json',
'method': 'PUT',
},
'request_user_identity_verification': {
'path': '/users/{{user_id}}/identities/{{identity_id}}/request_verification',
'path': '/users/{{user_id}}/identities/{{identity_id}}/request_verification.json',
'method': 'PUT',
},
'delete_user_identity': {
Expand Down Expand Up @@ -351,7 +351,7 @@
# Search
'search': {
'path': '/search.json',
'valid_params': ['query'],
'valid_params': ['query', 'sort_by', 'sort_order'],
'method': 'GET',
},
'anonymous_search': {
Expand Down Expand Up @@ -565,6 +565,7 @@
'upload_attachment': {
'path': '/uploads.json',
'method': 'POST',
'valid_params': ['filename', 'token'],
},

# Job Statuses
Expand Down Expand Up @@ -659,6 +660,6 @@
# Patch mapping table with correct HTTP Status expected
for method, api_map in mapping_table.iteritems():
status = 200
if method.startswith('create_'):
if method.startswith('create_') or method == 'upload_attachment':
status = 201
api_map['status'] = status
84 changes: 52 additions & 32 deletions zendesk/zendesk.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,32 +36,32 @@
from endpoints_v2 import mapping_table as mapping_table_v2

V2_COLLECTION_PARAMS = [
'page',
'per_page',
'sort_order',
]
'page',
'per_page',
'sort_order',
]


class ZendeskError(Exception):
def __init__(self, msg, error_code=None):
def __init__(self, msg, code, response):
self.msg = msg
self.error_code = error_code
# Zendesk will throw a 401 response for un-authneticated call
if self.error_code == 401:
raise AuthenticationError(self.msg)
self.error_code = code
self.response = response

def __str__(self):
return repr('%s: %s' % (self.error_code, self.msg))
return repr('%s: %s %s' % (self.error_code, self.msg, self.response))


class AuthenticationError(ZendeskError):
def __init__(self, msg):
self.msg = msg
pass

def __str__(self):
return repr(self.msg)

class RateLimitError(ZendeskError):
pass

re_identifier = re.compile(r".*/(?P<identifier>\d+)\.(json|xml)")


def get_id_from_url(url):
match = re_identifier.match(url)
if match and match.group('identifier'):
Expand Down Expand Up @@ -130,9 +130,7 @@ def __init__(self, zendesk_url, zendesk_username=None,
elif self.api_version == 2:
self.mapping_table = mapping_table_v2
else:
raise ValueError("Unsupported Zendesk API Version: %d" %
(self.api_version,))

raise ValueError("Unsupported Zendesk API Version: %d" % (self.api_version,))

def __getattr__(self, api_call):
"""
Expand Down Expand Up @@ -174,9 +172,7 @@ def call(self, **kwargs):
# Validate remaining kwargs against valid_params and add
# params url encoded to url variable.
for kw in kwargs:
if (kw not in valid_params and
(self.api_version == 2 and kw not in V2_COLLECTION_PARAMS)
):
if (kw not in valid_params and (self.api_version == 2 and kw not in V2_COLLECTION_PARAMS)):
raise TypeError("%s() got an unexpected keyword argument "
"'%s'" % (api_call, kw))
else:
Expand All @@ -193,14 +189,16 @@ def call(self, **kwargs):
elif "Authorization" in self.headers:
del(self.headers["Authorization"])

# uploading of attachments requires diff mime-type
# see http://developer.zendesk.com/documentation/rest_api/attachments.html

if path == "/api/v2/uploads.json":
self.headers["Content-Type"] = "application/binary"
else:
body = json.dumps(body)

# Make an http request (data replacements are finalized)
response, content = \
self.client.request(
url,
method,
body=json.dumps(body),
headers=self.headers
)
response, content = self.client.request(url, method, body=body, headers=self.headers)
# Use a response handler to determine success/fail
return self._response_handler(response, content, status)

Expand All @@ -213,7 +211,7 @@ def call(self, **kwargs):

@staticmethod
def _response_handler(response, content, status):
"""
"""
Handle response as callback

If the response status is different from status defined in the
Expand All @@ -222,13 +220,35 @@ def _response_handler(response, content, status):
Zendesk's response is sometimes the url of a newly created user/
ticket/group/etc and they pass this through 'location'. Otherwise,
the body of 'content' has our response.

response = {
'status': '429',
'x-zendesk-api-version': 'v2',
'content-location': 'https://takealot1372789905.zendesk.com/api/v2/search.json?query=twitter&sort_order=&sort_by=',
'x-content-type-options': 'nosniff',
'transfer-encoding': 'chunked',
'connection': 'keep-alive',
'content-length': '76',
'server': 'nginx/1.0.15',
'date': 'Fri, 05 Jul 2013 15:33:10 GMT',
'x-runtime': '119',
'etag': '"788813e9d5f7a30ad995b89094cc9745"',
'x-zendesk-origin-server': 'app27.sys.zendesk.com',
'cache-control': 'private, max-age=0, must-revalidate',
'x-zendesk-user-id': '421106531',
'retry-after': 10,
'content-type': 'application/json; charset=utf-8',
'-content-encoding': 'gzip'}
"""
# Just in case
if not response:
raise ZendeskError('Response Not Found')
response_status = int(response.get('status', 0))

if response_status != status:
raise ZendeskError(content, response_status)
if response_status == 401:
raise AuthenticationError(content, response_status, response)
elif response_status == 429:
raise RateLimitError(content, response_status, response)
else:
raise ZendeskError(content, response_status, response)

# Deserialize json content if content exist. In some cases Zendesk
# returns ' ' strings. Also return false non strings (0, [], (), {})
Expand Down