From db275a15733e65117c79cc38f0b19e0afc92c5fa Mon Sep 17 00:00:00 2001 From: Waldemar Hummer Date: Thu, 11 Aug 2016 16:14:13 +1000 Subject: [PATCH] initial support for apigateway stages, in particular the update_stage method --- moto/apigateway/models.py | 33 ++++++++++++++++++++++++ moto/apigateway/responses.py | 13 ++++++++++ moto/apigateway/urls.py | 3 ++- moto/server.py | 2 +- tests/test_apigateway/test_apigateway.py | 32 ++++++++++++++++++++++- 5 files changed, 80 insertions(+), 3 deletions(-) diff --git a/moto/apigateway/models.py b/moto/apigateway/models.py index 000a3903e..d20e01d2c 100644 --- a/moto/apigateway/models.py +++ b/moto/apigateway/models.py @@ -149,6 +149,26 @@ class Resource(object): return self.resource_methods[method_type].pop('methodIntegration') +class Stage(dict): + def __init__(self, name=None, deployment_id=None): + super(Stage, self).__init__() + self['stageName'] = name + self['deploymentId'] = deployment_id + self['methodSettings'] = {} + self['variables'] = {} + self['description'] = '' + + def apply_operations(self, patch_operations): + for op in patch_operations: + if op['op'] == 'replace': + # TODO: match the path against the values hash + # (e.g., path could be '/*/*/logging/loglevel') + self[op['path']] = op['value'] + else: + raise Exception('Patch operation "%s" not implemented' % op['op']) + return self + + class RestAPI(object): def __init__(self, id, region_name, name, description): self.id = id @@ -158,6 +178,7 @@ class RestAPI(object): self.create_date = datetime.datetime.utcnow() self.deployments = {} + self.stages = {} self.resources = {} self.add_child('/') # Add default child @@ -202,6 +223,7 @@ class RestAPI(object): deployment_id = create_id() deployment = Deployment(deployment_id, name) self.deployments[deployment_id] = deployment + self.stages[name] = Stage(name=name, deployment_id=deployment_id) self.update_integration_mocks(name) @@ -276,6 +298,17 @@ class APIGatewayBackend(BaseBackend): method = resource.add_method(method_type, authorization_type) return method + def get_stage(self, function_id, stage_name): + api = self.get_rest_api(function_id) + return api.stages.get(stage_name) + + def update_stage(self, function_id, stage_name, patch_operations): + stage = self.get_stage(function_id, stage_name) + if not stage: + api = self.get_rest_api(function_id) + stage = api.stages[stage_name] = Stage() + return stage.apply_operations(patch_operations) + def get_method_response(self, function_id, resource_id, method_type, response_code): method = self.get_method(function_id, resource_id, method_type) method_response = method.get_response(response_code) diff --git a/moto/apigateway/responses.py b/moto/apigateway/responses.py index beda1fc17..89714eec9 100644 --- a/moto/apigateway/responses.py +++ b/moto/apigateway/responses.py @@ -95,6 +95,19 @@ class APIGatewayResponse(BaseResponse): method_response = self.backend.delete_method_response(function_id, resource_id, method_type, response_code) return 200, headers, json.dumps(method_response) + def stages(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + url_path_parts = self.path.split("/") + function_id = url_path_parts[2] + stage_name = url_path_parts[4] + + if self.method == 'GET': + stage_response = self.backend.get_stage(function_id, stage_name) + elif self.method == 'PATCH': + path_operations = self._get_param('patchOperations') + stage_response = self.backend.update_stage(function_id, stage_name, path_operations) + return 200, headers, json.dumps(stage_response) + def integrations(self, request, full_url, headers): self.setup_class(request, full_url, headers) url_path_parts = self.path.split("/") diff --git a/moto/apigateway/urls.py b/moto/apigateway/urls.py index e34dbbe61..d302a83f3 100644 --- a/moto/apigateway/urls.py +++ b/moto/apigateway/urls.py @@ -9,11 +9,12 @@ url_paths = { '{0}/restapis$': APIGatewayResponse().restapis, '{0}/restapis/(?P[^/]+)/?$': APIGatewayResponse().restapis_individual, '{0}/restapis/(?P[^/]+)/resources$': APIGatewayResponse().resources, + '{0}/restapis/(?P[^/]+)/stages/(?P[^/]+)/?$': APIGatewayResponse().stages, '{0}/restapis/(?P[^/]+)/deployments$': APIGatewayResponse().deployments, '{0}/restapis/(?P[^/]+)/deployments/(?P[^/]+)/?$': APIGatewayResponse().individual_deployment, '{0}/restapis/(?P[^/]+)/resources/(?P[^/]+)/?$': APIGatewayResponse().resource_individual, '{0}/restapis/(?P[^/]+)/resources/(?P[^/]+)/methods/(?P[^/]+)/?$': APIGatewayResponse().resource_methods, - '{0}/restapis/(?P[^/]+)/resources/(?P[^/]+)/methods/(?P[^/]+)/responses/200$': APIGatewayResponse().resource_method_responses, + '{0}/restapis/(?P[^/]+)/resources/(?P[^/]+)/methods/(?P[^/]+)/responses/(?P\d+)$': APIGatewayResponse().resource_method_responses, '{0}/restapis/(?P[^/]+)/resources/(?P[^/]+)/methods/(?P[^/]+)/integration/?$': APIGatewayResponse().integrations, '{0}/restapis/(?P[^/]+)/resources/(?P[^/]+)/methods/(?P[^/]+)/integration/responses/(?P\d+)/?$': APIGatewayResponse().integration_responses, } diff --git a/moto/server.py b/moto/server.py index d32c6f45e..2793b3dbb 100644 --- a/moto/server.py +++ b/moto/server.py @@ -16,7 +16,7 @@ from werkzeug.serving import run_simple from moto.backends import BACKENDS from moto.core.utils import convert_flask_to_httpretty_response -HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "HEAD"] +HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH"] class DomainDispatcherApplication(object): diff --git a/tests/test_apigateway/test_apigateway.py b/tests/test_apigateway/test_apigateway.py index 5521fa885..44c33b7a6 100644 --- a/tests/test_apigateway/test_apigateway.py +++ b/tests/test_apigateway/test_apigateway.py @@ -472,6 +472,7 @@ def test_integration_response(): @mock_apigateway def test_deployment(): client = boto3.client('apigateway', region_name='us-west-2') + stage_name = 'staging' response = client.create_rest_api( name='my_api', description='this is my api', @@ -480,7 +481,7 @@ def test_deployment(): response = client.create_deployment( restApiId=api_id, - stageName='staging', + stageName=stage_name, ) deployment_id = response['id'] @@ -511,6 +512,35 @@ def test_deployment(): ) len(response['items']).should.equal(0) + # test deployment stages + + stage = client.get_stage( + restApiId=api_id, + stageName=stage_name + ) + stage['stageName'].should.equal(stage_name) + stage['deploymentId'].should.equal(deployment_id) + + stage = client.update_stage( + restApiId=api_id, + stageName=stage_name, + patchOperations=[ + { + 'op': 'replace', + 'path': 'description', + 'value': '_new_description_' + }, + ] + ) + + stage = client.get_stage( + restApiId=api_id, + stageName=stage_name + ) + stage['stageName'].should.equal(stage_name) + stage['deploymentId'].should.equal(deployment_id) + stage['description'].should.equal('_new_description_') + @httpretty.activate @mock_apigateway