API Gateway: put_rest_api and import_rest_api (#5140)
This commit is contained in:
parent
ecfd6d6065
commit
8bbd242d75
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
## apigateway
|
## apigateway
|
||||||
<details>
|
<details>
|
||||||
<summary>62% implemented</summary>
|
<summary>65% implemented</summary>
|
||||||
|
|
||||||
- [X] create_api_key
|
- [X] create_api_key
|
||||||
- [X] create_authorizer
|
- [X] create_authorizer
|
||||||
@ -93,7 +93,7 @@
|
|||||||
- [X] get_request_validator
|
- [X] get_request_validator
|
||||||
- [X] get_request_validators
|
- [X] get_request_validators
|
||||||
- [X] get_resource
|
- [X] get_resource
|
||||||
- [ ] get_resources
|
- [X] get_resources
|
||||||
- [X] get_rest_api
|
- [X] get_rest_api
|
||||||
- [ ] get_rest_apis
|
- [ ] get_rest_apis
|
||||||
- [ ] get_sdk
|
- [ ] get_sdk
|
||||||
@ -111,13 +111,13 @@
|
|||||||
- [X] get_vpc_links
|
- [X] get_vpc_links
|
||||||
- [ ] import_api_keys
|
- [ ] import_api_keys
|
||||||
- [ ] import_documentation_parts
|
- [ ] import_documentation_parts
|
||||||
- [ ] import_rest_api
|
- [X] import_rest_api
|
||||||
- [X] put_gateway_response
|
- [X] put_gateway_response
|
||||||
- [X] put_integration
|
- [X] put_integration
|
||||||
- [X] put_integration_response
|
- [X] put_integration_response
|
||||||
- [X] put_method
|
- [X] put_method
|
||||||
- [X] put_method_response
|
- [X] put_method_response
|
||||||
- [ ] put_rest_api
|
- [X] put_rest_api
|
||||||
- [ ] tag_resource
|
- [ ] tag_resource
|
||||||
- [ ] test_invoke_authorizer
|
- [ ] test_invoke_authorizer
|
||||||
- [ ] test_invoke_method
|
- [ ] test_invoke_method
|
||||||
|
@ -100,7 +100,7 @@ apigateway
|
|||||||
- [X] get_request_validator
|
- [X] get_request_validator
|
||||||
- [X] get_request_validators
|
- [X] get_request_validators
|
||||||
- [X] get_resource
|
- [X] get_resource
|
||||||
- [ ] get_resources
|
- [X] get_resources
|
||||||
- [X] get_rest_api
|
- [X] get_rest_api
|
||||||
- [ ] get_rest_apis
|
- [ ] get_rest_apis
|
||||||
- [ ] get_sdk
|
- [ ] get_sdk
|
||||||
@ -122,13 +122,21 @@ apigateway
|
|||||||
|
|
||||||
- [ ] import_api_keys
|
- [ ] import_api_keys
|
||||||
- [ ] import_documentation_parts
|
- [ ] import_documentation_parts
|
||||||
- [ ] import_rest_api
|
- [X] import_rest_api
|
||||||
|
|
||||||
|
Only a subset of the OpenAPI spec 3.x is currently implemented.
|
||||||
|
|
||||||
|
|
||||||
- [X] put_gateway_response
|
- [X] put_gateway_response
|
||||||
- [X] put_integration
|
- [X] put_integration
|
||||||
- [X] put_integration_response
|
- [X] put_integration_response
|
||||||
- [X] put_method
|
- [X] put_method
|
||||||
- [X] put_method_response
|
- [X] put_method_response
|
||||||
- [ ] put_rest_api
|
- [X] put_rest_api
|
||||||
|
|
||||||
|
Only a subset of the OpenAPI spec 3.x is currently implemented.
|
||||||
|
|
||||||
|
|
||||||
- [ ] tag_resource
|
- [ ] tag_resource
|
||||||
- [ ] test_invoke_authorizer
|
- [ ] test_invoke_authorizer
|
||||||
- [ ] test_invoke_method
|
- [ ] test_invoke_method
|
||||||
|
@ -50,6 +50,25 @@ class IntegrationMethodNotDefined(BadRequestException):
|
|||||||
super().__init__("Enumeration value for HttpMethod must be non-empty")
|
super().__init__("Enumeration value for HttpMethod must be non-empty")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidOpenAPIDocumentException(BadRequestException):
|
||||||
|
def __init__(self, cause):
|
||||||
|
super().__init__(
|
||||||
|
f"Failed to parse the uploaded OpenAPI document due to: {cause.message}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidOpenApiDocVersionException(BadRequestException):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Only OpenAPI 3.x.x are currently supported")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidOpenApiModeException(BadRequestException):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(
|
||||||
|
'Enumeration value of OpenAPI import mode must be "overwrite" or "merge"',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InvalidResourcePathException(BadRequestException):
|
class InvalidResourcePathException(BadRequestException):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@ -233,6 +252,13 @@ class BasePathNotFoundException(NotFoundException):
|
|||||||
super().__init__("Invalid base path mapping identifier specified")
|
super().__init__("Invalid base path mapping identifier specified")
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceIdNotFoundException(NotFoundException):
|
||||||
|
code = 404
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Invalid resource identifier specified")
|
||||||
|
|
||||||
|
|
||||||
class VpcLinkNotFound(NotFoundException):
|
class VpcLinkNotFound(NotFoundException):
|
||||||
code = 404
|
code = 404
|
||||||
|
|
||||||
|
@ -6,10 +6,13 @@ import re
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
|
from openapi_spec_validator import validate_spec
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
import responses
|
import responses
|
||||||
|
|
||||||
|
from openapi_spec_validator.exceptions import OpenAPIValidationError
|
||||||
from moto.core import get_account_id, BaseBackend, BaseModel, CloudFormationModel
|
from moto.core import get_account_id, BaseBackend, BaseModel, CloudFormationModel
|
||||||
from .utils import create_id, to_path
|
from .utils import create_id, to_path
|
||||||
from moto.core.utils import path_url, BackendDict
|
from moto.core.utils import path_url, BackendDict
|
||||||
@ -27,9 +30,13 @@ from .exceptions import (
|
|||||||
InvalidArn,
|
InvalidArn,
|
||||||
InvalidIntegrationArn,
|
InvalidIntegrationArn,
|
||||||
InvalidHttpEndpoint,
|
InvalidHttpEndpoint,
|
||||||
|
InvalidOpenAPIDocumentException,
|
||||||
|
InvalidOpenApiDocVersionException,
|
||||||
|
InvalidOpenApiModeException,
|
||||||
InvalidResourcePathException,
|
InvalidResourcePathException,
|
||||||
AuthorizerNotFoundException,
|
AuthorizerNotFoundException,
|
||||||
StageNotFoundException,
|
StageNotFoundException,
|
||||||
|
ResourceIdNotFoundException,
|
||||||
RoleNotSpecified,
|
RoleNotSpecified,
|
||||||
NoIntegrationDefined,
|
NoIntegrationDefined,
|
||||||
NoIntegrationResponseDefined,
|
NoIntegrationResponseDefined,
|
||||||
@ -184,7 +191,7 @@ class Method(CloudFormationModel, dict):
|
|||||||
requestValidatorId=kwargs.get("request_validator_id"),
|
requestValidatorId=kwargs.get("request_validator_id"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.method_responses = {}
|
self["methodResponses"] = {}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cloudformation_name_type():
|
def cloudformation_name_type():
|
||||||
@ -229,14 +236,14 @@ class Method(CloudFormationModel, dict):
|
|||||||
method_response = MethodResponse(
|
method_response = MethodResponse(
|
||||||
response_code, response_models, response_parameters
|
response_code, response_models, response_parameters
|
||||||
)
|
)
|
||||||
self.method_responses[response_code] = method_response
|
self["methodResponses"][response_code] = method_response
|
||||||
return method_response
|
return method_response
|
||||||
|
|
||||||
def get_response(self, response_code):
|
def get_response(self, response_code):
|
||||||
return self.method_responses.get(response_code)
|
return self["methodResponses"].get(response_code)
|
||||||
|
|
||||||
def delete_response(self, response_code):
|
def delete_response(self, response_code):
|
||||||
return self.method_responses.pop(response_code, None)
|
return self["methodResponses"].pop(response_code, None)
|
||||||
|
|
||||||
|
|
||||||
class Resource(CloudFormationModel):
|
class Resource(CloudFormationModel):
|
||||||
@ -288,7 +295,7 @@ class Resource(CloudFormationModel):
|
|||||||
backend = apigateway_backends[region_name]
|
backend = apigateway_backends[region_name]
|
||||||
if parent == api_id:
|
if parent == api_id:
|
||||||
# A Root path (/) is automatically created. Any new paths should use this as their parent
|
# A Root path (/) is automatically created. Any new paths should use this as their parent
|
||||||
resources = backend.list_resources(function_id=api_id)
|
resources = backend.get_resources(function_id=api_id)
|
||||||
root_id = [resource for resource in resources if resource.path_part == "/"][
|
root_id = [resource for resource in resources if resource.path_part == "/"][
|
||||||
0
|
0
|
||||||
].id
|
].id
|
||||||
@ -789,7 +796,7 @@ class RestAPI(CloudFormationModel):
|
|||||||
self.resources = {}
|
self.resources = {}
|
||||||
self.models = {}
|
self.models = {}
|
||||||
self.request_validators = {}
|
self.request_validators = {}
|
||||||
self.add_child("/") # Add default child
|
self.default = self.add_child("/") # Add default child
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self.id)
|
return str(self.id)
|
||||||
@ -811,8 +818,6 @@ class RestAPI(CloudFormationModel):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def apply_patch_operations(self, patch_operations):
|
def apply_patch_operations(self, patch_operations):
|
||||||
def to_path(prop):
|
|
||||||
return "/" + prop
|
|
||||||
|
|
||||||
for op in patch_operations:
|
for op in patch_operations:
|
||||||
path = op[self.OPERATION_PATH]
|
path = op[self.OPERATION_PATH]
|
||||||
@ -1270,12 +1275,80 @@ class APIGatewayBackend(BaseBackend):
|
|||||||
self.apis[api_id] = rest_api
|
self.apis[api_id] = rest_api
|
||||||
return rest_api
|
return rest_api
|
||||||
|
|
||||||
|
def import_rest_api(self, api_doc, fail_on_warnings):
|
||||||
|
"""
|
||||||
|
Only a subset of the OpenAPI spec 3.x is currently implemented.
|
||||||
|
"""
|
||||||
|
if fail_on_warnings:
|
||||||
|
try:
|
||||||
|
validate_spec(api_doc)
|
||||||
|
except OpenAPIValidationError as e:
|
||||||
|
raise InvalidOpenAPIDocumentException(e)
|
||||||
|
name = api_doc["info"]["title"]
|
||||||
|
description = api_doc["info"]["description"]
|
||||||
|
api = self.create_rest_api(name=name, description=description)
|
||||||
|
self.put_rest_api(api.id, api_doc, fail_on_warnings=fail_on_warnings)
|
||||||
|
return api
|
||||||
|
|
||||||
def get_rest_api(self, function_id):
|
def get_rest_api(self, function_id):
|
||||||
rest_api = self.apis.get(function_id)
|
rest_api = self.apis.get(function_id)
|
||||||
if rest_api is None:
|
if rest_api is None:
|
||||||
raise RestAPINotFound()
|
raise RestAPINotFound()
|
||||||
return rest_api
|
return rest_api
|
||||||
|
|
||||||
|
def put_rest_api(self, function_id, api_doc, mode="merge", fail_on_warnings=False):
|
||||||
|
"""
|
||||||
|
Only a subset of the OpenAPI spec 3.x is currently implemented.
|
||||||
|
"""
|
||||||
|
if mode not in ["merge", "overwrite"]:
|
||||||
|
raise InvalidOpenApiModeException()
|
||||||
|
|
||||||
|
if api_doc.get("swagger") is not None or (
|
||||||
|
api_doc.get("openapi") is not None and api_doc["openapi"][0] != "3"
|
||||||
|
):
|
||||||
|
raise InvalidOpenApiDocVersionException()
|
||||||
|
|
||||||
|
if fail_on_warnings:
|
||||||
|
try:
|
||||||
|
validate_spec(api_doc)
|
||||||
|
except OpenAPIValidationError as e:
|
||||||
|
raise InvalidOpenAPIDocumentException(e)
|
||||||
|
|
||||||
|
if mode == "overwrite":
|
||||||
|
api = self.get_rest_api(function_id)
|
||||||
|
api.resources = {}
|
||||||
|
api.default = api.add_child("/") # Add default child
|
||||||
|
|
||||||
|
for (path, resource_doc) in sorted(
|
||||||
|
api_doc["paths"].items(), key=lambda x: x[0]
|
||||||
|
):
|
||||||
|
parent_path_part = path[0 : path.rfind("/")] or "/"
|
||||||
|
parent_resource_id = (
|
||||||
|
self.apis[function_id].get_resource_for_path(parent_path_part).id
|
||||||
|
)
|
||||||
|
resource = self.create_resource(
|
||||||
|
function_id=function_id,
|
||||||
|
parent_resource_id=parent_resource_id,
|
||||||
|
path_part=path[path.rfind("/") + 1 :],
|
||||||
|
)
|
||||||
|
|
||||||
|
for (method_type, method_doc) in resource_doc.items():
|
||||||
|
method_type = method_type.upper()
|
||||||
|
if method_doc.get("x-amazon-apigateway-integration") is None:
|
||||||
|
self.put_method(function_id, resource.id, method_type, None)
|
||||||
|
method_responses = method_doc.get("responses", {}).items()
|
||||||
|
for (response_code, _) in method_responses:
|
||||||
|
self.put_method_response(
|
||||||
|
function_id,
|
||||||
|
resource.id,
|
||||||
|
method_type,
|
||||||
|
response_code,
|
||||||
|
response_models=None,
|
||||||
|
response_parameters=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.get_rest_api(function_id)
|
||||||
|
|
||||||
def update_rest_api(self, function_id, patch_operations):
|
def update_rest_api(self, function_id, patch_operations):
|
||||||
rest_api = self.apis.get(function_id)
|
rest_api = self.apis.get(function_id)
|
||||||
if rest_api is None:
|
if rest_api is None:
|
||||||
@ -1290,19 +1363,24 @@ class APIGatewayBackend(BaseBackend):
|
|||||||
rest_api = self.apis.pop(function_id)
|
rest_api = self.apis.pop(function_id)
|
||||||
return rest_api
|
return rest_api
|
||||||
|
|
||||||
def list_resources(self, function_id):
|
def get_resources(self, function_id):
|
||||||
api = self.get_rest_api(function_id)
|
api = self.get_rest_api(function_id)
|
||||||
return api.resources.values()
|
return api.resources.values()
|
||||||
|
|
||||||
def get_resource(self, function_id, resource_id):
|
def get_resource(self, function_id, resource_id):
|
||||||
api = self.get_rest_api(function_id)
|
api = self.get_rest_api(function_id)
|
||||||
|
if resource_id not in api.resources:
|
||||||
|
raise ResourceIdNotFoundException
|
||||||
resource = api.resources[resource_id]
|
resource = api.resources[resource_id]
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
def create_resource(self, function_id, parent_resource_id, path_part):
|
def create_resource(self, function_id, parent_resource_id, path_part):
|
||||||
|
api = self.get_rest_api(function_id)
|
||||||
|
if not path_part:
|
||||||
|
# We're attempting to create the default resource, which already exists.
|
||||||
|
return api.default
|
||||||
if not re.match("^\\{?[a-zA-Z0-9._-]+\\+?\\}?$", path_part):
|
if not re.match("^\\{?[a-zA-Z0-9._-]+\\+?\\}?$", path_part):
|
||||||
raise InvalidResourcePathException()
|
raise InvalidResourcePathException()
|
||||||
api = self.get_rest_api(function_id)
|
|
||||||
child = api.add_child(path=path_part, parent_id=parent_resource_id)
|
child = api.add_child(path=path_part, parent_id=parent_resource_id)
|
||||||
return child
|
return child
|
||||||
|
|
||||||
@ -1578,7 +1656,7 @@ class APIGatewayBackend(BaseBackend):
|
|||||||
api = self.get_rest_api(function_id)
|
api = self.get_rest_api(function_id)
|
||||||
methods = [
|
methods = [
|
||||||
list(res.resource_methods.values())
|
list(res.resource_methods.values())
|
||||||
for res in self.list_resources(function_id)
|
for res in self.get_resources(function_id)
|
||||||
]
|
]
|
||||||
methods = [m for sublist in methods for m in sublist]
|
methods = [m for sublist in methods for m in sublist]
|
||||||
if not any(methods):
|
if not any(methods):
|
||||||
|
@ -4,6 +4,7 @@ from urllib.parse import unquote
|
|||||||
from moto.utilities.utils import merge_multiple_dicts
|
from moto.utilities.utils import merge_multiple_dicts
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
from .models import apigateway_backends
|
from .models import apigateway_backends
|
||||||
|
from .utils import deserialize_body
|
||||||
from .exceptions import InvalidRequestInput
|
from .exceptions import InvalidRequestInput
|
||||||
|
|
||||||
API_KEY_SOURCES = ["AUTHORIZER", "HEADER"]
|
API_KEY_SOURCES = ["AUTHORIZER", "HEADER"]
|
||||||
@ -56,8 +57,16 @@ class APIGatewayResponse(BaseResponse):
|
|||||||
apis = self.backend.list_apis()
|
apis = self.backend.list_apis()
|
||||||
return 200, {}, json.dumps({"item": [api.to_dict() for api in apis]})
|
return 200, {}, json.dumps({"item": [api.to_dict() for api in apis]})
|
||||||
elif self.method == "POST":
|
elif self.method == "POST":
|
||||||
|
api_doc = deserialize_body(self.body)
|
||||||
|
if api_doc:
|
||||||
|
fail_on_warnings = self._get_bool_param("failonwarnings")
|
||||||
|
rest_api = self.backend.import_rest_api(api_doc, fail_on_warnings)
|
||||||
|
|
||||||
|
return 200, {}, json.dumps(rest_api.to_dict())
|
||||||
|
|
||||||
name = self._get_param("name")
|
name = self._get_param("name")
|
||||||
description = self._get_param("description")
|
description = self._get_param("description")
|
||||||
|
|
||||||
api_key_source = self._get_param("apiKeySource")
|
api_key_source = self._get_param("apiKeySource")
|
||||||
endpoint_configuration = self._get_param("endpointConfiguration")
|
endpoint_configuration = self._get_param("endpointConfiguration")
|
||||||
tags = self._get_param("tags")
|
tags = self._get_param("tags")
|
||||||
@ -82,6 +91,7 @@ class APIGatewayResponse(BaseResponse):
|
|||||||
policy=policy,
|
policy=policy,
|
||||||
minimum_compression_size=minimum_compression_size,
|
minimum_compression_size=minimum_compression_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
return 200, {}, json.dumps(rest_api.to_dict())
|
return 200, {}, json.dumps(rest_api.to_dict())
|
||||||
|
|
||||||
def __validte_rest_patch_operations(self, patch_operations):
|
def __validte_rest_patch_operations(self, patch_operations):
|
||||||
@ -99,6 +109,15 @@ class APIGatewayResponse(BaseResponse):
|
|||||||
rest_api = self.backend.get_rest_api(function_id)
|
rest_api = self.backend.get_rest_api(function_id)
|
||||||
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 == "PUT":
|
||||||
|
mode = self._get_param("mode", "merge")
|
||||||
|
fail_on_warnings = self._get_bool_param("failonwarnings", False)
|
||||||
|
|
||||||
|
api_doc = deserialize_body(self.body)
|
||||||
|
|
||||||
|
rest_api = self.backend.put_rest_api(
|
||||||
|
function_id, api_doc, mode, fail_on_warnings
|
||||||
|
)
|
||||||
elif self.method == "PATCH":
|
elif self.method == "PATCH":
|
||||||
patch_operations = self._get_param("patchOperations")
|
patch_operations = self._get_param("patchOperations")
|
||||||
response = self.__validte_rest_patch_operations(patch_operations)
|
response = self.__validte_rest_patch_operations(patch_operations)
|
||||||
@ -113,7 +132,7 @@ class APIGatewayResponse(BaseResponse):
|
|||||||
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":
|
||||||
resources = self.backend.list_resources(function_id)
|
resources = self.backend.get_resources(function_id)
|
||||||
return (
|
return (
|
||||||
200,
|
200,
|
||||||
{},
|
{},
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
import json
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
def create_id():
|
def create_id():
|
||||||
@ -8,5 +10,17 @@ def create_id():
|
|||||||
return "".join(str(random.choice(chars)) for x in range(size))
|
return "".join(str(random.choice(chars)) for x in range(size))
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_body(body):
|
||||||
|
try:
|
||||||
|
api_doc = json.loads(body)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
api_doc = yaml.safe_load(body)
|
||||||
|
|
||||||
|
if "openapi" in api_doc or "swagger" in api_doc:
|
||||||
|
return api_doc
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def to_path(prop):
|
def to_path(prop):
|
||||||
return "/" + prop
|
return "/" + prop
|
||||||
|
4
setup.py
4
setup.py
@ -54,6 +54,7 @@ _dep_aws_xray_sdk = "aws-xray-sdk!=0.96,>=0.93"
|
|||||||
_dep_idna = "idna<4,>=2.5"
|
_dep_idna = "idna<4,>=2.5"
|
||||||
_dep_cfn_lint = "cfn-lint>=0.4.0"
|
_dep_cfn_lint = "cfn-lint>=0.4.0"
|
||||||
_dep_sshpubkeys = "sshpubkeys>=3.1.0"
|
_dep_sshpubkeys = "sshpubkeys>=3.1.0"
|
||||||
|
_dep_openapi = "openapi-spec-validator>=0.2.8"
|
||||||
_dep_pyparsing = "pyparsing>=3.0.0"
|
_dep_pyparsing = "pyparsing>=3.0.0"
|
||||||
_setuptools = "setuptools"
|
_setuptools = "setuptools"
|
||||||
|
|
||||||
@ -69,6 +70,7 @@ all_extra_deps = [
|
|||||||
_dep_cfn_lint,
|
_dep_cfn_lint,
|
||||||
_dep_sshpubkeys,
|
_dep_sshpubkeys,
|
||||||
_dep_pyparsing,
|
_dep_pyparsing,
|
||||||
|
_dep_openapi,
|
||||||
_setuptools,
|
_setuptools,
|
||||||
]
|
]
|
||||||
all_server_deps = all_extra_deps + ["flask", "flask-cors"]
|
all_server_deps = all_extra_deps + ["flask", "flask-cors"]
|
||||||
@ -82,7 +84,7 @@ for service_name in [
|
|||||||
extras_per_service[service_name] = []
|
extras_per_service[service_name] = []
|
||||||
extras_per_service.update(
|
extras_per_service.update(
|
||||||
{
|
{
|
||||||
"apigateway": [_dep_PyYAML, _dep_python_jose, _dep_python_jose_ecdsa_pin],
|
"apigateway": [_dep_PyYAML, _dep_python_jose, _dep_python_jose_ecdsa_pin, _dep_openapi],
|
||||||
"apigatewayv2": [_dep_PyYAML],
|
"apigatewayv2": [_dep_PyYAML],
|
||||||
"appsync": [_dep_graphql],
|
"appsync": [_dep_graphql],
|
||||||
"awslambda": [_dep_docker],
|
"awslambda": [_dep_docker],
|
||||||
|
102
tests/test_apigateway/resources/test_api.json
Normal file
102
tests/test_apigateway/resources/test_api.json
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"description": "description from JSON file",
|
||||||
|
"version": "1",
|
||||||
|
"title": "doc"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
"description": "Method description.",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "200 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Empty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/test": {
|
||||||
|
"post": {
|
||||||
|
"description": "Method description.",
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "201 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Empty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-amazon-apigateway-documentation": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"documentationParts": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "API"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "API description",
|
||||||
|
"info": {
|
||||||
|
"description": "API info description 4",
|
||||||
|
"version": "API info version 3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "METHOD",
|
||||||
|
"method": "GET"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "Method description."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "MODEL",
|
||||||
|
"name": "Empty"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"title": "Empty Schema"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "RESPONSE",
|
||||||
|
"method": "GET",
|
||||||
|
"statusCode": "200"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "200 response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Empty": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Empty Schema"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
tests/test_apigateway/resources/test_api.yaml
Normal file
60
tests/test_apigateway/resources/test_api.yaml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
description: description
|
||||||
|
version: '1'
|
||||||
|
title: doc
|
||||||
|
paths:
|
||||||
|
/:
|
||||||
|
get:
|
||||||
|
description: Method description.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 200 response
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Empty'
|
||||||
|
/test:
|
||||||
|
post:
|
||||||
|
description: Method description.
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 200 response
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Empty'
|
||||||
|
|
||||||
|
x-amazon-apigateway-documentation:
|
||||||
|
version: 1.0.3
|
||||||
|
documentationParts:
|
||||||
|
- location:
|
||||||
|
type: API
|
||||||
|
properties:
|
||||||
|
description: API description
|
||||||
|
info:
|
||||||
|
description: API info description 4
|
||||||
|
version: API info version 3
|
||||||
|
- location:
|
||||||
|
type: METHOD
|
||||||
|
method: GET
|
||||||
|
properties:
|
||||||
|
description: Method description.
|
||||||
|
- location:
|
||||||
|
type: MODEL
|
||||||
|
name: Empty
|
||||||
|
properties:
|
||||||
|
title: Empty Schema
|
||||||
|
- location:
|
||||||
|
type: RESPONSE
|
||||||
|
method: GET
|
||||||
|
statusCode: '200'
|
||||||
|
properties:
|
||||||
|
description: 200 response
|
||||||
|
servers:
|
||||||
|
- url: /
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Empty:
|
||||||
|
type: object
|
||||||
|
title: Empty Schema
|
102
tests/test_apigateway/resources/test_api_invalid.json
Normal file
102
tests/test_apigateway/resources/test_api_invalid.json
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.0",
|
||||||
|
"info": {
|
||||||
|
"description": "description",
|
||||||
|
"version": "1",
|
||||||
|
"title": "doc"
|
||||||
|
},
|
||||||
|
"paaths": {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
"description": "Method description.",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "200 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Empty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/test": {
|
||||||
|
"post": {
|
||||||
|
"description": "Method description.",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "200 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Empty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-amazon-apigateway-documentation": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"documentationParts": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "API"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "API description",
|
||||||
|
"info": {
|
||||||
|
"description": "API info description 4",
|
||||||
|
"version": "API info version 3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "METHOD",
|
||||||
|
"method": "GET"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "Method description."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "MODEL",
|
||||||
|
"name": "Empty"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"title": "Empty Schema"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "RESPONSE",
|
||||||
|
"method": "GET",
|
||||||
|
"statusCode": "200"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "200 response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Empty": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Empty Schema"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
tests/test_apigateway/resources/test_api_invalid_version.json
Normal file
102
tests/test_apigateway/resources/test_api_invalid_version.json
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
{
|
||||||
|
"openapi": "2.0.0",
|
||||||
|
"info": {
|
||||||
|
"description": "description",
|
||||||
|
"version": "1",
|
||||||
|
"title": "doc"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
"description": "Method description.",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "200 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Empty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/test": {
|
||||||
|
"post": {
|
||||||
|
"description": "Method description.",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "200 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Empty"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-amazon-apigateway-documentation": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"documentationParts": [
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "API"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "API description",
|
||||||
|
"info": {
|
||||||
|
"description": "API info description 4",
|
||||||
|
"version": "API info version 3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "METHOD",
|
||||||
|
"method": "GET"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "Method description."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "MODEL",
|
||||||
|
"name": "Empty"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"title": "Empty Schema"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"location": {
|
||||||
|
"type": "RESPONSE",
|
||||||
|
"method": "GET",
|
||||||
|
"statusCode": "200"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"description": "200 response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Empty": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Empty Schema"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -367,6 +367,7 @@ def test_create_method():
|
|||||||
"httpMethod": "GET",
|
"httpMethod": "GET",
|
||||||
"authorizationType": "none",
|
"authorizationType": "none",
|
||||||
"apiKeyRequired": False,
|
"apiKeyRequired": False,
|
||||||
|
"methodResponses": {},
|
||||||
"ResponseMetadata": {"HTTPStatusCode": 200},
|
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -401,6 +402,7 @@ def test_create_method_apikeyrequired():
|
|||||||
"httpMethod": "GET",
|
"httpMethod": "GET",
|
||||||
"authorizationType": "none",
|
"authorizationType": "none",
|
||||||
"apiKeyRequired": True,
|
"apiKeyRequired": True,
|
||||||
|
"methodResponses": {},
|
||||||
"ResponseMetadata": {"HTTPStatusCode": 200},
|
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -452,6 +454,19 @@ def test_create_method_response():
|
|||||||
response.should.equal({"ResponseMetadata": {"HTTPStatusCode": 200}})
|
response.should.equal({"ResponseMetadata": {"HTTPStatusCode": 200}})
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_get_method_unknown_resource_id():
|
||||||
|
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"]
|
||||||
|
|
||||||
|
with pytest.raises(ClientError) as ex:
|
||||||
|
client.get_method(restApiId=api_id, resourceId="sth", httpMethod="GET")
|
||||||
|
err = ex.value.response["Error"]
|
||||||
|
err["Code"].should.equal("NotFoundException")
|
||||||
|
err["Message"].should.equal("Invalid resource identifier specified")
|
||||||
|
|
||||||
|
|
||||||
@mock_apigateway
|
@mock_apigateway
|
||||||
def test_delete_method():
|
def test_delete_method():
|
||||||
client = boto3.client("apigateway", region_name="us-west-2")
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
70
tests/test_apigateway/test_apigateway_importrestapi.py
Normal file
70
tests/test_apigateway/test_apigateway_importrestapi.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import boto3
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
|
from moto import mock_apigateway
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_import_rest_api__api_is_created():
|
||||||
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api.json", "rb") as api_json:
|
||||||
|
response = client.import_rest_api(body=api_json.read())
|
||||||
|
|
||||||
|
response.should.have.key("id")
|
||||||
|
response.should.have.key("name").which.should.equal("doc")
|
||||||
|
response.should.have.key("description").which.should.equal(
|
||||||
|
"description from JSON file"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get_rest_api(restApiId=response["id"])
|
||||||
|
response.should.have.key("name").which.should.equal("doc")
|
||||||
|
response.should.have.key("description").which.should.equal(
|
||||||
|
"description from JSON file"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_import_rest_api__invalid_api_creates_nothing():
|
||||||
|
client = boto3.client("apigateway", region_name="us-west-2")
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api_invalid.json", "rb") as api_json:
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
client.import_rest_api(body=api_json.read(), failOnWarnings=True)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("BadRequestException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"Failed to parse the uploaded OpenAPI document due to: 'paths' is a required property"
|
||||||
|
)
|
||||||
|
|
||||||
|
client.get_rest_apis().should.have.key("items").length_of(0)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_import_rest_api__methods_are_created():
|
||||||
|
client = boto3.client("apigateway", region_name="us-east-1")
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api.json", "rb") as api_json:
|
||||||
|
resp = client.import_rest_api(body=api_json.read())
|
||||||
|
api_id = resp["id"]
|
||||||
|
|
||||||
|
resources = client.get_resources(restApiId=api_id)
|
||||||
|
root_id = [res for res in resources["items"] if res["path"] == "/"][0]["id"]
|
||||||
|
|
||||||
|
# We have a GET-method
|
||||||
|
resp = client.get_method(restApiId=api_id, resourceId=root_id, httpMethod="GET")
|
||||||
|
resp["methodResponses"].should.equal({"200": {"statusCode": "200"}})
|
||||||
|
|
||||||
|
# We have a POST on /test
|
||||||
|
test_path_id = [res for res in resources["items"] if res["path"] == "/test"][0][
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
resp = client.get_method(
|
||||||
|
restApiId=api_id, resourceId=test_path_id, httpMethod="POST"
|
||||||
|
)
|
||||||
|
resp["methodResponses"].should.equal({"201": {"statusCode": "201"}})
|
217
tests/test_apigateway/test_apigateway_putrestapi.py
Normal file
217
tests/test_apigateway/test_apigateway_putrestapi.py
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import boto3
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
|
from moto import mock_apigateway
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_put_rest_api__api_details_are_persisted():
|
||||||
|
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"]
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api.json", "rb") as api_json:
|
||||||
|
response = client.put_rest_api(
|
||||||
|
restApiId=api_id,
|
||||||
|
mode="overwrite",
|
||||||
|
failOnWarnings=True,
|
||||||
|
body=api_json.read(),
|
||||||
|
)
|
||||||
|
|
||||||
|
response.should.have.key("id").which.should.equal(api_id)
|
||||||
|
response.should.have.key("name").which.should.equal("my_api")
|
||||||
|
response.should.have.key("description").which.should.equal("this is my api")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_put_rest_api__methods_are_created():
|
||||||
|
client = boto3.client("apigateway", region_name="us-east-2")
|
||||||
|
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api.json", "rb") as api_json:
|
||||||
|
client.put_rest_api(restApiId=api_id, body=api_json.read())
|
||||||
|
|
||||||
|
resources = client.get_resources(restApiId=api_id)
|
||||||
|
root_id = [res for res in resources["items"] if res["path"] == "/"][0]["id"]
|
||||||
|
|
||||||
|
# We have a GET-method
|
||||||
|
resp = client.get_method(restApiId=api_id, resourceId=root_id, httpMethod="GET")
|
||||||
|
resp["methodResponses"].should.equal({"200": {"statusCode": "200"}})
|
||||||
|
|
||||||
|
# We have a POST on /test
|
||||||
|
test_path_id = [res for res in resources["items"] if res["path"] == "/test"][0][
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
resp = client.get_method(
|
||||||
|
restApiId=api_id, resourceId=test_path_id, httpMethod="POST"
|
||||||
|
)
|
||||||
|
resp["methodResponses"].should.equal({"201": {"statusCode": "201"}})
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_put_rest_api__existing_methods_are_overwritten():
|
||||||
|
client = boto3.client("apigateway", region_name="us-east-2")
|
||||||
|
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
resources = client.get_resources(restApiId=api_id)
|
||||||
|
root_id = [resource for resource in resources["items"] if resource["path"] == "/"][
|
||||||
|
0
|
||||||
|
]["id"]
|
||||||
|
|
||||||
|
client.put_method(
|
||||||
|
restApiId=api_id,
|
||||||
|
resourceId=root_id,
|
||||||
|
httpMethod="POST",
|
||||||
|
authorizationType="none",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get_method(
|
||||||
|
restApiId=api_id, resourceId=root_id, httpMethod="POST"
|
||||||
|
)
|
||||||
|
response.should.have.key("httpMethod").equals("POST")
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api.json", "rb") as api_json:
|
||||||
|
client.put_rest_api(
|
||||||
|
restApiId=api_id,
|
||||||
|
mode="overwrite",
|
||||||
|
failOnWarnings=True,
|
||||||
|
body=api_json.read(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Since we chose mode=overwrite, the root_id is different
|
||||||
|
resources = client.get_resources(restApiId=api_id)
|
||||||
|
new_root_id = [
|
||||||
|
resource for resource in resources["items"] if resource["path"] == "/"
|
||||||
|
][0]["id"]
|
||||||
|
|
||||||
|
new_root_id.shouldnt.equal(root_id)
|
||||||
|
|
||||||
|
# Our POST-method should be gone
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
client.get_method(restApiId=api_id, resourceId=new_root_id, httpMethod="POST")
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("NotFoundException")
|
||||||
|
|
||||||
|
# We just have a GET-method, as defined in the JSON
|
||||||
|
client.get_method(restApiId=api_id, resourceId=new_root_id, httpMethod="GET")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_put_rest_api__existing_methods_still_exist():
|
||||||
|
client = boto3.client("apigateway", region_name="us-east-2")
|
||||||
|
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
resources = client.get_resources(restApiId=api_id)
|
||||||
|
root_id = [resource for resource in resources["items"] if resource["path"] == "/"][
|
||||||
|
0
|
||||||
|
]["id"]
|
||||||
|
|
||||||
|
client.put_method(
|
||||||
|
restApiId=api_id,
|
||||||
|
resourceId=root_id,
|
||||||
|
httpMethod="POST",
|
||||||
|
authorizationType="none",
|
||||||
|
)
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api.json", "rb") as api_json:
|
||||||
|
client.put_rest_api(
|
||||||
|
restApiId=api_id,
|
||||||
|
mode="merge",
|
||||||
|
failOnWarnings=True,
|
||||||
|
body=api_json.read(),
|
||||||
|
)
|
||||||
|
|
||||||
|
response = client.get_method(
|
||||||
|
restApiId=api_id, resourceId=root_id, httpMethod="POST"
|
||||||
|
)
|
||||||
|
response.should.have.key("httpMethod").equals("POST")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_put_rest_api__fail_on_invalid_spec():
|
||||||
|
client = boto3.client("apigateway", region_name="us-east-2")
|
||||||
|
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api_invalid.json", "rb") as api_json:
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
client.put_rest_api(
|
||||||
|
restApiId=api_id, failOnWarnings=True, body=api_json.read()
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("BadRequestException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"Failed to parse the uploaded OpenAPI document due to: 'paths' is a required property"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_put_rest_api__fail_on_invalid_version():
|
||||||
|
client = boto3.client("apigateway", region_name="us-east-2")
|
||||||
|
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api_invalid_version.json", "rb") as api_json:
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
client.put_rest_api(
|
||||||
|
restApiId=api_id, failOnWarnings=True, body=api_json.read()
|
||||||
|
)
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("BadRequestException")
|
||||||
|
err["Message"].should.equal("Only OpenAPI 3.x.x are currently supported")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_put_rest_api__fail_on_invalid_mode():
|
||||||
|
client = boto3.client("apigateway", region_name="us-east-2")
|
||||||
|
|
||||||
|
response = client.create_rest_api(name="my_api", description="this is my api")
|
||||||
|
api_id = response["id"]
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api.json", "rb") as api_json:
|
||||||
|
with pytest.raises(ClientError) as exc:
|
||||||
|
client.put_rest_api(restApiId=api_id, mode="unknown", body=api_json.read())
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("BadRequestException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
'Enumeration value of OpenAPI import mode must be "overwrite" or "merge"'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigateway
|
||||||
|
def test_put_rest_api__as_yaml():
|
||||||
|
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"]
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
with open(path + "/resources/test_api.yaml", "rb") as api_yaml:
|
||||||
|
response = client.put_rest_api(
|
||||||
|
restApiId=api_id,
|
||||||
|
mode="overwrite",
|
||||||
|
failOnWarnings=True,
|
||||||
|
body=api_yaml.read(),
|
||||||
|
)
|
||||||
|
|
||||||
|
response.should.have.key("id").which.should.equal(api_id)
|
||||||
|
response.should.have.key("name").which.should.equal("my_api")
|
||||||
|
response.should.have.key("description").which.should.equal("this is my api")
|
Loading…
x
Reference in New Issue
Block a user