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

@ -1,6 +1,6 @@
from __future__ import unicode_literals
from .models import apigateway_backends
from ..core.models import MockAWS, base_decorator, HttprettyMockAWS, deprecated_base_decorator
from ..core.models import base_decorator, deprecated_base_decorator
apigateway_backend = apigateway_backends['us-east-1']
mock_apigateway = base_decorator(apigateway_backends)

View file

@ -4,9 +4,7 @@ from moto.core.exceptions import RESTError
class StageNotFoundException(RESTError):
code = 404
def __init__(self):
super(StageNotFoundException, self).__init__(
"NotFoundException", "Invalid stage identifier specified")

View file

@ -14,15 +14,18 @@ STAGE_URL = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_nam
class Deployment(dict):
def __init__(self, deployment_id, name, description=""):
super(Deployment, self).__init__()
self['id'] = deployment_id
self['stageName'] = name
self['description'] = description
self['createdDate'] = iso_8601_datetime_with_milliseconds(datetime.datetime.now())
self['createdDate'] = iso_8601_datetime_with_milliseconds(
datetime.datetime.now())
class IntegrationResponse(dict):
def __init__(self, status_code, selection_pattern=None):
self['responseTemplates'] = {"application/json": None}
self['statusCode'] = status_code
@ -31,6 +34,7 @@ class IntegrationResponse(dict):
class Integration(dict):
def __init__(self, integration_type, uri, http_method, request_templates=None):
super(Integration, self).__init__()
self['type'] = integration_type
@ -42,7 +46,8 @@ class Integration(dict):
}
def create_integration_response(self, status_code, selection_pattern):
integration_response = IntegrationResponse(status_code, selection_pattern)
integration_response = IntegrationResponse(
status_code, selection_pattern)
self["integrationResponses"][status_code] = integration_response
return integration_response
@ -54,12 +59,14 @@ class Integration(dict):
class MethodResponse(dict):
def __init__(self, status_code):
super(MethodResponse, self).__init__()
self['statusCode'] = status_code
class Method(dict):
def __init__(self, method_type, authorization_type):
super(Method, self).__init__()
self.update(dict(
@ -86,6 +93,7 @@ class Method(dict):
class Resource(object):
def __init__(self, id, region_name, api_id, path_part, parent_id):
self.id = id
self.region_name = region_name
@ -127,14 +135,17 @@ class Resource(object):
if integration_type == 'HTTP':
uri = integration['uri']
requests_func = getattr(requests, integration['httpMethod'].lower())
requests_func = getattr(requests, integration[
'httpMethod'].lower())
response = requests_func(uri)
else:
raise NotImplementedError("The {0} type has not been implemented".format(integration_type))
raise NotImplementedError(
"The {0} type has not been implemented".format(integration_type))
return response.status_code, response.text
def add_method(self, method_type, authorization_type):
method = Method(method_type=method_type, authorization_type=authorization_type)
method = Method(method_type=method_type,
authorization_type=authorization_type)
self.resource_methods[method_type] = method
return method
@ -142,7 +153,8 @@ class Resource(object):
return self.resource_methods[method_type]
def add_integration(self, method_type, integration_type, uri, request_templates=None):
integration = Integration(integration_type, uri, method_type, request_templates=request_templates)
integration = Integration(
integration_type, uri, method_type, request_templates=request_templates)
self.resource_methods[method_type]['methodIntegration'] = integration
return integration
@ -155,9 +167,8 @@ class Resource(object):
class Stage(dict):
def __init__(self, name=None, deployment_id=None, variables=None,
description='',cacheClusterEnabled=False,cacheClusterSize=None):
description='', cacheClusterEnabled=False, cacheClusterSize=None):
super(Stage, self).__init__()
if variables is None:
variables = {}
@ -190,21 +201,24 @@ class Stage(dict):
elif op['op'] == 'replace':
# Method Settings drop into here
# (e.g., path could be '/*/*/logging/loglevel')
split_path = op['path'].split('/',3)
if len(split_path)!=4:
split_path = op['path'].split('/', 3)
if len(split_path) != 4:
continue
self._patch_method_setting('/'.join(split_path[1:3]),split_path[3],op['value'])
self._patch_method_setting(
'/'.join(split_path[1:3]), split_path[3], op['value'])
else:
raise Exception('Patch operation "%s" not implemented' % op['op'])
raise Exception(
'Patch operation "%s" not implemented' % op['op'])
return self
def _patch_method_setting(self,resource_path_and_method,key,value):
def _patch_method_setting(self, resource_path_and_method, key, value):
updated_key = self._method_settings_translations(key)
if updated_key is not None:
if resource_path_and_method not in self['methodSettings']:
self['methodSettings'][resource_path_and_method] = self._get_default_method_settings()
self['methodSettings'][resource_path_and_method][updated_key] = self._convert_to_type(updated_key,value)
self['methodSettings'][
resource_path_and_method] = self._get_default_method_settings()
self['methodSettings'][resource_path_and_method][
updated_key] = self._convert_to_type(updated_key, value)
def _get_default_method_settings(self):
return {
@ -219,18 +233,18 @@ class Stage(dict):
"requireAuthorizationForCacheControl": True
}
def _method_settings_translations(self,key):
def _method_settings_translations(self, key):
mappings = {
'metrics/enabled' :'metricsEnabled',
'logging/loglevel' : 'loggingLevel',
'logging/dataTrace' : 'dataTraceEnabled' ,
'throttling/burstLimit' : 'throttlingBurstLimit',
'throttling/rateLimit' : 'throttlingRateLimit',
'caching/enabled' : 'cachingEnabled',
'caching/ttlInSeconds' : 'cacheTtlInSeconds',
'caching/dataEncrypted' : 'cacheDataEncrypted',
'caching/requireAuthorizationForCacheControl' : 'requireAuthorizationForCacheControl',
'caching/unauthorizedCacheControlHeaderStrategy' : 'unauthorizedCacheControlHeaderStrategy'
'metrics/enabled': 'metricsEnabled',
'logging/loglevel': 'loggingLevel',
'logging/dataTrace': 'dataTraceEnabled',
'throttling/burstLimit': 'throttlingBurstLimit',
'throttling/rateLimit': 'throttlingRateLimit',
'caching/enabled': 'cachingEnabled',
'caching/ttlInSeconds': 'cacheTtlInSeconds',
'caching/dataEncrypted': 'cacheDataEncrypted',
'caching/requireAuthorizationForCacheControl': 'requireAuthorizationForCacheControl',
'caching/unauthorizedCacheControlHeaderStrategy': 'unauthorizedCacheControlHeaderStrategy'
}
if key in mappings:
@ -238,21 +252,21 @@ class Stage(dict):
else:
None
def _str2bool(self,v):
def _str2bool(self, v):
return v.lower() == "true"
def _convert_to_type(self,key,val):
def _convert_to_type(self, key, val):
type_mappings = {
'metricsEnabled' : 'bool',
'loggingLevel' : 'str',
'dataTraceEnabled' : 'bool',
'throttlingBurstLimit' : 'int',
'throttlingRateLimit' : 'float',
'cachingEnabled' : 'bool',
'cacheTtlInSeconds' : 'int',
'cacheDataEncrypted' : 'bool',
'requireAuthorizationForCacheControl' :'bool',
'unauthorizedCacheControlHeaderStrategy' : 'str'
'metricsEnabled': 'bool',
'loggingLevel': 'str',
'dataTraceEnabled': 'bool',
'throttlingBurstLimit': 'int',
'throttlingRateLimit': 'float',
'cachingEnabled': 'bool',
'cacheTtlInSeconds': 'int',
'cacheDataEncrypted': 'bool',
'requireAuthorizationForCacheControl': 'bool',
'unauthorizedCacheControlHeaderStrategy': 'str'
}
if key in type_mappings:
@ -261,7 +275,7 @@ class Stage(dict):
if type_value == 'bool':
return self._str2bool(val)
elif type_value == 'int':
return int(val)
return int(val)
elif type_value == 'float':
return float(val)
else:
@ -269,10 +283,8 @@ class Stage(dict):
else:
return str(val)
def _apply_operation_to_variables(self,op):
key = op['path'][op['path'].rindex("variables/")+10:]
def _apply_operation_to_variables(self, op):
key = op['path'][op['path'].rindex("variables/") + 10:]
if op['op'] == 'remove':
self['variables'].pop(key, None)
elif op['op'] == 'replace':
@ -281,8 +293,8 @@ class Stage(dict):
raise Exception('Patch operation "%s" not implemented' % op['op'])
class RestAPI(object):
def __init__(self, id, region_name, name, description):
self.id = id
self.region_name = region_name
@ -306,7 +318,8 @@ class RestAPI(object):
def add_child(self, path, parent_id=None):
child_id = create_id()
child = Resource(id=child_id, region_name=self.region_name, api_id=self.id, path_part=path, parent_id=parent_id)
child = Resource(id=child_id, region_name=self.region_name,
api_id=self.id, path_part=path, parent_id=parent_id)
self.resources[child_id] = child
return child
@ -326,25 +339,28 @@ class RestAPI(object):
return status_code, {}, response
def update_integration_mocks(self, stage_name):
stage_url = STAGE_URL.format(api_id=self.id.upper(), region_name=self.region_name, stage_name=stage_name)
responses.add_callback(responses.GET, stage_url, callback=self.resource_callback)
stage_url = STAGE_URL.format(api_id=self.id.upper(),
region_name=self.region_name, stage_name=stage_name)
responses.add_callback(responses.GET, stage_url,
callback=self.resource_callback)
def create_stage(self, name, deployment_id,variables=None,description='',cacheClusterEnabled=None,cacheClusterSize=None):
def create_stage(self, name, deployment_id, variables=None, description='', cacheClusterEnabled=None, cacheClusterSize=None):
if variables is None:
variables = {}
stage = Stage(name=name, deployment_id=deployment_id,variables=variables,
description=description,cacheClusterSize=cacheClusterSize,cacheClusterEnabled=cacheClusterEnabled)
stage = Stage(name=name, deployment_id=deployment_id, variables=variables,
description=description, cacheClusterSize=cacheClusterSize, cacheClusterEnabled=cacheClusterEnabled)
self.stages[name] = stage
self.update_integration_mocks(name)
return stage
def create_deployment(self, name, description="",stage_variables=None):
def create_deployment(self, name, description="", stage_variables=None):
if stage_variables is None:
stage_variables = {}
deployment_id = create_id()
deployment = Deployment(deployment_id, name, description)
self.deployments[deployment_id] = deployment
self.stages[name] = Stage(name=name, deployment_id=deployment_id,variables=stage_variables)
self.stages[name] = Stage(
name=name, deployment_id=deployment_id, variables=stage_variables)
self.update_integration_mocks(name)
return deployment
@ -353,7 +369,7 @@ class RestAPI(object):
return self.deployments[deployment_id]
def get_stages(self):
return list(self.stages.values())
return list(self.stages.values())
def get_deployments(self):
return list(self.deployments.values())
@ -363,6 +379,7 @@ class RestAPI(object):
class APIGatewayBackend(BaseBackend):
def __init__(self, region_name):
super(APIGatewayBackend, self).__init__()
self.apis = {}
@ -429,19 +446,17 @@ class APIGatewayBackend(BaseBackend):
else:
return stage
def get_stages(self, function_id):
api = self.get_rest_api(function_id)
return api.get_stages()
def create_stage(self, function_id, stage_name, deploymentId,
variables=None,description='',cacheClusterEnabled=None,cacheClusterSize=None):
variables=None, description='', cacheClusterEnabled=None, cacheClusterSize=None):
if variables is None:
variables = {}
api = self.get_rest_api(function_id)
api.create_stage(stage_name,deploymentId,variables=variables,
description=description,cacheClusterEnabled=cacheClusterEnabled,cacheClusterSize=cacheClusterSize)
api.create_stage(stage_name, deploymentId, variables=variables,
description=description, cacheClusterEnabled=cacheClusterEnabled, cacheClusterSize=cacheClusterSize)
return api.stages.get(stage_name)
def update_stage(self, function_id, stage_name, patch_operations):
@ -467,10 +482,10 @@ class APIGatewayBackend(BaseBackend):
return method_response
def create_integration(self, function_id, resource_id, method_type, integration_type, uri,
request_templates=None):
request_templates=None):
resource = self.get_resource(function_id, resource_id)
integration = resource.add_integration(method_type, integration_type, uri,
request_templates=request_templates)
request_templates=request_templates)
return integration
def get_integration(self, function_id, resource_id, method_type):
@ -482,25 +497,31 @@ class APIGatewayBackend(BaseBackend):
return resource.delete_integration(method_type)
def create_integration_response(self, function_id, resource_id, method_type, status_code, selection_pattern):
integration = self.get_integration(function_id, resource_id, method_type)
integration_response = integration.create_integration_response(status_code, selection_pattern)
integration = self.get_integration(
function_id, resource_id, method_type)
integration_response = integration.create_integration_response(
status_code, selection_pattern)
return integration_response
def get_integration_response(self, function_id, resource_id, method_type, status_code):
integration = self.get_integration(function_id, resource_id, method_type)
integration_response = integration.get_integration_response(status_code)
integration = self.get_integration(
function_id, resource_id, method_type)
integration_response = integration.get_integration_response(
status_code)
return integration_response
def delete_integration_response(self, function_id, resource_id, method_type, status_code):
integration = self.get_integration(function_id, resource_id, method_type)
integration_response = integration.delete_integration_response(status_code)
integration = self.get_integration(
function_id, resource_id, method_type)
integration_response = integration.delete_integration_response(
status_code)
return integration_response
def create_deployment(self, function_id, name, description ="", stage_variables=None):
def create_deployment(self, function_id, name, description="", stage_variables=None):
if stage_variables is None:
stage_variables = {}
api = self.get_rest_api(function_id)
deployment = api.create_deployment(name, description,stage_variables)
deployment = api.create_deployment(name, description, stage_variables)
return deployment
def get_deployment(self, function_id, deployment_id):
@ -515,6 +536,8 @@ class APIGatewayBackend(BaseBackend):
api = self.get_rest_api(function_id)
return api.delete_deployment(deployment_id)
apigateway_backends = {}
for region_name in ['us-east-1', 'us-west-2', 'eu-west-1', 'ap-northeast-1']: # Not available in boto yet
# Not available in boto yet
for region_name in ['us-east-1', 'us-west-2', 'eu-west-1', 'ap-northeast-1']:
apigateway_backends[region_name] = APIGatewayBackend(region_name)

View file

@ -12,7 +12,6 @@ class APIGatewayResponse(BaseResponse):
def _get_param(self, key):
return json.loads(self.body).get(key)
def _get_param_with_default_value(self, key, default):
jsonbody = json.loads(self.body)
@ -69,7 +68,8 @@ class APIGatewayResponse(BaseResponse):
resource = self.backend.get_resource(function_id, resource_id)
elif self.method == 'POST':
path_part = self._get_param("pathPart")
resource = self.backend.create_resource(function_id, resource_id, path_part)
resource = self.backend.create_resource(
function_id, resource_id, path_part)
elif self.method == 'DELETE':
resource = self.backend.delete_resource(function_id, resource_id)
return 200, {}, json.dumps(resource.to_dict())
@ -82,11 +82,13 @@ class APIGatewayResponse(BaseResponse):
method_type = url_path_parts[6]
if self.method == 'GET':
method = self.backend.get_method(function_id, resource_id, method_type)
method = self.backend.get_method(
function_id, resource_id, method_type)
return 200, {}, json.dumps(method)
elif self.method == 'PUT':
authorization_type = self._get_param("authorizationType")
method = self.backend.create_method(function_id, resource_id, method_type, authorization_type)
method = self.backend.create_method(
function_id, resource_id, method_type, authorization_type)
return 200, {}, json.dumps(method)
def resource_method_responses(self, request, full_url, headers):
@ -98,11 +100,14 @@ class APIGatewayResponse(BaseResponse):
response_code = url_path_parts[8]
if self.method == 'GET':
method_response = self.backend.get_method_response(function_id, resource_id, method_type, response_code)
method_response = self.backend.get_method_response(
function_id, resource_id, method_type, response_code)
elif self.method == 'PUT':
method_response = self.backend.create_method_response(function_id, resource_id, method_type, response_code)
method_response = self.backend.create_method_response(
function_id, resource_id, method_type, response_code)
elif self.method == 'DELETE':
method_response = self.backend.delete_method_response(function_id, resource_id, method_type, response_code)
method_response = self.backend.delete_method_response(
function_id, resource_id, method_type, response_code)
return 200, {}, json.dumps(method_response)
def restapis_stages(self, request, full_url, headers):
@ -113,10 +118,13 @@ class APIGatewayResponse(BaseResponse):
if self.method == 'POST':
stage_name = self._get_param("stageName")
deployment_id = self._get_param("deploymentId")
stage_variables = self._get_param_with_default_value('variables',{})
description = self._get_param_with_default_value('description','')
cacheClusterEnabled = self._get_param_with_default_value('cacheClusterEnabled',False)
cacheClusterSize = self._get_param_with_default_value('cacheClusterSize',None)
stage_variables = self._get_param_with_default_value(
'variables', {})
description = self._get_param_with_default_value('description', '')
cacheClusterEnabled = self._get_param_with_default_value(
'cacheClusterEnabled', False)
cacheClusterSize = self._get_param_with_default_value(
'cacheClusterSize', None)
stage_response = self.backend.create_stage(function_id, stage_name, deployment_id,
variables=stage_variables, description=description,
@ -135,12 +143,14 @@ class APIGatewayResponse(BaseResponse):
if self.method == 'GET':
try:
stage_response = self.backend.get_stage(function_id, stage_name)
stage_response = self.backend.get_stage(
function_id, stage_name)
except StageNotFoundException as error:
return error.code, {},'{{"message":"{0}","code":"{1}"}}'.format(error.message,error.error_type)
return error.code, {}, '{{"message":"{0}","code":"{1}"}}'.format(error.message, error.error_type)
elif self.method == 'PATCH':
patch_operations = self._get_param('patchOperations')
stage_response = self.backend.update_stage(function_id, stage_name, patch_operations)
stage_response = self.backend.update_stage(
function_id, stage_name, patch_operations)
return 200, {}, json.dumps(stage_response)
def integrations(self, request, full_url, headers):
@ -151,14 +161,17 @@ class APIGatewayResponse(BaseResponse):
method_type = url_path_parts[6]
if self.method == 'GET':
integration_response = self.backend.get_integration(function_id, resource_id, method_type)
integration_response = self.backend.get_integration(
function_id, resource_id, method_type)
elif self.method == 'PUT':
integration_type = self._get_param('type')
uri = self._get_param('uri')
request_templates = self._get_param('requestTemplates')
integration_response = self.backend.create_integration(function_id, resource_id, method_type, integration_type, uri, request_templates=request_templates)
integration_response = self.backend.create_integration(
function_id, resource_id, method_type, integration_type, uri, request_templates=request_templates)
elif self.method == 'DELETE':
integration_response = self.backend.delete_integration(function_id, resource_id, method_type)
integration_response = self.backend.delete_integration(
function_id, resource_id, method_type)
return 200, {}, json.dumps(integration_response)
def integration_responses(self, request, full_url, headers):
@ -193,9 +206,11 @@ class APIGatewayResponse(BaseResponse):
return 200, {}, json.dumps({"item": deployments})
elif self.method == 'POST':
name = self._get_param("stageName")
description = self._get_param_with_default_value("description","")
stage_variables = self._get_param_with_default_value('variables',{})
deployment = self.backend.create_deployment(function_id, name, description,stage_variables)
description = self._get_param_with_default_value("description", "")
stage_variables = self._get_param_with_default_value(
'variables', {})
deployment = self.backend.create_deployment(
function_id, name, description, stage_variables)
return 200, {}, json.dumps(deployment)
def individual_deployment(self, request, full_url, headers):
@ -205,7 +220,9 @@ class APIGatewayResponse(BaseResponse):
deployment_id = url_path_parts[4]
if self.method == 'GET':
deployment = self.backend.get_deployment(function_id, deployment_id)
deployment = self.backend.get_deployment(
function_id, deployment_id)
elif self.method == 'DELETE':
deployment = self.backend.delete_deployment(function_id, deployment_id)
deployment = self.backend.delete_deployment(
function_id, deployment_id)
return 200, {}, json.dumps(deployment)