import json
from xml.etree import ElementTree
from sdklib.compat import convert_bytes_to_str
from sdklib.http.session import Cookie
from sdklib.util.structures import xml_string_to_dict, CaseInsensitiveDict
from sdklib.html import HTML
[docs]class JsonResponseMixin(object):
_body = ""
@property
def json(self):
try:
return json.loads(convert_bytes_to_str(self._body))
except Exception:
return dict()
@property
def case_insensitive_dict(self):
return CaseInsensitiveDict(self.json)
[docs]class Response(JsonResponseMixin):
def __init__(self, headers=None, status=None, status_text=None, http_version=None, body=None):
self.headers = headers
self.status = status
self.status_text = status_text
self.http_version = http_version
self.body = body
self._cookie = None
@property
def headers(self):
"""
Returns a dictionary of the response headers.
"""
return self._headers
@headers.setter
def headers(self, value):
self._headers = value or {}
@property
def status(self):
return self._status
@status.setter
def status(self, value):
self._status = value
@property
def status_text(self):
return self._status_text
@status_text.setter
def status_text(self, value):
self._status_text = value
@property
def http_version(self):
return self._http_version
@http_version.setter
def http_version(self, value):
self._http_version = value
@property
def body(self):
return self._body
@body.setter
def body(self, value):
self._body = value
@property
def xml(self):
return ElementTree.fromstring(self.body)
@property
def raw(self):
"""
Returns urllib3 response data.
"""
return self.body
@property
def html(self):
"""
Returns HTML response data.
"""
return HTML(self.body)
@property
def data(self):
data = self.body
try:
data = data.decode()
except Exception:
pass
try:
return json.loads(data)
except Exception:
pass
try:
return xml_string_to_dict(data)
except Exception:
return data
[docs]class AbstractBaseHttpResponse(object):
"""
Wrapper of Urllib3 HTTPResponse class needed to implement any HttpSdk response class.
See `Urllib3 <http://urllib3.readthedocs.io/en/latest/user-guide.html#response-content>`_.
"""
urllib3_response = None
_cookie = None
def __init__(self, resp):
self.urllib3_response = resp
@property
def cookie(self):
if not self._cookie:
self._cookie = Cookie(self.headers)
else:
self._cookie.load_from_headers(self.headers)
return self._cookie
@property
def headers(self):
"""
Returns a dictionary of the response headers.
"""
return self.urllib3_response.getheaders()
[docs]class HttpResponse(Response, AbstractBaseHttpResponse):
"""
Wrapper of Urllib3 HTTPResponse class compatible with AbstractBaseHttpResponse.
See `Urllib3 <http://urllib3.readthedocs.io/en/latest/user-guide.html#response-content>`_.
"""
def __init__(self, resp):
self.urllib3_response = resp
super(HttpResponse, self).__init__(
headers=self.urllib3_response.getheaders(),
status=self.urllib3_response.status,
status_text=self.urllib3_response.reason,
body=self.urllib3_response.data
)
@property
def reason(self):
return self.status_text
[docs]class Error(object):
def __init__(self, json_data):
self.json = json_data
self.case_insensitive_dict = CaseInsensitiveDict(self.json)
@property
def code(self):
return self.case_insensitive_dict['code'] if "code" in self.case_insensitive_dict else None
@property
def message(self):
return self.case_insensitive_dict['message'] \
if "message" in self.case_insensitive_dict else None
@property
def json(self):
return self._json
@json.setter
def json(self, value):
self._json = value if isinstance(value, dict) else dict()
def __repr__(self):
return json.dumps(self.json)
def __str__(self):
return self.__repr__()
[docs]class Api11PathsResponse(AbstractBaseHttpResponse, JsonResponseMixin):
"""
This class models a response from any of the endpoints in most of 11Paths APIs.
It consists of a "data" and an "error" elements. Although normally only one of them will be
present, they are not mutually exclusive, since errors can be non fatal, and therefore a
response could have valid information in the data field and at the same time inform of an error.
"""
def __init__(self, resp):
super(Api11PathsResponse, self).__init__(resp)
self._body = self.urllib3_response.data
@property
def data(self):
"""
:return: data part of the API response into a dictionary
"""
return self.case_insensitive_dict.get("data", None)
@property
def error(self):
"""
@return Error the error part of the API response, consisting of an error code and an error
message
"""
return Error(self.case_insensitive_dict["error"]) \
if self.case_insensitive_dict.get("error", None) is not None else None