basic implementation of update rest api (#3951)
* basic implementation of update rest api * basic implementation of update rest api * basic implementation of update rest api * review comments from bblommers Co-authored-by: rajinder saini <rajinder.saini@c02vt5k2htd6.corp.climate.com>
This commit is contained in:
parent
290f6585c2
commit
fbbc8fc472
@ -376,7 +376,7 @@
|
|||||||
- [ ] update_model
|
- [ ] update_model
|
||||||
- [ ] update_request_validator
|
- [ ] update_request_validator
|
||||||
- [ ] update_resource
|
- [ ] update_resource
|
||||||
- [ ] update_rest_api
|
- [X] update_rest_api
|
||||||
- [X] update_stage
|
- [X] update_stage
|
||||||
- [ ] update_usage
|
- [ ] update_usage
|
||||||
- [X] update_usage_plan
|
- [X] update_usage_plan
|
||||||
|
@ -595,6 +595,8 @@ class RestAPI(CloudFormationModel):
|
|||||||
self.region_name = region_name
|
self.region_name = region_name
|
||||||
self.name = name
|
self.name = name
|
||||||
self.description = description
|
self.description = description
|
||||||
|
self.version = kwargs.get("version") or "V1"
|
||||||
|
self.binaryMediaTypes = kwargs.get("binaryMediaTypes") or []
|
||||||
self.create_date = int(time.time())
|
self.create_date = int(time.time())
|
||||||
self.api_key_source = kwargs.get("api_key_source") or "HEADER"
|
self.api_key_source = kwargs.get("api_key_source") or "HEADER"
|
||||||
self.policy = kwargs.get("policy") or None
|
self.policy = kwargs.get("policy") or None
|
||||||
@ -602,7 +604,9 @@ class RestAPI(CloudFormationModel):
|
|||||||
"types": ["EDGE"]
|
"types": ["EDGE"]
|
||||||
}
|
}
|
||||||
self.tags = kwargs.get("tags") or {}
|
self.tags = kwargs.get("tags") or {}
|
||||||
|
self.disableExecuteApiEndpoint = (
|
||||||
|
kwargs.get("disableExecuteApiEndpoint") or False
|
||||||
|
)
|
||||||
self.deployments = {}
|
self.deployments = {}
|
||||||
self.authorizers = {}
|
self.authorizers = {}
|
||||||
self.stages = {}
|
self.stages = {}
|
||||||
@ -618,13 +622,32 @@ class RestAPI(CloudFormationModel):
|
|||||||
"id": self.id,
|
"id": self.id,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"description": self.description,
|
"description": self.description,
|
||||||
|
"version": self.version,
|
||||||
|
"binaryMediaTypes": self.binaryMediaTypes,
|
||||||
"createdDate": int(time.time()),
|
"createdDate": int(time.time()),
|
||||||
"apiKeySource": self.api_key_source,
|
"apiKeySource": self.api_key_source,
|
||||||
"endpointConfiguration": self.endpoint_configuration,
|
"endpointConfiguration": self.endpoint_configuration,
|
||||||
"tags": self.tags,
|
"tags": self.tags,
|
||||||
"policy": self.policy,
|
"policy": self.policy,
|
||||||
|
"disableExecuteApiEndpoint": self.disableExecuteApiEndpoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def apply_patch_operations(self, patch_operations):
|
||||||
|
for op in patch_operations:
|
||||||
|
path = op["path"]
|
||||||
|
value = op["value"]
|
||||||
|
if op["op"] == "replace":
|
||||||
|
if "/name" in path:
|
||||||
|
self.name = value
|
||||||
|
if "/description" in path:
|
||||||
|
self.description = value
|
||||||
|
if "/apiKeySource" in path:
|
||||||
|
self.api_key_source = value
|
||||||
|
if "/binaryMediaTypes" in path:
|
||||||
|
self.binaryMediaTypes = value
|
||||||
|
if "/disableExecuteApiEndpoint" in path:
|
||||||
|
self.disableExecuteApiEndpoint = bool(value)
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
|
|
||||||
@ -905,6 +928,13 @@ class APIGatewayBackend(BaseBackend):
|
|||||||
raise RestAPINotFound()
|
raise RestAPINotFound()
|
||||||
return rest_api
|
return rest_api
|
||||||
|
|
||||||
|
def update_rest_api(self, function_id, patch_operations):
|
||||||
|
rest_api = self.apis.get(function_id)
|
||||||
|
if rest_api is None:
|
||||||
|
raise RestAPINotFound()
|
||||||
|
self.apis[function_id].apply_patch_operations(patch_operations)
|
||||||
|
return self.apis[function_id]
|
||||||
|
|
||||||
def list_apis(self):
|
def list_apis(self):
|
||||||
return self.apis.values()
|
return self.apis.values()
|
||||||
|
|
||||||
|
@ -39,6 +39,34 @@ class APIGatewayResponse(BaseResponse):
|
|||||||
def backend(self):
|
def backend(self):
|
||||||
return apigateway_backends[self.region]
|
return apigateway_backends[self.region]
|
||||||
|
|
||||||
|
def __validate_api_key_source(self, api_key_source):
|
||||||
|
if api_key_source and api_key_source not in API_KEY_SOURCES:
|
||||||
|
return self.error(
|
||||||
|
"ValidationException",
|
||||||
|
(
|
||||||
|
"1 validation error detected: "
|
||||||
|
"Value '{api_key_source}' at 'createRestApiInput.apiKeySource' failed "
|
||||||
|
"to satisfy constraint: Member must satisfy enum value set: "
|
||||||
|
"[AUTHORIZER, HEADER]"
|
||||||
|
).format(api_key_source=api_key_source),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __validate_endpoint_configuration(self, endpoint_configuration):
|
||||||
|
if endpoint_configuration and "types" in endpoint_configuration:
|
||||||
|
invalid_types = list(
|
||||||
|
set(endpoint_configuration["types"]) - set(ENDPOINT_CONFIGURATION_TYPES)
|
||||||
|
)
|
||||||
|
if invalid_types:
|
||||||
|
return self.error(
|
||||||
|
"ValidationException",
|
||||||
|
(
|
||||||
|
"1 validation error detected: Value '{endpoint_type}' "
|
||||||
|
"at 'createRestApiInput.endpointConfiguration.types' failed "
|
||||||
|
"to satisfy constraint: Member must satisfy enum value set: "
|
||||||
|
"[PRIVATE, EDGE, REGIONAL]"
|
||||||
|
).format(endpoint_type=invalid_types[0]),
|
||||||
|
)
|
||||||
|
|
||||||
def restapis(self, request, full_url, headers):
|
def restapis(self, request, full_url, headers):
|
||||||
self.setup_class(request, full_url, headers)
|
self.setup_class(request, full_url, headers)
|
||||||
|
|
||||||
@ -54,32 +82,13 @@ class APIGatewayResponse(BaseResponse):
|
|||||||
policy = self._get_param("policy")
|
policy = self._get_param("policy")
|
||||||
|
|
||||||
# Param validation
|
# Param validation
|
||||||
if api_key_source and api_key_source not in API_KEY_SOURCES:
|
response = self.__validate_api_key_source(api_key_source)
|
||||||
return self.error(
|
if response is not None:
|
||||||
"ValidationException",
|
return response
|
||||||
(
|
|
||||||
"1 validation error detected: "
|
|
||||||
"Value '{api_key_source}' at 'createRestApiInput.apiKeySource' failed "
|
|
||||||
"to satisfy constraint: Member must satisfy enum value set: "
|
|
||||||
"[AUTHORIZER, HEADER]"
|
|
||||||
).format(api_key_source=api_key_source),
|
|
||||||
)
|
|
||||||
|
|
||||||
if endpoint_configuration and "types" in endpoint_configuration:
|
response = self.__validate_endpoint_configuration(endpoint_configuration)
|
||||||
invalid_types = list(
|
if response is not None:
|
||||||
set(endpoint_configuration["types"])
|
return response
|
||||||
- set(ENDPOINT_CONFIGURATION_TYPES)
|
|
||||||
)
|
|
||||||
if invalid_types:
|
|
||||||
return self.error(
|
|
||||||
"ValidationException",
|
|
||||||
(
|
|
||||||
"1 validation error detected: Value '{endpoint_type}' "
|
|
||||||
"at 'createRestApiInput.endpointConfiguration.types' failed "
|
|
||||||
"to satisfy constraint: Member must satisfy enum value set: "
|
|
||||||
"[PRIVATE, EDGE, REGIONAL]"
|
|
||||||
).format(endpoint_type=invalid_types[0]),
|
|
||||||
)
|
|
||||||
|
|
||||||
rest_api = self.backend.create_rest_api(
|
rest_api = self.backend.create_rest_api(
|
||||||
name,
|
name,
|
||||||
@ -91,15 +100,37 @@ class APIGatewayResponse(BaseResponse):
|
|||||||
)
|
)
|
||||||
return 200, {}, json.dumps(rest_api.to_dict())
|
return 200, {}, json.dumps(rest_api.to_dict())
|
||||||
|
|
||||||
|
def __validte_rest_patch_operations(self, patch_operations):
|
||||||
|
for op in patch_operations:
|
||||||
|
path = op["path"]
|
||||||
|
value = op["value"]
|
||||||
|
if "apiKeySource" in path:
|
||||||
|
return self.__validate_api_key_source(value)
|
||||||
|
|
||||||
def restapis_individual(self, request, full_url, headers):
|
def restapis_individual(self, request, full_url, headers):
|
||||||
self.setup_class(request, full_url, headers)
|
self.setup_class(request, full_url, headers)
|
||||||
function_id = self.path.replace("/restapis/", "", 1).split("/")[0]
|
function_id = self.path.replace("/restapis/", "", 1).split("/")[0]
|
||||||
|
|
||||||
if self.method == "GET":
|
if self.method == "GET":
|
||||||
rest_api = self.backend.get_rest_api(function_id)
|
rest_api = self.backend.get_rest_api(function_id)
|
||||||
return 200, {}, json.dumps(rest_api.to_dict())
|
|
||||||
elif self.method == "DELETE":
|
elif self.method == "DELETE":
|
||||||
rest_api = self.backend.delete_rest_api(function_id)
|
rest_api = self.backend.delete_rest_api(function_id)
|
||||||
|
elif self.method == "PATCH":
|
||||||
|
patch_operations = self._get_param("patchOperations")
|
||||||
|
response = self.__validte_rest_patch_operations(patch_operations)
|
||||||
|
if response is not None:
|
||||||
|
return response
|
||||||
|
try:
|
||||||
|
rest_api = self.backend.update_rest_api(function_id, patch_operations)
|
||||||
|
except RestAPINotFound as error:
|
||||||
|
return (
|
||||||
|
error.code,
|
||||||
|
{},
|
||||||
|
'{{"message":"{0}","code":"{1}"}}'.format(
|
||||||
|
error.message, error.error_type
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
return 200, {}, json.dumps(rest_api.to_dict())
|
return 200, {}, json.dumps(rest_api.to_dict())
|
||||||
|
|
||||||
def resources(self, request, full_url, headers):
|
def resources(self, request, full_url, headers):
|
||||||
|
@ -31,13 +31,71 @@ def test_create_and_get_rest_api():
|
|||||||
"id": api_id,
|
"id": api_id,
|
||||||
"name": "my_api",
|
"name": "my_api",
|
||||||
"description": "this is my api",
|
"description": "this is my api",
|
||||||
|
"version": "V1",
|
||||||
|
"binaryMediaTypes": [],
|
||||||
"apiKeySource": "HEADER",
|
"apiKeySource": "HEADER",
|
||||||
"endpointConfiguration": {"types": ["EDGE"]},
|
"endpointConfiguration": {"types": ["EDGE"]},
|
||||||
"tags": {},
|
"tags": {},
|
||||||
|
"disableExecuteApiEndpoint": False,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_upate_rest_api():
|
||||||
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
patchOperations = [
|
||||||
|
{"op": "replace", "path": "/name", "value": "new-name"},
|
||||||
|
{"op": "replace", "path": "/description", "value": "new-description"},
|
||||||
|
{"op": "replace", "path": "/apiKeySource", "value": "AUTHORIZER"},
|
||||||
|
{"op": "replace", "path": "/binaryMediaTypes", "value": "image/jpeg"},
|
||||||
|
{"op": "replace", "path": "/disableExecuteApiEndpoint", "value": "True"},
|
||||||
|
]
|
||||||
|
|
||||||
|
response = client.update_rest_api(restApiId=api_id, patchOperations=patchOperations)
|
||||||
|
response.pop("ResponseMetadata")
|
||||||
|
response.pop("createdDate")
|
||||||
|
response.pop("binaryMediaTypes")
|
||||||
|
response.should.equal(
|
||||||
|
{
|
||||||
|
"id": api_id,
|
||||||
|
"name": "new-name",
|
||||||
|
"version": "V1",
|
||||||
|
"description": "new-description",
|
||||||
|
"apiKeySource": "AUTHORIZER",
|
||||||
|
"endpointConfiguration": {"types": ["EDGE"]},
|
||||||
|
"tags": {},
|
||||||
|
"disableExecuteApiEndpoint": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# should fail with wrong apikeysoruce
|
||||||
|
patchOperations = [
|
||||||
|
{"op": "replace", "path": "/apiKeySource", "value": "Wrong-value-AUTHORIZER"}
|
||||||
|
]
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
response = client.update_rest_api(
|
||||||
|
restApiId=api_id, patchOperations=patchOperations
|
||||||
|
)
|
||||||
|
|
||||||
|
ex.value.response["Error"]["Message"].should.equal(
|
||||||
|
"1 validation error detected: Value 'Wrong-value-AUTHORIZER' at 'createRestApiInput.apiKeySource' failed to satisfy constraint: Member must satisfy enum value set: [AUTHORIZER, HEADER]"
|
||||||
|
)
|
||||||
|
ex.value.response["Error"]["Code"].should.equal("ValidationException")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_upate_rest_api_invalid_api_id():
|
||||||
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
patchOperations = [
|
||||||
|
{"op": "replace", "path": "/apiKeySource", "value": "AUTHORIZER"}
|
||||||
|
]
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
client.update_rest_api(restApiId="api_id", patchOperations=patchOperations)
|
||||||
|
ex.value.response["Error"]["Code"].should.equal("NotFoundException")
|
||||||
|
|
||||||
|
|
||||||
@mock_apigateway
|
@mock_apigateway
|
||||||
def test_list_and_delete_apis():
|
def test_list_and_delete_apis():
|
||||||
client = boto3.client("apigateway", region_name="us-west-2")
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
Loading…
Reference in New Issue
Block a user