Adding server mode
This commit is contained in:
parent
c6f5afff75
commit
a728b2581a
31 changed files with 489 additions and 66 deletions
|
|
@ -2,6 +2,7 @@ import functools
|
|||
import re
|
||||
|
||||
from moto.packages.httpretty import HTTPretty
|
||||
from .utils import convert_regex_to_flask_path
|
||||
|
||||
|
||||
class MockAWS(object):
|
||||
|
|
@ -48,13 +49,56 @@ class BaseBackend(object):
|
|||
self.__init__()
|
||||
|
||||
@property
|
||||
def urls(self):
|
||||
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=['urls'])
|
||||
urls = backend_urls_module.urls
|
||||
backend_urls_module = __import__(backend_urls_module_name, fromlist=['url_bases', 'url_paths'])
|
||||
return backend_urls_module
|
||||
|
||||
@property
|
||||
def urls(self):
|
||||
"""
|
||||
A dictionary of the urls to be mocked with this service and the handlers
|
||||
that should be called in their place
|
||||
"""
|
||||
url_bases = self._url_module.url_bases
|
||||
unformatted_paths = self._url_module.url_paths
|
||||
|
||||
urls = {}
|
||||
for url_base in url_bases:
|
||||
for url_path, handler in unformatted_paths.iteritems():
|
||||
url = url_path.format(url_base)
|
||||
urls[url] = handler
|
||||
|
||||
return urls
|
||||
|
||||
@property
|
||||
def url_paths(self):
|
||||
"""
|
||||
A dictionary of the paths of the urls to be mocked with this service and
|
||||
the handlers that should be called in their place
|
||||
"""
|
||||
unformatted_paths = self._url_module.url_paths
|
||||
|
||||
paths = {}
|
||||
for unformatted_path, handler in unformatted_paths.iteritems():
|
||||
path = unformatted_path.format("")
|
||||
paths[path] = handler
|
||||
|
||||
return paths
|
||||
|
||||
@property
|
||||
def flask_paths(self):
|
||||
"""
|
||||
The url paths that will be used for the flask server
|
||||
"""
|
||||
paths = {}
|
||||
for url_path, handler in self.url_paths.iteritems():
|
||||
url_path = convert_regex_to_flask_path(url_path)
|
||||
paths[url_path] = handler
|
||||
|
||||
return paths
|
||||
|
||||
def decorator(self, func=None):
|
||||
if func:
|
||||
return MockAWS(self)(func)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ from moto.core.utils import headers_to_dict, camelcase_to_underscores, method_na
|
|||
|
||||
|
||||
class BaseResponse(object):
|
||||
def dispatch2(self, uri, body, headers):
|
||||
return self.dispatch(uri, body, headers)
|
||||
|
||||
def dispatch(self, uri, body, headers):
|
||||
if body:
|
||||
querystring = parse_qs(body)
|
||||
|
|
@ -13,7 +16,7 @@ class BaseResponse(object):
|
|||
self.path = uri.path
|
||||
self.querystring = querystring
|
||||
|
||||
action = querystring['Action'][0]
|
||||
action = querystring.get('Action', [""])[0]
|
||||
action = camelcase_to_underscores(action)
|
||||
|
||||
method_names = method_names_from_class(self.__class__)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
from collections import namedtuple
|
||||
import inspect
|
||||
import random
|
||||
import re
|
||||
from urlparse import parse_qs
|
||||
|
||||
from flask import request
|
||||
|
||||
|
||||
def headers_to_dict(headers):
|
||||
if isinstance(headers, dict):
|
||||
# If already dict, return
|
||||
return headers
|
||||
|
||||
result = {}
|
||||
for index, header in enumerate(headers.split("\r\n")):
|
||||
if not header:
|
||||
|
|
@ -51,3 +59,55 @@ def get_random_hex(length=8):
|
|||
|
||||
def get_random_message_id():
|
||||
return '{}-{}-{}-{}-{}'.format(get_random_hex(8), get_random_hex(4), get_random_hex(4), get_random_hex(4), get_random_hex(12))
|
||||
|
||||
|
||||
def convert_regex_to_flask_path(url_path):
|
||||
"""
|
||||
Converts a regex matching url to one that can be used with flask
|
||||
"""
|
||||
for token in ["$"]:
|
||||
url_path = url_path.replace(token, "")
|
||||
|
||||
def caller(reg):
|
||||
match_name, match_pattern = reg.groups()
|
||||
return '<regex("{0}"):{1}>'.format(match_pattern, match_name)
|
||||
|
||||
url_path = re.sub("\(\?P<(.*?)>(.*?)\)", caller, url_path)
|
||||
return url_path
|
||||
|
||||
|
||||
class convert_flask_to_httpretty_response(object):
|
||||
def __init__(self, callback):
|
||||
self.callback = callback
|
||||
|
||||
@property
|
||||
def __name__(self):
|
||||
# For instance methods, use class and method names. Otherwise
|
||||
# use module and method name
|
||||
if inspect.ismethod(self.callback):
|
||||
outer = self.callback.im_class.__name__
|
||||
else:
|
||||
outer = self.callback.__module__
|
||||
return "{}.{}".format(outer, self.callback.__name__)
|
||||
|
||||
def __call__(self, args=None, **kwargs):
|
||||
hostname = request.host_url
|
||||
method = request.method
|
||||
path = request.path
|
||||
query = request.query_string
|
||||
|
||||
# Mimic the HTTPretty URIInfo class
|
||||
URI = namedtuple('URI', 'hostname method path query')
|
||||
uri = URI(hostname, method, path, query)
|
||||
|
||||
body = request.data or query
|
||||
headers = dict(request.headers)
|
||||
result = self.callback(uri, body, headers)
|
||||
if isinstance(result, basestring):
|
||||
# result is just the response
|
||||
return result
|
||||
else:
|
||||
# result is a responce, headers tuple
|
||||
response, headers = result
|
||||
status = headers.pop('status', None)
|
||||
return response, status, headers
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue