You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
5.4 KiB
226 lines
5.4 KiB
#! /usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
# Date: 2018/9/18
|
|
|
|
import hmac
|
|
import base64
|
|
import inspect
|
|
import logging
|
|
|
|
from urllib.parse import quote
|
|
|
|
from .. import BaseAli
|
|
from ..yun import api
|
|
from ..yun.api.base import BaseAliYunAPI
|
|
from ..tools import get_uuid
|
|
from ..tools import get_iso_8061_date
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
QUERY_STRING = None
|
|
|
|
|
|
def _get_signature(string_to_sign, secret, signer=hmac):
|
|
"""获取签名
|
|
|
|
"""
|
|
secret = "{}&".format(secret)
|
|
hmb = signer.new(secret.encode("utf-8"), string_to_sign.encode("utf-8"), "sha1").digest()
|
|
return quote(base64.standard_b64encode(hmb).decode("ascii"), safe="~")
|
|
|
|
|
|
def __pop_standard_urlencode(query):
|
|
ret = ""
|
|
for item in query:
|
|
|
|
if not item[1]:
|
|
continue
|
|
|
|
ret += quote(item[0], safe="~") + "=" + quote(item[1], safe="~") + "&"
|
|
|
|
return ret
|
|
|
|
|
|
def _compose_sign_string(query_params):
|
|
"""组成签名字符串
|
|
|
|
"""
|
|
|
|
sorted_parameters = sorted(query_params.items(), key=lambda url_params: url_params[0])
|
|
|
|
string_to_sign = __pop_standard_urlencode(sorted_parameters)
|
|
|
|
query_string = string_to_sign[:-1]
|
|
|
|
global QUERY_STRING
|
|
QUERY_STRING = query_string
|
|
|
|
tosign = "GET&%2F&" + quote(query_string, safe="~")
|
|
|
|
return tosign
|
|
|
|
|
|
def _is_api_endpoint(instance):
|
|
return issubclass(instance.__class__, BaseAliYunAPI)
|
|
|
|
|
|
class AliYunClient(BaseAli):
|
|
|
|
VERSION = "" # 每个接口都需要版本号
|
|
|
|
API_BASE_URL = "" # 阿里云api网关地址均从使用的接口定义
|
|
|
|
# 点播api
|
|
vod = api.AliYunVod()
|
|
# 短信api
|
|
sms = api.AliYunSms()
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
"""注册接口
|
|
|
|
"""
|
|
self = super(AliYunClient, cls).__new__(cls)
|
|
api_endpoints = inspect.getmembers(self, _is_api_endpoint)
|
|
for name, api_ins in api_endpoints:
|
|
api_cls = type(api_ins)
|
|
api_ins = api_cls(self)
|
|
setattr(self, name, api_ins)
|
|
return self
|
|
|
|
def __init__(self, app_id, secret, timeout=None, auto_retry=False):
|
|
super(AliYunClient, self).__init__(
|
|
timeout, auto_retry
|
|
)
|
|
self.app_id = app_id
|
|
self.secret = secret
|
|
|
|
def request(self, method, action, **kwargs):
|
|
"""构造请求用户授权的url
|
|
|
|
Parameters
|
|
----------
|
|
method : string
|
|
请求类型
|
|
|
|
action : string
|
|
API的命名,固定值,如发送短信API的值为:SendSms
|
|
|
|
kwargs : dict
|
|
请求参数
|
|
|
|
Returns
|
|
-------
|
|
dict
|
|
"""
|
|
query_string = ""
|
|
|
|
api_base_url = kwargs.pop("api_base_url", self.API_BASE_URL)
|
|
|
|
# 获取请求参数
|
|
if isinstance(kwargs.get("data", {}), dict):
|
|
|
|
data = kwargs["data"]
|
|
|
|
data["Action"] = action
|
|
|
|
# 封装公有请求参数
|
|
|
|
# 接口版本号
|
|
if "Version" not in data:
|
|
data["Version"] = kwargs.pop("version", self.VERSION)
|
|
|
|
# 时间戳
|
|
data["Timestamp"] = get_iso_8061_date()
|
|
# 签名算法名称
|
|
data["SignatureMethod"] = "HMAC-SHA1"
|
|
# 签名版本
|
|
data["SignatureVersion"] = "1.0"
|
|
# 随机码
|
|
data["SignatureNonce"] = get_uuid()
|
|
# access key
|
|
data["AccessKeyId"] = self.app_id
|
|
# 接口响应类型均为 `JSON`
|
|
data["Format"] = "JSON"
|
|
|
|
# 计算签名
|
|
string_to_sign = _compose_sign_string(data)
|
|
signature = _get_signature(string_to_sign, self.secret)
|
|
|
|
query_string = '?{}{}'.format(QUERY_STRING, "&" + "Signature=" + signature)
|
|
|
|
# 结果解析器, 如果需要定制可单独定制
|
|
result_processor = kwargs.pop("result_processor", None)
|
|
|
|
url = "{}{}".format(api_base_url, query_string)
|
|
|
|
res = self._http.request(
|
|
method=method,
|
|
url=url,
|
|
**kwargs
|
|
)
|
|
|
|
# res.raise_for_status()
|
|
|
|
return self._handle_result(res, method, url, result_processor, **kwargs)
|
|
|
|
def _handle_result(self, res, method=None, url=None,
|
|
result_processor=None, **kwargs):
|
|
"""结果解析
|
|
|
|
Parameters
|
|
----------
|
|
res : request instance
|
|
响应对象 response
|
|
|
|
method : string
|
|
请求方法
|
|
|
|
url : string
|
|
请求的 `url`
|
|
|
|
result_processor: func OR None
|
|
结果处理器
|
|
|
|
kwargs: dict
|
|
更多参数
|
|
|
|
Returns
|
|
-------
|
|
dict
|
|
"""
|
|
if not isinstance(res, dict):
|
|
result = res.json()
|
|
else:
|
|
result = res
|
|
|
|
if not isinstance(result, dict):
|
|
return result
|
|
|
|
if "Code" in result:
|
|
|
|
code = result.get("Code")
|
|
|
|
if code != "OK":
|
|
logger.error("AliApi {} 调用异常, Code {} Errmsg {}".format(
|
|
url, code, result.get("Message")
|
|
))
|
|
|
|
if code in []:
|
|
self.request(method, url, **kwargs)
|
|
|
|
return result if not result_processor else result_processor(result)
|
|
|
|
def get(self, url, **kwargs):
|
|
return self.request(
|
|
method="get",
|
|
action=url,
|
|
**kwargs
|
|
)
|
|
|
|
def post(self, url, **kwargs):
|
|
return self.request(
|
|
method="post",
|
|
action=url,
|
|
**kwargs
|
|
)
|
|
|