Run black on moto & test directories.
This commit is contained in:
parent
c820395dbf
commit
96e5b1993d
507 changed files with 52541 additions and 47814 deletions
|
|
@ -40,7 +40,7 @@ def _decode_dict(d):
|
|||
newkey = []
|
||||
for k in key:
|
||||
if isinstance(k, six.binary_type):
|
||||
newkey.append(k.decode('utf-8'))
|
||||
newkey.append(k.decode("utf-8"))
|
||||
else:
|
||||
newkey.append(k)
|
||||
else:
|
||||
|
|
@ -52,7 +52,7 @@ def _decode_dict(d):
|
|||
newvalue = []
|
||||
for v in value:
|
||||
if isinstance(v, six.binary_type):
|
||||
newvalue.append(v.decode('utf-8'))
|
||||
newvalue.append(v.decode("utf-8"))
|
||||
else:
|
||||
newvalue.append(v)
|
||||
else:
|
||||
|
|
@ -90,7 +90,8 @@ class _TemplateEnvironmentMixin(object):
|
|||
super(_TemplateEnvironmentMixin, self).__init__()
|
||||
self.loader = DynamicDictLoader({})
|
||||
self.environment = Environment(
|
||||
loader=self.loader, autoescape=self.should_autoescape)
|
||||
loader=self.loader, autoescape=self.should_autoescape
|
||||
)
|
||||
|
||||
@property
|
||||
def should_autoescape(self):
|
||||
|
|
@ -104,13 +105,15 @@ class _TemplateEnvironmentMixin(object):
|
|||
template_id = id(source)
|
||||
if not self.contains_template(template_id):
|
||||
collapsed = re.sub(
|
||||
self.RIGHT_PATTERN,
|
||||
">",
|
||||
re.sub(self.LEFT_PATTERN, "<", source)
|
||||
self.RIGHT_PATTERN, ">", re.sub(self.LEFT_PATTERN, "<", source)
|
||||
)
|
||||
self.loader.update({template_id: collapsed})
|
||||
self.environment = Environment(loader=self.loader, autoescape=self.should_autoescape, trim_blocks=True,
|
||||
lstrip_blocks=True)
|
||||
self.environment = Environment(
|
||||
loader=self.loader,
|
||||
autoescape=self.should_autoescape,
|
||||
trim_blocks=True,
|
||||
lstrip_blocks=True,
|
||||
)
|
||||
return self.environment.get_template(template_id)
|
||||
|
||||
|
||||
|
|
@ -119,8 +122,13 @@ class ActionAuthenticatorMixin(object):
|
|||
request_count = 0
|
||||
|
||||
def _authenticate_and_authorize_action(self, iam_request_cls):
|
||||
if ActionAuthenticatorMixin.request_count >= settings.INITIAL_NO_AUTH_ACTION_COUNT:
|
||||
iam_request = iam_request_cls(method=self.method, path=self.path, data=self.data, headers=self.headers)
|
||||
if (
|
||||
ActionAuthenticatorMixin.request_count
|
||||
>= settings.INITIAL_NO_AUTH_ACTION_COUNT
|
||||
):
|
||||
iam_request = iam_request_cls(
|
||||
method=self.method, path=self.path, data=self.data, headers=self.headers
|
||||
)
|
||||
iam_request.check_signature()
|
||||
iam_request.check_action_permitted()
|
||||
else:
|
||||
|
|
@ -137,10 +145,17 @@ class ActionAuthenticatorMixin(object):
|
|||
def decorator(function):
|
||||
def wrapper(*args, **kwargs):
|
||||
if settings.TEST_SERVER_MODE:
|
||||
response = requests.post("http://localhost:5000/moto-api/reset-auth", data=str(initial_no_auth_action_count).encode())
|
||||
original_initial_no_auth_action_count = response.json()['PREVIOUS_INITIAL_NO_AUTH_ACTION_COUNT']
|
||||
response = requests.post(
|
||||
"http://localhost:5000/moto-api/reset-auth",
|
||||
data=str(initial_no_auth_action_count).encode(),
|
||||
)
|
||||
original_initial_no_auth_action_count = response.json()[
|
||||
"PREVIOUS_INITIAL_NO_AUTH_ACTION_COUNT"
|
||||
]
|
||||
else:
|
||||
original_initial_no_auth_action_count = settings.INITIAL_NO_AUTH_ACTION_COUNT
|
||||
original_initial_no_auth_action_count = (
|
||||
settings.INITIAL_NO_AUTH_ACTION_COUNT
|
||||
)
|
||||
original_request_count = ActionAuthenticatorMixin.request_count
|
||||
settings.INITIAL_NO_AUTH_ACTION_COUNT = initial_no_auth_action_count
|
||||
ActionAuthenticatorMixin.request_count = 0
|
||||
|
|
@ -148,10 +163,15 @@ class ActionAuthenticatorMixin(object):
|
|||
result = function(*args, **kwargs)
|
||||
finally:
|
||||
if settings.TEST_SERVER_MODE:
|
||||
requests.post("http://localhost:5000/moto-api/reset-auth", data=str(original_initial_no_auth_action_count).encode())
|
||||
requests.post(
|
||||
"http://localhost:5000/moto-api/reset-auth",
|
||||
data=str(original_initial_no_auth_action_count).encode(),
|
||||
)
|
||||
else:
|
||||
ActionAuthenticatorMixin.request_count = original_request_count
|
||||
settings.INITIAL_NO_AUTH_ACTION_COUNT = original_initial_no_auth_action_count
|
||||
settings.INITIAL_NO_AUTH_ACTION_COUNT = (
|
||||
original_initial_no_auth_action_count
|
||||
)
|
||||
return result
|
||||
|
||||
functools.update_wrapper(wrapper, function)
|
||||
|
|
@ -163,11 +183,13 @@ class ActionAuthenticatorMixin(object):
|
|||
|
||||
class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
||||
|
||||
default_region = 'us-east-1'
|
||||
default_region = "us-east-1"
|
||||
# to extract region, use [^.]
|
||||
region_regex = re.compile(r'\.(?P<region>[a-z]{2}-[a-z]+-\d{1})\.amazonaws\.com')
|
||||
param_list_regex = re.compile(r'(.*)\.(\d+)\.')
|
||||
access_key_regex = re.compile(r'AWS.*(?P<access_key>(?<![A-Z0-9])[A-Z0-9]{20}(?![A-Z0-9]))[:/]')
|
||||
region_regex = re.compile(r"\.(?P<region>[a-z]{2}-[a-z]+-\d{1})\.amazonaws\.com")
|
||||
param_list_regex = re.compile(r"(.*)\.(\d+)\.")
|
||||
access_key_regex = re.compile(
|
||||
r"AWS.*(?P<access_key>(?<![A-Z0-9])[A-Z0-9]{20}(?![A-Z0-9]))[:/]"
|
||||
)
|
||||
aws_service_spec = None
|
||||
|
||||
@classmethod
|
||||
|
|
@ -176,7 +198,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
|
||||
def setup_class(self, request, full_url, headers):
|
||||
querystring = {}
|
||||
if hasattr(request, 'body'):
|
||||
if hasattr(request, "body"):
|
||||
# Boto
|
||||
self.body = request.body
|
||||
else:
|
||||
|
|
@ -189,24 +211,29 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
|
||||
querystring = {}
|
||||
for key, value in request.form.items():
|
||||
querystring[key] = [value, ]
|
||||
querystring[key] = [value]
|
||||
|
||||
raw_body = self.body
|
||||
if isinstance(self.body, six.binary_type):
|
||||
self.body = self.body.decode('utf-8')
|
||||
self.body = self.body.decode("utf-8")
|
||||
|
||||
if not querystring:
|
||||
querystring.update(
|
||||
parse_qs(urlparse(full_url).query, keep_blank_values=True))
|
||||
parse_qs(urlparse(full_url).query, keep_blank_values=True)
|
||||
)
|
||||
if not querystring:
|
||||
if 'json' in request.headers.get('content-type', []) and self.aws_service_spec:
|
||||
if (
|
||||
"json" in request.headers.get("content-type", [])
|
||||
and self.aws_service_spec
|
||||
):
|
||||
decoded = json.loads(self.body)
|
||||
|
||||
target = request.headers.get(
|
||||
'x-amz-target') or request.headers.get('X-Amz-Target')
|
||||
service, method = target.split('.')
|
||||
target = request.headers.get("x-amz-target") or request.headers.get(
|
||||
"X-Amz-Target"
|
||||
)
|
||||
service, method = target.split(".")
|
||||
input_spec = self.aws_service_spec.input_spec(method)
|
||||
flat = flatten_json_request_body('', decoded, input_spec)
|
||||
flat = flatten_json_request_body("", decoded, input_spec)
|
||||
for key, value in flat.items():
|
||||
querystring[key] = [value]
|
||||
elif self.body:
|
||||
|
|
@ -231,17 +258,19 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
self.uri_match = None
|
||||
|
||||
self.headers = request.headers
|
||||
if 'host' not in self.headers:
|
||||
self.headers['host'] = urlparse(full_url).netloc
|
||||
if "host" not in self.headers:
|
||||
self.headers["host"] = urlparse(full_url).netloc
|
||||
self.response_headers = {"server": "amazon.com"}
|
||||
|
||||
def get_region_from_url(self, request, full_url):
|
||||
match = self.region_regex.search(full_url)
|
||||
if match:
|
||||
region = match.group(1)
|
||||
elif 'Authorization' in request.headers and 'AWS4' in request.headers['Authorization']:
|
||||
region = request.headers['Authorization'].split(",")[
|
||||
0].split("/")[2]
|
||||
elif (
|
||||
"Authorization" in request.headers
|
||||
and "AWS4" in request.headers["Authorization"]
|
||||
):
|
||||
region = request.headers["Authorization"].split(",")[0].split("/")[2]
|
||||
else:
|
||||
region = self.default_region
|
||||
return region
|
||||
|
|
@ -250,16 +279,16 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
"""
|
||||
Returns the access key id used in this request as the current user id
|
||||
"""
|
||||
if 'Authorization' in self.headers:
|
||||
match = self.access_key_regex.search(self.headers['Authorization'])
|
||||
if "Authorization" in self.headers:
|
||||
match = self.access_key_regex.search(self.headers["Authorization"])
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
if self.querystring.get('AWSAccessKeyId'):
|
||||
return self.querystring.get('AWSAccessKeyId')
|
||||
if self.querystring.get("AWSAccessKeyId"):
|
||||
return self.querystring.get("AWSAccessKeyId")
|
||||
else:
|
||||
# Should we raise an unauthorized exception instead?
|
||||
return '111122223333'
|
||||
return "111122223333"
|
||||
|
||||
def _dispatch(self, request, full_url, headers):
|
||||
self.setup_class(request, full_url, headers)
|
||||
|
|
@ -274,17 +303,22 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
-> '^/cars/.*/drivers/.*/drive$'
|
||||
|
||||
"""
|
||||
def _convert(elem, is_last):
|
||||
if not re.match('^{.*}$', elem):
|
||||
return elem
|
||||
name = elem.replace('{', '').replace('}', '')
|
||||
if is_last:
|
||||
return '(?P<%s>[^/]*)' % name
|
||||
return '(?P<%s>.*)' % name
|
||||
|
||||
elems = uri.split('/')
|
||||
def _convert(elem, is_last):
|
||||
if not re.match("^{.*}$", elem):
|
||||
return elem
|
||||
name = elem.replace("{", "").replace("}", "")
|
||||
if is_last:
|
||||
return "(?P<%s>[^/]*)" % name
|
||||
return "(?P<%s>.*)" % name
|
||||
|
||||
elems = uri.split("/")
|
||||
num_elems = len(elems)
|
||||
regexp = '^{}$'.format('/'.join([_convert(elem, (i == num_elems - 1)) for i, elem in enumerate(elems)]))
|
||||
regexp = "^{}$".format(
|
||||
"/".join(
|
||||
[_convert(elem, (i == num_elems - 1)) for i, elem in enumerate(elems)]
|
||||
)
|
||||
)
|
||||
return regexp
|
||||
|
||||
def _get_action_from_method_and_request_uri(self, method, request_uri):
|
||||
|
|
@ -295,19 +329,19 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
|
||||
# service response class should have 'SERVICE_NAME' class member,
|
||||
# if you want to get action from method and url
|
||||
if not hasattr(self, 'SERVICE_NAME'):
|
||||
if not hasattr(self, "SERVICE_NAME"):
|
||||
return None
|
||||
service = self.SERVICE_NAME
|
||||
conn = boto3.client(service, region_name=self.region)
|
||||
|
||||
# make cache if it does not exist yet
|
||||
if not hasattr(self, 'method_urls'):
|
||||
if not hasattr(self, "method_urls"):
|
||||
self.method_urls = defaultdict(lambda: defaultdict(str))
|
||||
op_names = conn._service_model.operation_names
|
||||
for op_name in op_names:
|
||||
op_model = conn._service_model.operation_model(op_name)
|
||||
_method = op_model.http['method']
|
||||
uri_regexp = self.uri_to_regexp(op_model.http['requestUri'])
|
||||
_method = op_model.http["method"]
|
||||
uri_regexp = self.uri_to_regexp(op_model.http["requestUri"])
|
||||
self.method_urls[_method][uri_regexp] = op_model.name
|
||||
regexp_and_names = self.method_urls[method]
|
||||
for regexp, name in regexp_and_names.items():
|
||||
|
|
@ -318,11 +352,10 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
return None
|
||||
|
||||
def _get_action(self):
|
||||
action = self.querystring.get('Action', [""])[0]
|
||||
action = self.querystring.get("Action", [""])[0]
|
||||
if not action: # Some services use a header for the action
|
||||
# Headers are case-insensitive. Probably a better way to do this.
|
||||
match = self.headers.get(
|
||||
'x-amz-target') or self.headers.get('X-Amz-Target')
|
||||
match = self.headers.get("x-amz-target") or self.headers.get("X-Amz-Target")
|
||||
if match:
|
||||
action = match.split(".")[-1]
|
||||
# get action from method and uri
|
||||
|
|
@ -354,10 +387,11 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
return self._send_response(headers, response)
|
||||
|
||||
if not action:
|
||||
return 404, headers, ''
|
||||
return 404, headers, ""
|
||||
|
||||
raise NotImplementedError(
|
||||
"The {0} action has not been implemented".format(action))
|
||||
"The {0} action has not been implemented".format(action)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _send_response(headers, response):
|
||||
|
|
@ -365,11 +399,11 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
body, new_headers = response
|
||||
else:
|
||||
status, new_headers, body = response
|
||||
status = new_headers.get('status', 200)
|
||||
status = new_headers.get("status", 200)
|
||||
headers.update(new_headers)
|
||||
# Cast status to string
|
||||
if "status" in headers:
|
||||
headers['status'] = str(headers['status'])
|
||||
headers["status"] = str(headers["status"])
|
||||
return status, headers, body
|
||||
|
||||
def _get_param(self, param_name, if_none=None):
|
||||
|
|
@ -403,9 +437,9 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
def _get_bool_param(self, param_name, if_none=None):
|
||||
val = self._get_param(param_name)
|
||||
if val is not None:
|
||||
if val.lower() == 'true':
|
||||
if val.lower() == "true":
|
||||
return True
|
||||
elif val.lower() == 'false':
|
||||
elif val.lower() == "false":
|
||||
return False
|
||||
return if_none
|
||||
|
||||
|
|
@ -423,11 +457,16 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
if is_tracked(name) or not name.startswith(param_prefix):
|
||||
continue
|
||||
|
||||
if len(name) > len(param_prefix) and \
|
||||
not name[len(param_prefix):].startswith('.'):
|
||||
if len(name) > len(param_prefix) and not name[
|
||||
len(param_prefix) :
|
||||
].startswith("."):
|
||||
continue
|
||||
|
||||
match = self.param_list_regex.search(name[len(param_prefix):]) if len(name) > len(param_prefix) else None
|
||||
match = (
|
||||
self.param_list_regex.search(name[len(param_prefix) :])
|
||||
if len(name) > len(param_prefix)
|
||||
else None
|
||||
)
|
||||
if match:
|
||||
prefix = param_prefix + match.group(1)
|
||||
value = self._get_multi_param(prefix)
|
||||
|
|
@ -442,7 +481,10 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
|
||||
if len(value_dict) > 1:
|
||||
# strip off period prefix
|
||||
value_dict = {name[len(param_prefix) + 1:]: value for name, value in value_dict.items()}
|
||||
value_dict = {
|
||||
name[len(param_prefix) + 1 :]: value
|
||||
for name, value in value_dict.items()
|
||||
}
|
||||
else:
|
||||
value_dict = list(value_dict.values())[0]
|
||||
|
||||
|
|
@ -461,7 +503,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
index = 1
|
||||
while True:
|
||||
value_dict = self._get_multi_param_helper(prefix + str(index))
|
||||
if not value_dict and value_dict != '':
|
||||
if not value_dict and value_dict != "":
|
||||
break
|
||||
|
||||
values.append(value_dict)
|
||||
|
|
@ -486,8 +528,9 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
params = {}
|
||||
for key, value in self.querystring.items():
|
||||
if key.startswith(param_prefix):
|
||||
params[camelcase_to_underscores(
|
||||
key.replace(param_prefix, ""))] = value[0]
|
||||
params[camelcase_to_underscores(key.replace(param_prefix, ""))] = value[
|
||||
0
|
||||
]
|
||||
return params
|
||||
|
||||
def _get_list_prefix(self, param_prefix):
|
||||
|
|
@ -520,19 +563,20 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
new_items = {}
|
||||
for key, value in self.querystring.items():
|
||||
if key.startswith(index_prefix):
|
||||
new_items[camelcase_to_underscores(
|
||||
key.replace(index_prefix, ""))] = value[0]
|
||||
new_items[
|
||||
camelcase_to_underscores(key.replace(index_prefix, ""))
|
||||
] = value[0]
|
||||
if not new_items:
|
||||
break
|
||||
results.append(new_items)
|
||||
param_index += 1
|
||||
return results
|
||||
|
||||
def _get_map_prefix(self, param_prefix, key_end='.key', value_end='.value'):
|
||||
def _get_map_prefix(self, param_prefix, key_end=".key", value_end=".value"):
|
||||
results = {}
|
||||
param_index = 1
|
||||
while 1:
|
||||
index_prefix = '{0}.{1}.'.format(param_prefix, param_index)
|
||||
index_prefix = "{0}.{1}.".format(param_prefix, param_index)
|
||||
|
||||
k, v = None, None
|
||||
for key, value in self.querystring.items():
|
||||
|
|
@ -559,8 +603,8 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
|
||||
param_index = 1
|
||||
while True:
|
||||
key_name = 'tag.{0}._key'.format(param_index)
|
||||
value_name = 'tag.{0}._value'.format(param_index)
|
||||
key_name = "tag.{0}._key".format(param_index)
|
||||
value_name = "tag.{0}._value".format(param_index)
|
||||
|
||||
try:
|
||||
results[resource_type][tag[key_name]] = tag[value_name]
|
||||
|
|
@ -570,7 +614,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
|
||||
return results
|
||||
|
||||
def _get_object_map(self, prefix, name='Name', value='Value'):
|
||||
def _get_object_map(self, prefix, name="Name", value="Value"):
|
||||
"""
|
||||
Given a query dict like
|
||||
{
|
||||
|
|
@ -598,15 +642,14 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
index = 1
|
||||
while True:
|
||||
# Loop through looking for keys representing object name
|
||||
name_key = '{0}.{1}.{2}'.format(prefix, index, name)
|
||||
name_key = "{0}.{1}.{2}".format(prefix, index, name)
|
||||
obj_name = self.querystring.get(name_key)
|
||||
if not obj_name:
|
||||
# Found all keys
|
||||
break
|
||||
|
||||
obj = {}
|
||||
value_key_prefix = '{0}.{1}.{2}.'.format(
|
||||
prefix, index, value)
|
||||
value_key_prefix = "{0}.{1}.{2}.".format(prefix, index, value)
|
||||
for k, v in self.querystring.items():
|
||||
if k.startswith(value_key_prefix):
|
||||
_, value_key = k.split(value_key_prefix, 1)
|
||||
|
|
@ -620,31 +663,46 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
|
|||
|
||||
@property
|
||||
def request_json(self):
|
||||
return 'JSON' in self.querystring.get('ContentType', [])
|
||||
return "JSON" in self.querystring.get("ContentType", [])
|
||||
|
||||
def is_not_dryrun(self, action):
|
||||
if 'true' in self.querystring.get('DryRun', ['false']):
|
||||
message = 'An error occurred (DryRunOperation) when calling the %s operation: Request would have succeeded, but DryRun flag is set' % action
|
||||
raise DryRunClientError(
|
||||
error_type="DryRunOperation", message=message)
|
||||
if "true" in self.querystring.get("DryRun", ["false"]):
|
||||
message = (
|
||||
"An error occurred (DryRunOperation) when calling the %s operation: Request would have succeeded, but DryRun flag is set"
|
||||
% action
|
||||
)
|
||||
raise DryRunClientError(error_type="DryRunOperation", message=message)
|
||||
return True
|
||||
|
||||
|
||||
class MotoAPIResponse(BaseResponse):
|
||||
|
||||
def reset_response(self, request, full_url, headers):
|
||||
if request.method == "POST":
|
||||
from .models import moto_api_backend
|
||||
|
||||
moto_api_backend.reset()
|
||||
return 200, {}, json.dumps({"status": "ok"})
|
||||
return 400, {}, json.dumps({"Error": "Need to POST to reset Moto"})
|
||||
|
||||
def reset_auth_response(self, request, full_url, headers):
|
||||
if request.method == "POST":
|
||||
previous_initial_no_auth_action_count = settings.INITIAL_NO_AUTH_ACTION_COUNT
|
||||
previous_initial_no_auth_action_count = (
|
||||
settings.INITIAL_NO_AUTH_ACTION_COUNT
|
||||
)
|
||||
settings.INITIAL_NO_AUTH_ACTION_COUNT = float(request.data.decode())
|
||||
ActionAuthenticatorMixin.request_count = 0
|
||||
return 200, {}, json.dumps({"status": "ok", "PREVIOUS_INITIAL_NO_AUTH_ACTION_COUNT": str(previous_initial_no_auth_action_count)})
|
||||
return (
|
||||
200,
|
||||
{},
|
||||
json.dumps(
|
||||
{
|
||||
"status": "ok",
|
||||
"PREVIOUS_INITIAL_NO_AUTH_ACTION_COUNT": str(
|
||||
previous_initial_no_auth_action_count
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
return 400, {}, json.dumps({"Error": "Need to POST to reset Moto Auth"})
|
||||
|
||||
def model_data(self, request, full_url, headers):
|
||||
|
|
@ -672,7 +730,8 @@ class MotoAPIResponse(BaseResponse):
|
|||
|
||||
def dashboard(self, request, full_url, headers):
|
||||
from flask import render_template
|
||||
return render_template('dashboard.html')
|
||||
|
||||
return render_template("dashboard.html")
|
||||
|
||||
|
||||
class _RecursiveDictRef(object):
|
||||
|
|
@ -683,7 +742,7 @@ class _RecursiveDictRef(object):
|
|||
self.dic = {}
|
||||
|
||||
def __repr__(self):
|
||||
return '{!r}'.format(self.dic)
|
||||
return "{!r}".format(self.dic)
|
||||
|
||||
def __getattr__(self, key):
|
||||
return self.dic.__getattr__(key)
|
||||
|
|
@ -707,21 +766,21 @@ class AWSServiceSpec(object):
|
|||
"""
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = resource_filename('botocore', path)
|
||||
with io.open(self.path, 'r', encoding='utf-8') as f:
|
||||
self.path = resource_filename("botocore", path)
|
||||
with io.open(self.path, "r", encoding="utf-8") as f:
|
||||
spec = json.load(f)
|
||||
self.metadata = spec['metadata']
|
||||
self.operations = spec['operations']
|
||||
self.shapes = spec['shapes']
|
||||
self.metadata = spec["metadata"]
|
||||
self.operations = spec["operations"]
|
||||
self.shapes = spec["shapes"]
|
||||
|
||||
def input_spec(self, operation):
|
||||
try:
|
||||
op = self.operations[operation]
|
||||
except KeyError:
|
||||
raise ValueError('Invalid operation: {}'.format(operation))
|
||||
if 'input' not in op:
|
||||
raise ValueError("Invalid operation: {}".format(operation))
|
||||
if "input" not in op:
|
||||
return {}
|
||||
shape = self.shapes[op['input']['shape']]
|
||||
shape = self.shapes[op["input"]["shape"]]
|
||||
return self._expand(shape)
|
||||
|
||||
def output_spec(self, operation):
|
||||
|
|
@ -735,129 +794,133 @@ class AWSServiceSpec(object):
|
|||
try:
|
||||
op = self.operations[operation]
|
||||
except KeyError:
|
||||
raise ValueError('Invalid operation: {}'.format(operation))
|
||||
if 'output' not in op:
|
||||
raise ValueError("Invalid operation: {}".format(operation))
|
||||
if "output" not in op:
|
||||
return {}
|
||||
shape = self.shapes[op['output']['shape']]
|
||||
shape = self.shapes[op["output"]["shape"]]
|
||||
return self._expand(shape)
|
||||
|
||||
def _expand(self, shape):
|
||||
def expand(dic, seen=None):
|
||||
seen = seen or {}
|
||||
if dic['type'] == 'structure':
|
||||
if dic["type"] == "structure":
|
||||
nodes = {}
|
||||
for k, v in dic['members'].items():
|
||||
for k, v in dic["members"].items():
|
||||
seen_till_here = dict(seen)
|
||||
if k in seen_till_here:
|
||||
nodes[k] = seen_till_here[k]
|
||||
continue
|
||||
seen_till_here[k] = _RecursiveDictRef()
|
||||
nodes[k] = expand(self.shapes[v['shape']], seen_till_here)
|
||||
nodes[k] = expand(self.shapes[v["shape"]], seen_till_here)
|
||||
seen_till_here[k].set_reference(k, nodes[k])
|
||||
nodes['type'] = 'structure'
|
||||
nodes["type"] = "structure"
|
||||
return nodes
|
||||
|
||||
elif dic['type'] == 'list':
|
||||
elif dic["type"] == "list":
|
||||
seen_till_here = dict(seen)
|
||||
shape = dic['member']['shape']
|
||||
shape = dic["member"]["shape"]
|
||||
if shape in seen_till_here:
|
||||
return seen_till_here[shape]
|
||||
seen_till_here[shape] = _RecursiveDictRef()
|
||||
expanded = expand(self.shapes[shape], seen_till_here)
|
||||
seen_till_here[shape].set_reference(shape, expanded)
|
||||
return {'type': 'list', 'member': expanded}
|
||||
return {"type": "list", "member": expanded}
|
||||
|
||||
elif dic['type'] == 'map':
|
||||
elif dic["type"] == "map":
|
||||
seen_till_here = dict(seen)
|
||||
node = {'type': 'map'}
|
||||
node = {"type": "map"}
|
||||
|
||||
if 'shape' in dic['key']:
|
||||
shape = dic['key']['shape']
|
||||
if "shape" in dic["key"]:
|
||||
shape = dic["key"]["shape"]
|
||||
seen_till_here[shape] = _RecursiveDictRef()
|
||||
node['key'] = expand(self.shapes[shape], seen_till_here)
|
||||
seen_till_here[shape].set_reference(shape, node['key'])
|
||||
node["key"] = expand(self.shapes[shape], seen_till_here)
|
||||
seen_till_here[shape].set_reference(shape, node["key"])
|
||||
else:
|
||||
node['key'] = dic['key']['type']
|
||||
node["key"] = dic["key"]["type"]
|
||||
|
||||
if 'shape' in dic['value']:
|
||||
shape = dic['value']['shape']
|
||||
if "shape" in dic["value"]:
|
||||
shape = dic["value"]["shape"]
|
||||
seen_till_here[shape] = _RecursiveDictRef()
|
||||
node['value'] = expand(self.shapes[shape], seen_till_here)
|
||||
seen_till_here[shape].set_reference(shape, node['value'])
|
||||
node["value"] = expand(self.shapes[shape], seen_till_here)
|
||||
seen_till_here[shape].set_reference(shape, node["value"])
|
||||
else:
|
||||
node['value'] = dic['value']['type']
|
||||
node["value"] = dic["value"]["type"]
|
||||
|
||||
return node
|
||||
|
||||
else:
|
||||
return {'type': dic['type']}
|
||||
return {"type": dic["type"]}
|
||||
|
||||
return expand(shape)
|
||||
|
||||
|
||||
def to_str(value, spec):
|
||||
vtype = spec['type']
|
||||
if vtype == 'boolean':
|
||||
return 'true' if value else 'false'
|
||||
elif vtype == 'integer':
|
||||
vtype = spec["type"]
|
||||
if vtype == "boolean":
|
||||
return "true" if value else "false"
|
||||
elif vtype == "integer":
|
||||
return str(value)
|
||||
elif vtype == 'float':
|
||||
elif vtype == "float":
|
||||
return str(value)
|
||||
elif vtype == 'double':
|
||||
elif vtype == "double":
|
||||
return str(value)
|
||||
elif vtype == 'timestamp':
|
||||
return datetime.datetime.utcfromtimestamp(
|
||||
value).replace(tzinfo=pytz.utc).isoformat()
|
||||
elif vtype == 'string':
|
||||
elif vtype == "timestamp":
|
||||
return (
|
||||
datetime.datetime.utcfromtimestamp(value)
|
||||
.replace(tzinfo=pytz.utc)
|
||||
.isoformat()
|
||||
)
|
||||
elif vtype == "string":
|
||||
return str(value)
|
||||
elif value is None:
|
||||
return 'null'
|
||||
return "null"
|
||||
else:
|
||||
raise TypeError('Unknown type {}'.format(vtype))
|
||||
raise TypeError("Unknown type {}".format(vtype))
|
||||
|
||||
|
||||
def from_str(value, spec):
|
||||
vtype = spec['type']
|
||||
if vtype == 'boolean':
|
||||
return True if value == 'true' else False
|
||||
elif vtype == 'integer':
|
||||
vtype = spec["type"]
|
||||
if vtype == "boolean":
|
||||
return True if value == "true" else False
|
||||
elif vtype == "integer":
|
||||
return int(value)
|
||||
elif vtype == 'float':
|
||||
elif vtype == "float":
|
||||
return float(value)
|
||||
elif vtype == 'double':
|
||||
elif vtype == "double":
|
||||
return float(value)
|
||||
elif vtype == 'timestamp':
|
||||
elif vtype == "timestamp":
|
||||
return value
|
||||
elif vtype == 'string':
|
||||
elif vtype == "string":
|
||||
return value
|
||||
raise TypeError('Unknown type {}'.format(vtype))
|
||||
raise TypeError("Unknown type {}".format(vtype))
|
||||
|
||||
|
||||
def flatten_json_request_body(prefix, dict_body, spec):
|
||||
"""Convert a JSON request body into query params."""
|
||||
if len(spec) == 1 and 'type' in spec:
|
||||
if len(spec) == 1 and "type" in spec:
|
||||
return {prefix: to_str(dict_body, spec)}
|
||||
|
||||
flat = {}
|
||||
for key, value in dict_body.items():
|
||||
node_type = spec[key]['type']
|
||||
if node_type == 'list':
|
||||
node_type = spec[key]["type"]
|
||||
if node_type == "list":
|
||||
for idx, v in enumerate(value, 1):
|
||||
pref = key + '.member.' + str(idx)
|
||||
flat.update(flatten_json_request_body(
|
||||
pref, v, spec[key]['member']))
|
||||
elif node_type == 'map':
|
||||
pref = key + ".member." + str(idx)
|
||||
flat.update(flatten_json_request_body(pref, v, spec[key]["member"]))
|
||||
elif node_type == "map":
|
||||
for idx, (k, v) in enumerate(value.items(), 1):
|
||||
pref = key + '.entry.' + str(idx)
|
||||
flat.update(flatten_json_request_body(
|
||||
pref + '.key', k, spec[key]['key']))
|
||||
flat.update(flatten_json_request_body(
|
||||
pref + '.value', v, spec[key]['value']))
|
||||
pref = key + ".entry." + str(idx)
|
||||
flat.update(
|
||||
flatten_json_request_body(pref + ".key", k, spec[key]["key"])
|
||||
)
|
||||
flat.update(
|
||||
flatten_json_request_body(pref + ".value", v, spec[key]["value"])
|
||||
)
|
||||
else:
|
||||
flat.update(flatten_json_request_body(key, value, spec[key]))
|
||||
|
||||
if prefix:
|
||||
prefix = prefix + '.'
|
||||
prefix = prefix + "."
|
||||
return dict((prefix + k, v) for k, v in flat.items())
|
||||
|
||||
|
||||
|
|
@ -880,41 +943,40 @@ def xml_to_json_response(service_spec, operation, xml, result_node=None):
|
|||
|
||||
od = OrderedDict()
|
||||
for k, v in value.items():
|
||||
if k.startswith('@'):
|
||||
if k.startswith("@"):
|
||||
continue
|
||||
|
||||
if k not in spec:
|
||||
# this can happen when with an older version of
|
||||
# botocore for which the node in XML template is not
|
||||
# defined in service spec.
|
||||
log.warning(
|
||||
'Field %s is not defined by the botocore version in use', k)
|
||||
log.warning("Field %s is not defined by the botocore version in use", k)
|
||||
continue
|
||||
|
||||
if spec[k]['type'] == 'list':
|
||||
if spec[k]["type"] == "list":
|
||||
if v is None:
|
||||
od[k] = []
|
||||
elif len(spec[k]['member']) == 1:
|
||||
if isinstance(v['member'], list):
|
||||
od[k] = transform(v['member'], spec[k]['member'])
|
||||
elif len(spec[k]["member"]) == 1:
|
||||
if isinstance(v["member"], list):
|
||||
od[k] = transform(v["member"], spec[k]["member"])
|
||||
else:
|
||||
od[k] = [transform(v['member'], spec[k]['member'])]
|
||||
elif isinstance(v['member'], list):
|
||||
od[k] = [transform(o, spec[k]['member'])
|
||||
for o in v['member']]
|
||||
elif isinstance(v['member'], OrderedDict):
|
||||
od[k] = [transform(v['member'], spec[k]['member'])]
|
||||
od[k] = [transform(v["member"], spec[k]["member"])]
|
||||
elif isinstance(v["member"], list):
|
||||
od[k] = [transform(o, spec[k]["member"]) for o in v["member"]]
|
||||
elif isinstance(v["member"], OrderedDict):
|
||||
od[k] = [transform(v["member"], spec[k]["member"])]
|
||||
else:
|
||||
raise ValueError('Malformatted input')
|
||||
elif spec[k]['type'] == 'map':
|
||||
raise ValueError("Malformatted input")
|
||||
elif spec[k]["type"] == "map":
|
||||
if v is None:
|
||||
od[k] = {}
|
||||
else:
|
||||
items = ([v['entry']] if not isinstance(v['entry'], list) else
|
||||
v['entry'])
|
||||
items = (
|
||||
[v["entry"]] if not isinstance(v["entry"], list) else v["entry"]
|
||||
)
|
||||
for item in items:
|
||||
key = from_str(item['key'], spec[k]['key'])
|
||||
val = from_str(item['value'], spec[k]['value'])
|
||||
key = from_str(item["key"], spec[k]["key"])
|
||||
val = from_str(item["value"], spec[k]["value"])
|
||||
if k not in od:
|
||||
od[k] = {}
|
||||
od[k][key] = val
|
||||
|
|
@ -928,7 +990,7 @@ def xml_to_json_response(service_spec, operation, xml, result_node=None):
|
|||
dic = xmltodict.parse(xml)
|
||||
output_spec = service_spec.output_spec(operation)
|
||||
try:
|
||||
for k in (result_node or (operation + 'Response', operation + 'Result')):
|
||||
for k in result_node or (operation + "Response", operation + "Result"):
|
||||
dic = dic[k]
|
||||
except KeyError:
|
||||
return None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue