diff --git a/moto/apigateway/models.py b/moto/apigateway/models.py index fd2fb7064..234d6636c 100644 --- a/moto/apigateway/models.py +++ b/moto/apigateway/models.py @@ -394,12 +394,17 @@ class UsagePlanKey(BaseModel, dict): class RestAPI(BaseModel): - def __init__(self, id, region_name, name, description): + def __init__(self, id, region_name, name, description, **kwargs): self.id = id self.region_name = region_name self.name = name self.description = description self.create_date = int(time.time()) + self.api_key_source = kwargs.get("api_key_source") or "HEADER" + self.endpoint_configuration = kwargs.get("endpoint_configuration") or { + "types": ["EDGE"] + } + self.tags = kwargs.get("tags") or {} self.deployments = {} self.stages = {} @@ -416,6 +421,9 @@ class RestAPI(BaseModel): "name": self.name, "description": self.description, "createdDate": int(time.time()), + "apiKeySource": self.api_key_source, + "endpointConfiguration": self.endpoint_configuration, + "tags": self.tags, } def add_child(self, path, parent_id=None): @@ -529,9 +537,24 @@ class APIGatewayBackend(BaseBackend): self.__dict__ = {} self.__init__(region_name) - def create_rest_api(self, name, description): + def create_rest_api( + self, + name, + description, + api_key_source=None, + endpoint_configuration=None, + tags=None, + ): api_id = create_id() - rest_api = RestAPI(api_id, self.region_name, name, description) + rest_api = RestAPI( + api_id, + self.region_name, + name, + description, + api_key_source=api_key_source, + endpoint_configuration=endpoint_configuration, + tags=tags, + ) self.apis[api_id] = rest_api return rest_api diff --git a/moto/apigateway/responses.py b/moto/apigateway/responses.py index c4c7b403e..e10d670c5 100644 --- a/moto/apigateway/responses.py +++ b/moto/apigateway/responses.py @@ -12,6 +12,9 @@ from .exceptions import ( ApiKeyAlreadyExists, ) +API_KEY_SOURCES = ["AUTHORIZER", "HEADER"] +ENDPOINT_CONFIGURATION_TYPES = ["PRIVATE", "EDGE", "REGIONAL"] + class APIGatewayResponse(BaseResponse): def error(self, type_, message, status=400): @@ -45,7 +48,45 @@ class APIGatewayResponse(BaseResponse): elif self.method == "POST": name = self._get_param("name") description = self._get_param("description") - rest_api = self.backend.create_rest_api(name, description) + api_key_source = self._get_param("apiKeySource") + endpoint_configuration = self._get_param("endpointConfiguration") + tags = self._get_param("tags") + + # Param validation + if api_key_source and api_key_source not in API_KEY_SOURCES: + return self.error( + "ValidationException", + ( + "1 validation error detected: " + "Value '{api_key_source}' at 'createRestApiInput.apiKeySource' failed " + "to satisfy constraint: Member must satisfy enum value set: " + "[AUTHORIZER, HEADER]" + ).format(api_key_source=api_key_source), + ) + + if endpoint_configuration and "types" in endpoint_configuration: + invalid_types = list( + set(endpoint_configuration["types"]) + - set(ENDPOINT_CONFIGURATION_TYPES) + ) + if invalid_types: + return self.error( + "ValidationException", + ( + "1 validation error detected: Value '{endpoint_type}' " + "at 'createRestApiInput.endpointConfiguration.types' failed " + "to satisfy constraint: Member must satisfy enum value set: " + "[PRIVATE, EDGE, REGIONAL]" + ).format(endpoint_type=invalid_types[0]), + ) + + rest_api = self.backend.create_rest_api( + name, + description, + api_key_source=api_key_source, + endpoint_configuration=endpoint_configuration, + tags=tags, + ) return 200, {}, json.dumps(rest_api.to_dict()) def restapis_individual(self, request, full_url, headers): diff --git a/tests/test_apigateway/test_apigateway.py b/tests/test_apigateway/test_apigateway.py index 59c0c07f6..37bcc97f7 100644 --- a/tests/test_apigateway/test_apigateway.py +++ b/tests/test_apigateway/test_apigateway.py @@ -26,7 +26,14 @@ def test_create_and_get_rest_api(): response.pop("ResponseMetadata") response.pop("createdDate") response.should.equal( - {"id": api_id, "name": "my_api", "description": "this is my api"} + { + "id": api_id, + "name": "my_api", + "description": "this is my api", + "apiKeySource": "HEADER", + "endpointConfiguration": {"types": ["EDGE"]}, + "tags": {}, + } ) @@ -47,6 +54,114 @@ def test_list_and_delete_apis(): len(response["items"]).should.equal(1) +@mock_apigateway +def test_create_rest_api_with_tags(): + client = boto3.client("apigateway", region_name="us-west-2") + + response = client.create_rest_api( + name="my_api", description="this is my api", tags={"MY_TAG1": "MY_VALUE1"} + ) + api_id = response["id"] + + response = client.get_rest_api(restApiId=api_id) + + assert "tags" in response + response["tags"].should.equal({"MY_TAG1": "MY_VALUE1"}) + + +@mock_apigateway +def test_create_rest_api_invalid_apikeysource(): + client = boto3.client("apigateway", region_name="us-west-2") + + with assert_raises(ClientError) as ex: + client.create_rest_api( + name="my_api", + description="this is my api", + apiKeySource="not a valid api key source", + ) + ex.exception.response["Error"]["Code"].should.equal("ValidationException") + + +@mock_apigateway +def test_create_rest_api_valid_apikeysources(): + client = boto3.client("apigateway", region_name="us-west-2") + + # 1. test creating rest api with HEADER apiKeySource + response = client.create_rest_api( + name="my_api", description="this is my api", apiKeySource="HEADER", + ) + api_id = response["id"] + + response = client.get_rest_api(restApiId=api_id) + response["apiKeySource"].should.equal("HEADER") + + # 2. test creating rest api with AUTHORIZER apiKeySource + response = client.create_rest_api( + name="my_api2", description="this is my api", apiKeySource="AUTHORIZER", + ) + api_id = response["id"] + + response = client.get_rest_api(restApiId=api_id) + response["apiKeySource"].should.equal("AUTHORIZER") + + +@mock_apigateway +def test_create_rest_api_invalid_endpointconfiguration(): + client = boto3.client("apigateway", region_name="us-west-2") + + with assert_raises(ClientError) as ex: + client.create_rest_api( + name="my_api", + description="this is my api", + endpointConfiguration={"types": ["INVALID"]}, + ) + ex.exception.response["Error"]["Code"].should.equal("ValidationException") + + +@mock_apigateway +def test_create_rest_api_valid_endpointconfigurations(): + client = boto3.client("apigateway", region_name="us-west-2") + + # 1. test creating rest api with PRIVATE endpointConfiguration + response = client.create_rest_api( + name="my_api", + description="this is my api", + endpointConfiguration={"types": ["PRIVATE"]}, + ) + api_id = response["id"] + + response = client.get_rest_api(restApiId=api_id) + response["endpointConfiguration"].should.equal( + {"types": ["PRIVATE"],} + ) + + # 2. test creating rest api with REGIONAL endpointConfiguration + response = client.create_rest_api( + name="my_api2", + description="this is my api", + endpointConfiguration={"types": ["REGIONAL"]}, + ) + api_id = response["id"] + + response = client.get_rest_api(restApiId=api_id) + response["endpointConfiguration"].should.equal( + {"types": ["REGIONAL"],} + ) + + # 3. test creating rest api with EDGE endpointConfiguration + response = client.create_rest_api( + name="my_api3", + description="this is my api", + endpointConfiguration={"types": ["EDGE"]}, + ) + api_id = response["id"] + + response = client.get_rest_api(restApiId=api_id) + response["endpointConfiguration"].should.equal( + {"types": ["EDGE"],} + ) + + @mock_apigateway def test_create_resource__validate_name(): client = boto3.client("apigateway", region_name="us-west-2")