base.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. # coding=utf-8
  2. import hashlib
  3. import inspect
  4. import time
  5. from collections import OrderedDict
  6. from urlparse import urljoin
  7. import typing
  8. import requests
  9. import logging
  10. import simplejson as json
  11. from simplejson import JSONDecodeError
  12. from library.jdopen.client.api.base import BaseJdOpenAPI
  13. from library.jdopen.constants import JdOpenResultCode
  14. from library.jdbase.exceptions import JDException, JDNetworkException
  15. if typing.TYPE_CHECKING:
  16. from requests import Response
  17. logger = logging.getLogger(__name__)
  18. class JdOpenBaseClient(object):
  19. API_BASE_URL = ""
  20. def __new__(cls, *args, **kwargs):
  21. self = super(JdOpenBaseClient, cls).__new__(cls)
  22. apis = inspect.getmembers(self, lambda x: inspect.isclass(x) and issubclass(x, BaseJdOpenAPI))
  23. for apiName, apiClient in apis:
  24. setattr(self, apiName, apiClient(self))
  25. return self
  26. def __init__(self, agentNum, accessKey, secretKey, timeout=None, auto_retry=True):
  27. self._agentNum = agentNum
  28. self._accessKey = accessKey
  29. self._secretKey = secretKey
  30. self._timeout = timeout
  31. self._auto_retry = auto_retry
  32. @property
  33. def agentNum(self):
  34. return self._agentNum
  35. @staticmethod
  36. def _decode_result(res): # type:(Response) -> typing.Optional[dict, str]
  37. """
  38. 解析request返回的结果
  39. """
  40. try:
  41. result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False)
  42. except (TypeError, ValueError, JSONDecodeError):
  43. logger.debug(u'错误的解析结构', exc_info=True)
  44. return res
  45. return result
  46. def calc_token(self, timestamp, path=None, body=None):
  47. data = OrderedDict()
  48. data["secretKey"] = self._secretKey
  49. data["timestamp"] = timestamp
  50. if path:
  51. data["path"] = path
  52. if body is not None:
  53. if isinstance(body, dict):
  54. data["body"] = json.dumps(body, separators=(",", ":"))
  55. else:
  56. data["body"] = body
  57. s = '&'.join([u'{}={}'.format(k, v) for k, v in data.items()])
  58. s = s.encode('utf-8')
  59. token = hashlib.sha1(s).hexdigest().upper()
  60. return token
  61. def _request(self, method, path, **kwargs):
  62. # 修补url
  63. logger.info("[{} send request], method = {}, path = {}, data = {}".format(self.__class__.__name__, method, path, kwargs))
  64. urlBase = kwargs.pop("urlBase", self.API_BASE_URL)
  65. url = urljoin(urlBase, path)
  66. # 修补data数据
  67. if isinstance(kwargs.get("data", ""), dict):
  68. data = kwargs.pop("data")
  69. kwargs['data'] = json.dumps(data, separators=(',', ':'))
  70. # 修补请求头
  71. timestamp = kwargs.pop("timestamp", str(int(time.time())))
  72. headers = {
  73. "Content-Type": "application/json",
  74. "accessKey": self._accessKey,
  75. "timestamp": timestamp,
  76. "token": self.calc_token(timestamp, path, body=kwargs.get("data", None))
  77. }
  78. kwargs.setdefault("params", {})
  79. kwargs.setdefault("timeout", self._timeout)
  80. processor = kwargs.pop("processor", None)
  81. with requests.sessions.Session() as _session:
  82. res = _session.request(
  83. method=method,
  84. url=url,
  85. headers=headers,
  86. **kwargs
  87. )
  88. try:
  89. res.raise_for_status()
  90. except requests.RequestException as rre:
  91. logger.info("[{} send request] error! status code = {}, error = {}".format(self.__class__.__name__, res.status_code, rre))
  92. raise JDNetworkException(
  93. errCode='HTTP{}'.format(res.status_code),
  94. errMsg=rre.message,
  95. client=self,
  96. request=rre.request,
  97. response=rre.response
  98. )
  99. return self._handle_result(
  100. res, method, url, processor, **kwargs
  101. )
  102. def _handle_result(self, res, method, url, processor, **kwargs):
  103. result = self._decode_result(res)
  104. logger.info("[{} handle_result] method = {}; url = {}; result = {}".format(
  105. self.__class__.__name__, method, url, result))
  106. if processor:
  107. return processor(self, result)
  108. else:
  109. if "result" not in result:
  110. return result
  111. if result["result"] == JdOpenResultCode.SUCCESS:
  112. return processor(result) if processor else result
  113. error = result["error"]
  114. raise JDException(
  115. errCode = error.get("errorCode", ''),
  116. errMsg = error.get("errorMsg", ''),
  117. client = self,
  118. request = res.request,
  119. response = res)
  120. def get(self, url, **kwargs):
  121. return self._request("get", url, **kwargs)
  122. def post(self, url, **kwargs):
  123. return self._request("post", url, **kwargs)