APIGatewayV2 mappings (#5711)
This commit is contained in:
parent
97b5e8b3ab
commit
22539585e7
@ -175,13 +175,13 @@
|
|||||||
|
|
||||||
## apigatewayv2
|
## apigatewayv2
|
||||||
<details>
|
<details>
|
||||||
<summary>58% implemented</summary>
|
<summary>69% implemented</summary>
|
||||||
|
|
||||||
- [X] create_api
|
- [X] create_api
|
||||||
- [ ] create_api_mapping
|
- [X] create_api_mapping
|
||||||
- [X] create_authorizer
|
- [X] create_authorizer
|
||||||
- [ ] create_deployment
|
- [ ] create_deployment
|
||||||
- [ ] create_domain_name
|
- [X] create_domain_name
|
||||||
- [X] create_integration
|
- [X] create_integration
|
||||||
- [X] create_integration_response
|
- [X] create_integration_response
|
||||||
- [X] create_model
|
- [X] create_model
|
||||||
@ -191,11 +191,11 @@
|
|||||||
- [X] create_vpc_link
|
- [X] create_vpc_link
|
||||||
- [ ] delete_access_log_settings
|
- [ ] delete_access_log_settings
|
||||||
- [X] delete_api
|
- [X] delete_api
|
||||||
- [ ] delete_api_mapping
|
- [X] delete_api_mapping
|
||||||
- [X] delete_authorizer
|
- [X] delete_authorizer
|
||||||
- [X] delete_cors_configuration
|
- [X] delete_cors_configuration
|
||||||
- [ ] delete_deployment
|
- [ ] delete_deployment
|
||||||
- [ ] delete_domain_name
|
- [X] delete_domain_name
|
||||||
- [X] delete_integration
|
- [X] delete_integration
|
||||||
- [X] delete_integration_response
|
- [X] delete_integration_response
|
||||||
- [X] delete_model
|
- [X] delete_model
|
||||||
@ -207,15 +207,15 @@
|
|||||||
- [X] delete_vpc_link
|
- [X] delete_vpc_link
|
||||||
- [ ] export_api
|
- [ ] export_api
|
||||||
- [X] get_api
|
- [X] get_api
|
||||||
- [ ] get_api_mapping
|
- [X] get_api_mapping
|
||||||
- [ ] get_api_mappings
|
- [X] get_api_mappings
|
||||||
- [X] get_apis
|
- [X] get_apis
|
||||||
- [X] get_authorizer
|
- [X] get_authorizer
|
||||||
- [ ] get_authorizers
|
- [ ] get_authorizers
|
||||||
- [ ] get_deployment
|
- [ ] get_deployment
|
||||||
- [ ] get_deployments
|
- [ ] get_deployments
|
||||||
- [ ] get_domain_name
|
- [X] get_domain_name
|
||||||
- [ ] get_domain_names
|
- [X] get_domain_names
|
||||||
- [X] get_integration
|
- [X] get_integration
|
||||||
- [X] get_integration_response
|
- [X] get_integration_response
|
||||||
- [X] get_integration_responses
|
- [X] get_integration_responses
|
||||||
|
@ -33,10 +33,10 @@ apigatewayv2
|
|||||||
CredentialsArn, RouteKey, Tags, Target
|
CredentialsArn, RouteKey, Tags, Target
|
||||||
|
|
||||||
|
|
||||||
- [ ] create_api_mapping
|
- [X] create_api_mapping
|
||||||
- [X] create_authorizer
|
- [X] create_authorizer
|
||||||
- [ ] create_deployment
|
- [ ] create_deployment
|
||||||
- [ ] create_domain_name
|
- [X] create_domain_name
|
||||||
- [X] create_integration
|
- [X] create_integration
|
||||||
- [X] create_integration_response
|
- [X] create_integration_response
|
||||||
- [X] create_model
|
- [X] create_model
|
||||||
@ -50,11 +50,11 @@ apigatewayv2
|
|||||||
- [X] create_vpc_link
|
- [X] create_vpc_link
|
||||||
- [ ] delete_access_log_settings
|
- [ ] delete_access_log_settings
|
||||||
- [X] delete_api
|
- [X] delete_api
|
||||||
- [ ] delete_api_mapping
|
- [X] delete_api_mapping
|
||||||
- [X] delete_authorizer
|
- [X] delete_authorizer
|
||||||
- [X] delete_cors_configuration
|
- [X] delete_cors_configuration
|
||||||
- [ ] delete_deployment
|
- [ ] delete_deployment
|
||||||
- [ ] delete_domain_name
|
- [X] delete_domain_name
|
||||||
- [X] delete_integration
|
- [X] delete_integration
|
||||||
- [X] delete_integration_response
|
- [X] delete_integration_response
|
||||||
- [X] delete_model
|
- [X] delete_model
|
||||||
@ -66,8 +66,8 @@ apigatewayv2
|
|||||||
- [X] delete_vpc_link
|
- [X] delete_vpc_link
|
||||||
- [ ] export_api
|
- [ ] export_api
|
||||||
- [X] get_api
|
- [X] get_api
|
||||||
- [ ] get_api_mapping
|
- [X] get_api_mapping
|
||||||
- [ ] get_api_mappings
|
- [X] get_api_mappings
|
||||||
- [X] get_apis
|
- [X] get_apis
|
||||||
|
|
||||||
Pagination is not yet implemented
|
Pagination is not yet implemented
|
||||||
@ -77,8 +77,12 @@ apigatewayv2
|
|||||||
- [ ] get_authorizers
|
- [ ] get_authorizers
|
||||||
- [ ] get_deployment
|
- [ ] get_deployment
|
||||||
- [ ] get_deployments
|
- [ ] get_deployments
|
||||||
- [ ] get_domain_name
|
- [X] get_domain_name
|
||||||
- [ ] get_domain_names
|
- [X] get_domain_names
|
||||||
|
|
||||||
|
Pagination is not yet implemented
|
||||||
|
|
||||||
|
|
||||||
- [X] get_integration
|
- [X] get_integration
|
||||||
- [X] get_integration_response
|
- [X] get_integration_response
|
||||||
- [X] get_integration_responses
|
- [X] get_integration_responses
|
||||||
|
@ -93,3 +93,33 @@ class UnknownProtocol(APIGatewayV2Error):
|
|||||||
"BadRequestException",
|
"BadRequestException",
|
||||||
"Invalid protocol specified. Must be one of [HTTP, WEBSOCKET]",
|
"Invalid protocol specified. Must be one of [HTTP, WEBSOCKET]",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DomainNameNotFound(APIGatewayV2Error):
|
||||||
|
code = 404
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__(
|
||||||
|
"NotFoundException",
|
||||||
|
"The domain name resource specified in the request was not found.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DomainNameAlreadyExists(APIGatewayV2Error):
|
||||||
|
code = 409
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__(
|
||||||
|
"ConflictException",
|
||||||
|
"The domain name resource already exists.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiMappingNotFound(APIGatewayV2Error):
|
||||||
|
code = 404
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__(
|
||||||
|
"NotFoundException",
|
||||||
|
"The api mapping resource specified in the request was not found.",
|
||||||
|
)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""ApiGatewayV2Backend class with methods for supported APIs."""
|
"""ApiGatewayV2Backend class with methods for supported APIs."""
|
||||||
|
import hashlib
|
||||||
import string
|
import string
|
||||||
import yaml
|
import yaml
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
from moto.core import BaseBackend, BackendDict, BaseModel
|
from moto.core import BaseBackend, BackendDict, BaseModel
|
||||||
from moto.core.utils import unix_time
|
from moto.core.utils import unix_time
|
||||||
@ -9,6 +10,7 @@ from moto.moto_api._internal import mock_random as random
|
|||||||
from moto.utilities.tagging_service import TaggingService
|
from moto.utilities.tagging_service import TaggingService
|
||||||
|
|
||||||
from .exceptions import (
|
from .exceptions import (
|
||||||
|
ApiMappingNotFound,
|
||||||
ApiNotFound,
|
ApiNotFound,
|
||||||
AuthorizerNotFound,
|
AuthorizerNotFound,
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
@ -18,6 +20,8 @@ from .exceptions import (
|
|||||||
IntegrationResponseNotFound,
|
IntegrationResponseNotFound,
|
||||||
RouteNotFound,
|
RouteNotFound,
|
||||||
VpcLinkNotFound,
|
VpcLinkNotFound,
|
||||||
|
DomainNameNotFound,
|
||||||
|
DomainNameAlreadyExists,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1011,6 +1015,55 @@ class VpcLink(BaseModel):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DomainName(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
domain_name: str,
|
||||||
|
domain_name_configurations: List[Dict[str, str]],
|
||||||
|
mutual_tls_authentication: Dict[str, str],
|
||||||
|
tags: Dict[str, str],
|
||||||
|
):
|
||||||
|
self.api_mapping_selection_expression = "$request.basepath"
|
||||||
|
self.domain_name = domain_name
|
||||||
|
self.domain_name_configurations = domain_name_configurations
|
||||||
|
self.mutual_tls_authentication = mutual_tls_authentication
|
||||||
|
self.tags = tags
|
||||||
|
|
||||||
|
def to_json(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"apiMappingSelectionExpression": self.api_mapping_selection_expression,
|
||||||
|
"domainName": self.domain_name,
|
||||||
|
"domainNameConfigurations": self.domain_name_configurations,
|
||||||
|
"mutualTlsAuthentication": self.mutual_tls_authentication,
|
||||||
|
"tags": self.tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ApiMapping(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
api_id: str,
|
||||||
|
api_mapping_key: str,
|
||||||
|
api_mapping_id: str,
|
||||||
|
domain_name: str,
|
||||||
|
stage: str,
|
||||||
|
) -> None:
|
||||||
|
self.api_id = api_id
|
||||||
|
self.api_mapping_key = api_mapping_key
|
||||||
|
self.api_mapping_id = api_mapping_id
|
||||||
|
self.domain_name = domain_name
|
||||||
|
self.stage = stage
|
||||||
|
|
||||||
|
def to_json(self) -> Dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"apiId": self.api_id,
|
||||||
|
"apiMappingId": self.api_mapping_id,
|
||||||
|
"apiMappingKey": self.api_mapping_key,
|
||||||
|
"domainName": self.domain_name,
|
||||||
|
"stage": self.stage,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ApiGatewayV2Backend(BaseBackend):
|
class ApiGatewayV2Backend(BaseBackend):
|
||||||
"""Implementation of ApiGatewayV2 APIs."""
|
"""Implementation of ApiGatewayV2 APIs."""
|
||||||
|
|
||||||
@ -1018,6 +1071,8 @@ class ApiGatewayV2Backend(BaseBackend):
|
|||||||
super().__init__(region_name, account_id)
|
super().__init__(region_name, account_id)
|
||||||
self.apis: Dict[str, Api] = dict()
|
self.apis: Dict[str, Api] = dict()
|
||||||
self.vpc_links: Dict[str, VpcLink] = dict()
|
self.vpc_links: Dict[str, VpcLink] = dict()
|
||||||
|
self.domain_names: Dict[str, DomainName] = dict()
|
||||||
|
self.api_mappings: Dict[str, ApiMapping] = dict()
|
||||||
self.tagger = TaggingService()
|
self.tagger = TaggingService()
|
||||||
|
|
||||||
def create_api(
|
def create_api(
|
||||||
@ -1537,5 +1592,114 @@ class ApiGatewayV2Backend(BaseBackend):
|
|||||||
vpc_link.update(name)
|
vpc_link.update(name)
|
||||||
return vpc_link
|
return vpc_link
|
||||||
|
|
||||||
|
def create_domain_name(
|
||||||
|
self,
|
||||||
|
domain_name: str,
|
||||||
|
domain_name_configurations: List[Dict[str, str]],
|
||||||
|
mutual_tls_authentication: Dict[str, str],
|
||||||
|
tags: Dict[str, str],
|
||||||
|
) -> DomainName:
|
||||||
|
|
||||||
|
if domain_name in self.domain_names.keys():
|
||||||
|
raise DomainNameAlreadyExists
|
||||||
|
|
||||||
|
domain = DomainName(
|
||||||
|
domain_name=domain_name,
|
||||||
|
domain_name_configurations=domain_name_configurations,
|
||||||
|
mutual_tls_authentication=mutual_tls_authentication,
|
||||||
|
tags=tags,
|
||||||
|
)
|
||||||
|
self.domain_names[domain.domain_name] = domain
|
||||||
|
return domain
|
||||||
|
|
||||||
|
def get_domain_name(self, domain_name: Union[str, None]) -> DomainName:
|
||||||
|
if domain_name is None or domain_name not in self.domain_names:
|
||||||
|
raise DomainNameNotFound
|
||||||
|
return self.domain_names[domain_name]
|
||||||
|
|
||||||
|
def get_domain_names(self) -> List[DomainName]:
|
||||||
|
"""
|
||||||
|
Pagination is not yet implemented
|
||||||
|
"""
|
||||||
|
return list(self.domain_names.values())
|
||||||
|
|
||||||
|
def delete_domain_name(self, domain_name: str) -> None:
|
||||||
|
if domain_name not in self.domain_names.keys():
|
||||||
|
raise DomainNameNotFound
|
||||||
|
|
||||||
|
for mapping_id, mapping in self.api_mappings.items():
|
||||||
|
if mapping.domain_name == domain_name:
|
||||||
|
del self.api_mappings[mapping_id]
|
||||||
|
|
||||||
|
del self.domain_names[domain_name]
|
||||||
|
|
||||||
|
def _generate_api_maping_id(
|
||||||
|
self, api_mapping_key: str, stage: str, domain_name: str
|
||||||
|
) -> str:
|
||||||
|
return str(
|
||||||
|
hashlib.sha256(
|
||||||
|
f"{stage} {domain_name}/{api_mapping_key}".encode("utf-8")
|
||||||
|
).hexdigest()
|
||||||
|
)[:5]
|
||||||
|
|
||||||
|
def create_api_mapping(
|
||||||
|
self, api_id: str, api_mapping_key: str, domain_name: str, stage: str
|
||||||
|
) -> ApiMapping:
|
||||||
|
if domain_name not in self.domain_names.keys():
|
||||||
|
raise DomainNameNotFound
|
||||||
|
|
||||||
|
if api_id not in self.apis.keys():
|
||||||
|
raise ApiNotFound("The resource specified in the request was not found.")
|
||||||
|
|
||||||
|
if api_mapping_key.startswith("/") or "//" in api_mapping_key:
|
||||||
|
raise BadRequestException(
|
||||||
|
"API mapping key should not start with a '/' or have consecutive '/'s."
|
||||||
|
)
|
||||||
|
|
||||||
|
if api_mapping_key.endswith("/"):
|
||||||
|
raise BadRequestException("API mapping key should not end with a '/'.")
|
||||||
|
|
||||||
|
api_mapping_id = self._generate_api_maping_id(
|
||||||
|
api_mapping_key=api_mapping_key, stage=stage, domain_name=domain_name
|
||||||
|
)
|
||||||
|
|
||||||
|
mapping = ApiMapping(
|
||||||
|
domain_name=domain_name,
|
||||||
|
api_id=api_id,
|
||||||
|
api_mapping_key=api_mapping_key,
|
||||||
|
api_mapping_id=api_mapping_id,
|
||||||
|
stage=stage,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.api_mappings[api_mapping_id] = mapping
|
||||||
|
return mapping
|
||||||
|
|
||||||
|
def get_api_mapping(self, api_mapping_id: str, domain_name: str) -> ApiMapping:
|
||||||
|
if domain_name not in self.domain_names.keys():
|
||||||
|
raise DomainNameNotFound
|
||||||
|
|
||||||
|
if api_mapping_id not in self.api_mappings.keys():
|
||||||
|
raise ApiMappingNotFound
|
||||||
|
|
||||||
|
return self.api_mappings[api_mapping_id]
|
||||||
|
|
||||||
|
def get_api_mappings(self, domain_name: str) -> List[ApiMapping]:
|
||||||
|
domain_mappings = []
|
||||||
|
for mapping in self.api_mappings.values():
|
||||||
|
if mapping.domain_name == domain_name:
|
||||||
|
domain_mappings.append(mapping)
|
||||||
|
return domain_mappings
|
||||||
|
|
||||||
|
def delete_api_mapping(self, api_mapping_id: str, domain_name: str) -> None:
|
||||||
|
if api_mapping_id not in self.api_mappings.keys():
|
||||||
|
raise ApiMappingNotFound
|
||||||
|
|
||||||
|
if self.api_mappings[api_mapping_id].domain_name != domain_name:
|
||||||
|
raise BadRequestException(
|
||||||
|
f"given domain name {domain_name} does not match with mapping definition of mapping {api_mapping_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
del self.api_mappings[api_mapping_id]
|
||||||
|
|
||||||
|
|
||||||
apigatewayv2_backends = BackendDict(ApiGatewayV2Backend, "apigatewayv2")
|
apigatewayv2_backends = BackendDict(ApiGatewayV2Backend, "apigatewayv2")
|
||||||
|
@ -180,6 +180,38 @@ class ApiGatewayV2Response(BaseResponse):
|
|||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
return self.create_vpc_link()
|
return self.create_vpc_link()
|
||||||
|
|
||||||
|
def domain_names(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE: # type: ignore[return]
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
|
||||||
|
if request.method == "GET":
|
||||||
|
return self.get_domain_names()
|
||||||
|
if request.method == "POST":
|
||||||
|
return self.create_domain_name()
|
||||||
|
|
||||||
|
def domain_name(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE: # type: ignore[return]
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
|
||||||
|
if request.method == "GET":
|
||||||
|
return self.get_domain_name()
|
||||||
|
if request.method == "DELETE":
|
||||||
|
return self.delete_domain_name()
|
||||||
|
|
||||||
|
def api_mappings(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE: # type: ignore[return]
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
|
||||||
|
if request.method == "GET":
|
||||||
|
return self.get_api_mappings()
|
||||||
|
if request.method == "POST":
|
||||||
|
return self.create_api_mapping()
|
||||||
|
|
||||||
|
def api_mapping(self, request: Any, full_url: str, headers: Any) -> TYPE_RESPONSE: # type: ignore[return]
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
|
||||||
|
if request.method == "GET":
|
||||||
|
return self.get_api_mapping()
|
||||||
|
if request.method == "DELETE":
|
||||||
|
return self.delete_api_mapping()
|
||||||
|
|
||||||
def create_api(self) -> TYPE_RESPONSE:
|
def create_api(self) -> TYPE_RESPONSE:
|
||||||
params = json.loads(self.body)
|
params = json.loads(self.body)
|
||||||
|
|
||||||
@ -751,3 +783,74 @@ class ApiGatewayV2Response(BaseResponse):
|
|||||||
|
|
||||||
vpc_link = self.apigatewayv2_backend.update_vpc_link(vpc_link_id, name=name)
|
vpc_link = self.apigatewayv2_backend.update_vpc_link(vpc_link_id, name=name)
|
||||||
return 200, {}, json.dumps(vpc_link.to_json())
|
return 200, {}, json.dumps(vpc_link.to_json())
|
||||||
|
|
||||||
|
def create_domain_name(self) -> TYPE_RESPONSE:
|
||||||
|
params = json.loads(self.body)
|
||||||
|
domain_name = params.get("domainName")
|
||||||
|
domain_name_configurations = params.get("domainNameConfigurations", [{}])
|
||||||
|
mutual_tls_authentication = params.get("mutualTlsAuthentication", {})
|
||||||
|
tags = params.get("tags", {})
|
||||||
|
domain_name = self.apigatewayv2_backend.create_domain_name(
|
||||||
|
domain_name=domain_name,
|
||||||
|
domain_name_configurations=domain_name_configurations,
|
||||||
|
mutual_tls_authentication=mutual_tls_authentication,
|
||||||
|
tags=tags,
|
||||||
|
)
|
||||||
|
return 201, {}, json.dumps(domain_name.to_json())
|
||||||
|
|
||||||
|
def get_domain_name(self) -> TYPE_RESPONSE:
|
||||||
|
domain_name_param = self.path.split("/")[-1]
|
||||||
|
domain_name = self.apigatewayv2_backend.get_domain_name(
|
||||||
|
domain_name=domain_name_param
|
||||||
|
)
|
||||||
|
return 200, {}, json.dumps(domain_name.to_json())
|
||||||
|
|
||||||
|
def get_domain_names(self) -> TYPE_RESPONSE:
|
||||||
|
domain_names = self.apigatewayv2_backend.get_domain_names()
|
||||||
|
list_of_dict = [domain_name.to_json() for domain_name in domain_names]
|
||||||
|
return 200, {}, json.dumps(dict(items=list_of_dict))
|
||||||
|
|
||||||
|
def create_api_mapping(self) -> TYPE_RESPONSE:
|
||||||
|
domain_name = self.path.split("/")[-2]
|
||||||
|
params = json.loads(self.body)
|
||||||
|
api_id = params.get("apiId")
|
||||||
|
api_mapping_key = params.get("apiMappingKey", "")
|
||||||
|
stage = params.get("stage")
|
||||||
|
mapping = self.apigatewayv2_backend.create_api_mapping(
|
||||||
|
api_id=api_id,
|
||||||
|
api_mapping_key=api_mapping_key,
|
||||||
|
domain_name=domain_name,
|
||||||
|
stage=stage,
|
||||||
|
)
|
||||||
|
return 201, {}, json.dumps(mapping.to_json())
|
||||||
|
|
||||||
|
def get_api_mapping(self) -> TYPE_RESPONSE:
|
||||||
|
api_mapping_id = self.path.split("/")[-1]
|
||||||
|
domain_name = self.path.split("/")[-3]
|
||||||
|
mapping = self.apigatewayv2_backend.get_api_mapping(
|
||||||
|
api_mapping_id=api_mapping_id,
|
||||||
|
domain_name=domain_name,
|
||||||
|
)
|
||||||
|
return 200, {}, json.dumps(mapping.to_json())
|
||||||
|
|
||||||
|
def get_api_mappings(self) -> TYPE_RESPONSE:
|
||||||
|
domain_name = self.path.split("/")[-2]
|
||||||
|
mappings = self.apigatewayv2_backend.get_api_mappings(domain_name=domain_name)
|
||||||
|
list_of_dict = [mapping.to_json() for mapping in mappings]
|
||||||
|
return 200, {}, json.dumps(dict(items=list_of_dict))
|
||||||
|
|
||||||
|
def delete_domain_name(self) -> TYPE_RESPONSE:
|
||||||
|
domain_name = self.path.split("/")[-1]
|
||||||
|
self.apigatewayv2_backend.delete_domain_name(
|
||||||
|
domain_name=domain_name,
|
||||||
|
)
|
||||||
|
return 204, {}, ""
|
||||||
|
|
||||||
|
def delete_api_mapping(self) -> TYPE_RESPONSE:
|
||||||
|
api_mapping_id = self.path.split("/")[-1]
|
||||||
|
domain_name = self.path.split("/")[-3]
|
||||||
|
self.apigatewayv2_backend.delete_api_mapping(
|
||||||
|
api_mapping_id=api_mapping_id,
|
||||||
|
domain_name=domain_name,
|
||||||
|
)
|
||||||
|
return 204, {}, ""
|
||||||
|
@ -31,4 +31,8 @@ url_paths = {
|
|||||||
"{0}/v2/tags/(?P<resource_arn_pt1>[^/]+)/vpclinks/(?P<resource_arn_pt2>[^/]+)$": response_v2.tags,
|
"{0}/v2/tags/(?P<resource_arn_pt1>[^/]+)/vpclinks/(?P<resource_arn_pt2>[^/]+)$": response_v2.tags,
|
||||||
"{0}/v2/vpclinks$": response_v2.vpc_links,
|
"{0}/v2/vpclinks$": response_v2.vpc_links,
|
||||||
"{0}/v2/vpclinks/(?P<vpc_link_id>[^/]+)$": response_v2.vpc_link,
|
"{0}/v2/vpclinks/(?P<vpc_link_id>[^/]+)$": response_v2.vpc_link,
|
||||||
|
"{0}/v2/domainnames$": response_v2.domain_names,
|
||||||
|
"{0}/v2/domainnames/(?P<domain_name>[^/]+)$": response_v2.domain_name,
|
||||||
|
"{0}/v2/domainnames/(?P<domain_name>[^/]+)/apimappings$": response_v2.api_mappings,
|
||||||
|
"{0}/v2/domainnames/(?P<domain_name>[^/]+)/apimappings/(?P<api_mapping_id>[^/]+)$": response_v2.api_mapping,
|
||||||
}
|
}
|
||||||
|
96
tests/test_apigatewayv2/test_apigatewayv2_domains.py
Normal file
96
tests/test_apigatewayv2/test_apigatewayv2_domains.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import boto3
|
||||||
|
import sure # noqa # pylint: disable=unused-import
|
||||||
|
import pytest
|
||||||
|
import botocore.exceptions
|
||||||
|
from moto import mock_apigatewayv2
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_create_domain_name():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="us-east-1")
|
||||||
|
domain_name = "dev"
|
||||||
|
tags = {"tag": "it"}
|
||||||
|
expected_keys = [
|
||||||
|
"DomainName",
|
||||||
|
"ApiMappingSelectionExpression",
|
||||||
|
"DomainNameConfigurations",
|
||||||
|
"MutualTlsAuthentication",
|
||||||
|
"Tags",
|
||||||
|
]
|
||||||
|
|
||||||
|
post_resp = client.create_domain_name(DomainName=domain_name, Tags=tags)
|
||||||
|
get_resp = client.get_domain_name(DomainName=domain_name)
|
||||||
|
|
||||||
|
# check post response has all keys
|
||||||
|
for key in expected_keys:
|
||||||
|
post_resp.should.have.key(key)
|
||||||
|
|
||||||
|
# check get response has all keys
|
||||||
|
for key in expected_keys:
|
||||||
|
get_resp.should.have.key(key)
|
||||||
|
|
||||||
|
# check that values are equal for post and get of same resource
|
||||||
|
for key in expected_keys:
|
||||||
|
post_resp.get(key).should.equal(get_resp.get(key))
|
||||||
|
|
||||||
|
# ensure known values are set correct in post response
|
||||||
|
post_resp.get("DomainName").should.equal(domain_name)
|
||||||
|
post_resp.get("Tags").should.equal(tags)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_create_domain_name_already_exists():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="us-east-1")
|
||||||
|
client.create_domain_name(DomainName="exists.io")
|
||||||
|
|
||||||
|
with pytest.raises(botocore.exceptions.ClientError) as exc:
|
||||||
|
client.create_domain_name(DomainName="exists.io")
|
||||||
|
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("ConflictException")
|
||||||
|
err["Message"].should.equal("The domain name resource already exists.")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_get_domain_names():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="us-east-1")
|
||||||
|
dev_domain = client.create_domain_name(DomainName="dev.service.io")
|
||||||
|
prod_domain = client.create_domain_name(DomainName="prod.service.io")
|
||||||
|
|
||||||
|
# sanity check responses
|
||||||
|
dev_domain.should.have.key("DomainName").equals("dev.service.io")
|
||||||
|
prod_domain.should.have.key("DomainName").equals("prod.service.io")
|
||||||
|
|
||||||
|
# make comparable
|
||||||
|
del dev_domain["ResponseMetadata"]
|
||||||
|
del prod_domain["ResponseMetadata"]
|
||||||
|
|
||||||
|
get_resp = client.get_domain_names()
|
||||||
|
get_resp.should.have.key("Items")
|
||||||
|
get_resp.get("Items").should.contain(dev_domain)
|
||||||
|
get_resp.get("Items").should.contain(prod_domain)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_delete_domain_name():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="ap-southeast-1")
|
||||||
|
post_resp = client.create_domain_name(DomainName="dev.service.io")
|
||||||
|
client.delete_domain_name(DomainName="dev.service.io")
|
||||||
|
get_resp = client.get_domain_names()
|
||||||
|
|
||||||
|
del post_resp["ResponseMetadata"]
|
||||||
|
get_resp.should.have.key("Items")
|
||||||
|
get_resp.get("Items").should_not.contain(post_resp)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_delete_domain_name_dne():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="ap-southeast-1")
|
||||||
|
with pytest.raises(botocore.exceptions.ClientError) as exc:
|
||||||
|
client.delete_domain_name(DomainName="dne.io")
|
||||||
|
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("NotFoundException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"The domain name resource specified in the request was not found."
|
||||||
|
)
|
195
tests/test_apigatewayv2/test_apigatewayv2_mappings.py
Normal file
195
tests/test_apigatewayv2/test_apigatewayv2_mappings.py
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import boto3
|
||||||
|
import sure # noqa # pylint: disable=unused-import
|
||||||
|
import pytest
|
||||||
|
import botocore.exceptions
|
||||||
|
from moto import mock_apigatewayv2
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_create_api_mapping():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="us-east-1")
|
||||||
|
api = client.create_api(Name="test-api", ProtocolType="HTTP")
|
||||||
|
dev_domain = client.create_domain_name(DomainName="dev.service.io")
|
||||||
|
expected_keys = ["ApiId", "ApiMappingId", "ApiMappingKey", "Stage"]
|
||||||
|
|
||||||
|
post_resp = client.create_api_mapping(
|
||||||
|
DomainName=dev_domain["DomainName"],
|
||||||
|
ApiMappingKey="v1/api",
|
||||||
|
Stage="$default",
|
||||||
|
ApiId=api["ApiId"],
|
||||||
|
)
|
||||||
|
|
||||||
|
get_resp = client.get_api_mapping(
|
||||||
|
DomainName="dev.service.io", ApiMappingId=post_resp["ApiMappingId"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# check post response has all expected keys
|
||||||
|
for key in expected_keys:
|
||||||
|
post_resp.should.have.key(key)
|
||||||
|
|
||||||
|
# check get response has all expected keys
|
||||||
|
for key in expected_keys:
|
||||||
|
get_resp.should.have.key(key)
|
||||||
|
|
||||||
|
# check that values are equal for post and get of same resource
|
||||||
|
for key in expected_keys:
|
||||||
|
post_resp.get(key).should.equal(get_resp.get(key))
|
||||||
|
|
||||||
|
# ensure known values are set correct in post response
|
||||||
|
post_resp.get("ApiId").should_not.equal(None)
|
||||||
|
post_resp.get("ApiMappingId").should_not.equal(None)
|
||||||
|
post_resp.get("ApiMappingKey").should.equal("v1/api")
|
||||||
|
post_resp.get("Stage").should.equal("$default")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_create_api_mapping_missing_api():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="us-east-1")
|
||||||
|
dev_domain = client.create_domain_name(DomainName="dev.service.io")
|
||||||
|
with pytest.raises(botocore.exceptions.ClientError) as exc:
|
||||||
|
client.create_api_mapping(
|
||||||
|
DomainName=dev_domain["DomainName"],
|
||||||
|
Stage="$default",
|
||||||
|
ApiId="123dne",
|
||||||
|
)
|
||||||
|
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("NotFoundException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"Invalid API identifier specified The resource specified in the request was not found."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_create_api_mapping_missing_domain():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="us-east-1")
|
||||||
|
api = client.create_api(Name="test-api", ProtocolType="HTTP")
|
||||||
|
|
||||||
|
with pytest.raises(botocore.exceptions.ClientError) as exc:
|
||||||
|
client.create_api_mapping(
|
||||||
|
DomainName="domain.dne",
|
||||||
|
Stage="$default",
|
||||||
|
ApiId=api["ApiId"],
|
||||||
|
)
|
||||||
|
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("NotFoundException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"The domain name resource specified in the request was not found."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_create_api_mapping_bad_mapping_keys():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="us-east-1")
|
||||||
|
api = client.create_api(Name="test-api", ProtocolType="HTTP")
|
||||||
|
dev_domain = client.create_domain_name(DomainName="dev.service.io")
|
||||||
|
bad_keys = {
|
||||||
|
"/api": "API mapping key should not start with a '/' or have consecutive '/'s.",
|
||||||
|
"v1//api": "API mapping key should not start with a '/' or have consecutive '/'s.",
|
||||||
|
"api/": "API mapping key should not end with a '/'.",
|
||||||
|
}
|
||||||
|
|
||||||
|
for bad_key, message in bad_keys.items():
|
||||||
|
with pytest.raises(botocore.exceptions.ClientError) as exc:
|
||||||
|
client.create_api_mapping(
|
||||||
|
DomainName=dev_domain["DomainName"],
|
||||||
|
ApiMappingKey=bad_key,
|
||||||
|
Stage="$default",
|
||||||
|
ApiId=api["ApiId"],
|
||||||
|
)
|
||||||
|
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("BadRequestException")
|
||||||
|
err["Message"].should.equal(message)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_get_api_mappings():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="eu-west-1")
|
||||||
|
api = client.create_api(Name="test-api", ProtocolType="HTTP")
|
||||||
|
included_domain = client.create_domain_name(DomainName="dev.service.io")
|
||||||
|
excluded_domain = client.create_domain_name(DomainName="hr.service.io")
|
||||||
|
|
||||||
|
v1_mapping = client.create_api_mapping(
|
||||||
|
DomainName=included_domain["DomainName"],
|
||||||
|
ApiMappingKey="v1/api",
|
||||||
|
Stage="$default",
|
||||||
|
ApiId=api["ApiId"],
|
||||||
|
)
|
||||||
|
|
||||||
|
v2_mapping = client.create_api_mapping(
|
||||||
|
DomainName=included_domain["DomainName"],
|
||||||
|
ApiMappingKey="v2/api",
|
||||||
|
Stage="$default",
|
||||||
|
ApiId=api["ApiId"],
|
||||||
|
)
|
||||||
|
|
||||||
|
hr_mapping = client.create_api_mapping(
|
||||||
|
DomainName=excluded_domain["DomainName"],
|
||||||
|
ApiMappingKey="hr/api",
|
||||||
|
Stage="$default",
|
||||||
|
ApiId=api["ApiId"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# sanity check responses
|
||||||
|
v1_mapping.should.have.key("ApiMappingKey").equals("v1/api")
|
||||||
|
v2_mapping.should.have.key("ApiMappingKey").equals("v2/api")
|
||||||
|
hr_mapping.should.have.key("ApiMappingKey").equals("hr/api")
|
||||||
|
|
||||||
|
# make comparable
|
||||||
|
del v1_mapping["ResponseMetadata"]
|
||||||
|
del v2_mapping["ResponseMetadata"]
|
||||||
|
del hr_mapping["ResponseMetadata"]
|
||||||
|
|
||||||
|
get_resp = client.get_api_mappings(DomainName=included_domain["DomainName"])
|
||||||
|
get_resp.should.have.key("Items")
|
||||||
|
get_resp.get("Items").should.contain(v1_mapping)
|
||||||
|
get_resp.get("Items").should.contain(v2_mapping)
|
||||||
|
get_resp.get("Items").should_not.contain(hr_mapping)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_delete_api_mapping():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="eu-west-1")
|
||||||
|
api = client.create_api(Name="test-api", ProtocolType="HTTP")
|
||||||
|
api_domain = client.create_domain_name(DomainName="dev.service.io")
|
||||||
|
|
||||||
|
v1_mapping = client.create_api_mapping(
|
||||||
|
DomainName=api_domain["DomainName"],
|
||||||
|
ApiMappingKey="v1/api",
|
||||||
|
Stage="$default",
|
||||||
|
ApiId=api["ApiId"],
|
||||||
|
)
|
||||||
|
|
||||||
|
del v1_mapping["ResponseMetadata"]
|
||||||
|
|
||||||
|
get_resp = client.get_api_mappings(DomainName=api_domain["DomainName"])
|
||||||
|
get_resp.should.have.key("Items")
|
||||||
|
get_resp.get("Items").should.contain(v1_mapping)
|
||||||
|
|
||||||
|
client.delete_api_mapping(
|
||||||
|
DomainName=api_domain["DomainName"], ApiMappingId=v1_mapping["ApiMappingId"]
|
||||||
|
)
|
||||||
|
|
||||||
|
get_resp = client.get_api_mappings(DomainName=api_domain["DomainName"])
|
||||||
|
get_resp.should.have.key("Items")
|
||||||
|
get_resp.get("Items").should_not.contain(v1_mapping)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_apigatewayv2
|
||||||
|
def test_delete_api_mapping_dne():
|
||||||
|
client = boto3.client("apigatewayv2", region_name="eu-west-1")
|
||||||
|
client.create_api(Name="test-api", ProtocolType="HTTP")
|
||||||
|
api_domain = client.create_domain_name(DomainName="dev.service.io")
|
||||||
|
|
||||||
|
with pytest.raises(botocore.exceptions.ClientError) as exc:
|
||||||
|
client.delete_api_mapping(
|
||||||
|
DomainName=api_domain["DomainName"], ApiMappingId="123dne"
|
||||||
|
)
|
||||||
|
|
||||||
|
err = exc.value.response["Error"]
|
||||||
|
err["Code"].should.equal("NotFoundException")
|
||||||
|
err["Message"].should.equal(
|
||||||
|
"The api mapping resource specified in the request was not found."
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user