This commit is contained in:
Steve Pulec 2017-02-23 21:37:43 -05:00
commit f37bad0e00
260 changed files with 6363 additions and 3766 deletions

View file

@ -2,7 +2,6 @@ from __future__ import unicode_literals
from werkzeug.exceptions import HTTPException
from jinja2 import DictLoader, Environment
from six import text_type
SINGLE_ERROR_RESPONSE = u"""<?xml version="1.0" encoding="UTF-8"?>
@ -33,6 +32,7 @@ ERROR_JSON_RESPONSE = u"""{
}
"""
class RESTError(HTTPException):
templates = {
'single_error': SINGLE_ERROR_RESPONSE,
@ -54,8 +54,10 @@ class DryRunClientError(RESTError):
class JsonRESTError(RESTError):
def __init__(self, error_type, message, template='error_json', **kwargs):
super(JsonRESTError, self).__init__(error_type, message, template, **kwargs)
super(JsonRESTError, self).__init__(
error_type, message, template, **kwargs)
def get_headers(self, *args, **kwargs):
return [('Content-Type', 'application/json')]

View file

@ -3,7 +3,6 @@ from __future__ import absolute_import
import functools
import inspect
import os
import re
from moto import settings
@ -15,6 +14,7 @@ from .utils import (
convert_flask_to_responses_response,
)
class BaseMockAWS(object):
nested_count = 0
@ -58,7 +58,6 @@ class BaseMockAWS(object):
if self.__class__.nested_count < 0:
raise RuntimeError('Called stop() before start().')
if self.__class__.nested_count == 0:
self.disable_patching()
@ -96,6 +95,7 @@ class BaseMockAWS(object):
class HttprettyMockAWS(BaseMockAWS):
def reset(self):
HTTPretty.reset()
@ -118,10 +118,11 @@ class HttprettyMockAWS(BaseMockAWS):
RESPONSES_METHODS = [responses.GET, responses.DELETE, responses.HEAD,
responses.OPTIONS, responses.PATCH, responses.POST, responses.PUT]
responses.OPTIONS, responses.PATCH, responses.POST, responses.PUT]
class ResponsesMockAWS(BaseMockAWS):
def reset(self):
responses.reset()
@ -146,6 +147,7 @@ class ResponsesMockAWS(BaseMockAWS):
pass
responses.reset()
MockAWS = ResponsesMockAWS
@ -167,12 +169,14 @@ class ServerModeMockAWS(BaseMockAWS):
if 'endpoint_url' not in kwargs:
kwargs['endpoint_url'] = "http://localhost:8086"
return real_boto3_client(*args, **kwargs)
def fake_boto3_resource(*args, **kwargs):
if 'endpoint_url' not in kwargs:
kwargs['endpoint_url'] = "http://localhost:8086"
return real_boto3_resource(*args, **kwargs)
self._client_patcher = mock.patch('boto3.client', fake_boto3_client)
self._resource_patcher = mock.patch('boto3.resource', fake_boto3_resource)
self._resource_patcher = mock.patch(
'boto3.resource', fake_boto3_resource)
self._client_patcher.start()
self._resource_patcher.start()
@ -181,7 +185,9 @@ class ServerModeMockAWS(BaseMockAWS):
self._client_patcher.stop()
self._resource_patcher.stop()
class Model(type):
def __new__(self, clsname, bases, namespace):
cls = super(Model, self).__new__(self, clsname, bases, namespace)
cls.__models__ = {}
@ -203,6 +209,7 @@ class Model(type):
class BaseBackend(object):
def reset(self):
self.__dict__ = {}
self.__init__()
@ -211,7 +218,8 @@ class BaseBackend(object):
def _url_module(self):
backend_module = self.__class__.__module__
backend_urls_module_name = backend_module.replace("models", "urls")
backend_urls_module = __import__(backend_urls_module_name, fromlist=['url_bases', 'url_paths'])
backend_urls_module = __import__(backend_urls_module_name, fromlist=[
'url_bases', 'url_paths'])
return backend_urls_module
@property
@ -306,6 +314,7 @@ class deprecated_base_decorator(base_decorator):
class MotoAPIBackend(BaseBackend):
def reset(self):
from moto.backends import BACKENDS
for name, backends in BACKENDS.items():
@ -315,4 +324,5 @@ class MotoAPIBackend(BaseBackend):
backend.reset()
self.__init__()
moto_api_backend = MotoAPIBackend()

View file

@ -59,6 +59,7 @@ class DynamicDictLoader(DictLoader):
Including the fixed (current) method version here to ensure performance benefit
even for those using older jinja versions.
"""
def get_source(self, environment, template):
if template in self.mapping:
source = self.mapping[template]
@ -77,7 +78,8 @@ class _TemplateEnvironmentMixin(object):
def __init__(self):
super(_TemplateEnvironmentMixin, self).__init__()
self.loader = DynamicDictLoader({})
self.environment = Environment(loader=self.loader, autoescape=self.should_autoescape)
self.environment = Environment(
loader=self.loader, autoescape=self.should_autoescape)
@property
def should_autoescape(self):
@ -127,12 +129,14 @@ class BaseResponse(_TemplateEnvironmentMixin):
self.body = self.body.decode('utf-8')
if not querystring:
querystring.update(parse_qs(urlparse(full_url).query, keep_blank_values=True))
querystring.update(
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:
decoded = json.loads(self.body)
target = request.headers.get('x-amz-target') or request.headers.get('X-Amz-Target')
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)
@ -161,7 +165,8 @@ class BaseResponse(_TemplateEnvironmentMixin):
if match:
region = match.group(1)
elif 'Authorization' in request.headers:
region = request.headers['Authorization'].split(",")[0].split("/")[2]
region = request.headers['Authorization'].split(",")[
0].split("/")[2]
else:
region = self.default_region
return region
@ -175,7 +180,8 @@ class BaseResponse(_TemplateEnvironmentMixin):
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]
@ -198,7 +204,8 @@ class BaseResponse(_TemplateEnvironmentMixin):
headers['status'] = str(headers['status'])
return status, headers, body
raise NotImplementedError("The {0} action has not been implemented".format(action))
raise NotImplementedError(
"The {0} action has not been implemented".format(action))
def _get_param(self, param_name, if_none=None):
val = self.querystring.get(param_name)
@ -258,7 +265,8 @@ class BaseResponse(_TemplateEnvironmentMixin):
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):
@ -291,7 +299,8 @@ class BaseResponse(_TemplateEnvironmentMixin):
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)
@ -327,7 +336,8 @@ class BaseResponse(_TemplateEnvironmentMixin):
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)
raise DryRunClientError(
error_type="DryRunOperation", message=message)
return True
@ -343,6 +353,7 @@ class MotoAPIResponse(BaseResponse):
class _RecursiveDictRef(object):
"""Store a recursive reference to dict."""
def __init__(self):
self.key = None
self.dic = {}
@ -502,12 +513,15 @@ def flatten_json_request_body(prefix, dict_body, spec):
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']))
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']))
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]))
@ -542,7 +556,8 @@ def xml_to_json_response(service_spec, operation, xml, result_node=None):
# 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':
@ -554,7 +569,8 @@ def xml_to_json_response(service_spec, operation, xml, result_node=None):
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']]
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:

View file

@ -98,7 +98,7 @@ class convert_httpretty_response(object):
result = self.callback(request, url, headers)
status, headers, response = result
if 'server' not in headers:
headers["server"] = "amazon.com"
headers["server"] = "amazon.com"
return status, headers, response