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