Skip to content
This repository was archived by the owner on Mar 23, 2026. It is now read-only.

Commit 20cc1b3

Browse files
authored
APIGW: fix model typing / update custom id logic (#13694)
1 parent ac2f031 commit 20cc1b3

File tree

4 files changed

+51
-17
lines changed

4 files changed

+51
-17
lines changed

localstack-core/localstack/services/apigateway/models.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from typing import Any
22

3-
from requests.structures import CaseInsensitiveDict
4-
53
from localstack.aws.api.apigateway import (
64
Account,
75
Authorizer,
@@ -109,8 +107,7 @@ def __init__(
109107

110108
class ApiGatewayStore(BaseStore):
111109
# maps (API id) -> RestApiContainer
112-
# TODO: remove CaseInsensitiveDict, and lower the value of the ID when getting it from the tags
113-
rest_apis: dict[str, RestApiContainer] = LocalAttribute(default=CaseInsensitiveDict)
110+
rest_apis: dict[str, RestApiContainer] = LocalAttribute(default=dict)
114111

115112
# account details
116113
_account: Account = LocalAttribute(default=dict)

localstack-core/localstack/services/apigateway/patches.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
from moto.apigateway import models as apigateway_models
66
from moto.apigateway.exceptions import (
7+
BadRequestException,
78
DeploymentNotFoundException,
8-
RestAPINotFound,
99
StageStillActive,
1010
)
1111
from moto.apigateway.responses import APIGatewayResponse
@@ -169,19 +169,19 @@ def create_rest_api(fn, self, *args, tags=None, **kwargs):
169169
"""
170170
tags = tags or {}
171171
result = fn(self, *args, tags=tags, **kwargs)
172-
# TODO: lower the custom_id when getting it from the tags, as AWS is case insensitive
172+
173173
if custom_id := tags.get(TAG_KEY_CUSTOM_ID):
174174
self.apis.pop(result.id)
175175
result.id = custom_id
176176
self.apis[custom_id] = result
177-
return result
178177

179-
@patch(apigateway_models.APIGatewayBackend.get_rest_api, pass_target=False)
180-
def get_rest_api(self, function_id):
181-
for key in self.apis.keys():
182-
if key.lower() == function_id.lower():
183-
return self.apis[key]
184-
raise RestAPINotFound()
178+
if not (result.id.islower() or result.id.isnumeric()):
179+
self.apis.pop(result.id)
180+
raise BadRequestException(
181+
f"The RestApiId '{result.id}' cannot contain uppercase characters"
182+
)
183+
184+
return result
185185

186186
@patch(apigateway_models.RestAPI.delete_deployment, pass_target=False)
187187
def patch_delete_deployment(self, deployment_id: str) -> apigateway_models.Deployment:

tests/aws/services/apigateway/test_apigateway_basic.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def test_create_rest_api_with_custom_id(self, create_rest_apigw, url_function, a
142142
if not is_next_gen_api() and url_function == localstack_path_based_url:
143143
pytest.skip("This URL type is not supported in the legacy implementation")
144144
apigw_name = f"gw-{short_uid()}"
145-
test_id = "testId123"
145+
test_id = "test-id123"
146146
api_id, name, _ = create_rest_apigw(name=apigw_name, tags={TAG_KEY_CUSTOM_ID: test_id})
147147
assert test_id == api_id
148148
assert apigw_name == name
@@ -160,6 +160,20 @@ def test_create_rest_api_with_custom_id(self, create_rest_apigw, url_function, a
160160
assert response.ok
161161
assert response._content == b'{"echo": "foobar", "response": "mocked"}'
162162

163+
@markers.aws.only_localstack
164+
# This is not a possible feature on aws.
165+
def test_create_rest_api_with_invalid_custom_id(self, create_rest_apigw, aws_client):
166+
apigw_name = f"gw-{short_uid()}"
167+
test_id = "testId123"
168+
with pytest.raises(ClientError) as exc:
169+
create_rest_apigw(name=apigw_name, tags={TAG_KEY_CUSTOM_ID: test_id})
170+
171+
assert exc.value.response["Error"]["Code"] == "BadRequestException"
172+
assert (
173+
exc.value.response["Error"]["Message"]
174+
== f"The RestApiId '{test_id}' cannot contain uppercase characters"
175+
)
176+
163177
@markers.aws.validated
164178
def test_update_rest_api_deployment(self, create_rest_apigw, aws_client, snapshot):
165179
snapshot.add_transformer(snapshot.transform.key_value("id"))
@@ -1505,10 +1519,11 @@ class TestTagging:
15051519
def test_tag_api(self, create_rest_apigw, aws_client, account_id, region_name):
15061520
api_name = f"api-{short_uid()}"
15071521
tags = {"foo": "bar"}
1522+
custom_id = "c0stiom1d"
15081523

15091524
# add resource tags
1510-
api_id, _, _ = create_rest_apigw(name=api_name, tags={TAG_KEY_CUSTOM_ID: "c0stIOm1d"})
1511-
assert api_id == "c0stIOm1d"
1525+
api_id, _, _ = create_rest_apigw(name=api_name, tags={TAG_KEY_CUSTOM_ID: custom_id})
1526+
assert api_id == custom_id
15121527

15131528
api_arn = arns.apigateway_restapi_arn(api_id, account_id, region_name)
15141529
aws_client.apigateway.tag_resource(resourceArn=api_arn, tags=tags)

tests/aws/services/apigateway/test_apigateway_custom_ids.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import pytest
2+
from botocore.exceptions import ClientError
13
from moto.apigateway.utils import (
24
ApigwApiKeyIdentifier,
35
ApigwResourceIdentifier,
@@ -7,7 +9,7 @@
79
from localstack.testing.pytest import markers
810
from localstack.utils.strings import long_uid, short_uid
911

10-
API_ID = "ApiId"
12+
API_ID = "api-id"
1113
ROOT_RESOURCE_ID = "RootId"
1214
PET_1_RESOURCE_ID = "Pet1Id"
1315
PET_2_RESOURCE_ID = "Pet2Id"
@@ -60,3 +62,23 @@ def test_apigateway_custom_ids(
6062
assert pet_resource_1["id"] == PET_1_RESOURCE_ID
6163
assert pet_resource_2["id"] == PET_2_RESOURCE_ID
6264
assert api_key["id"] == API_KEY_ID
65+
66+
67+
@markers.aws.only_localstack
68+
@markers.requires_in_process
69+
def test_apigateway_invalid_rest_api_custom_id(
70+
aws_client, set_resource_custom_id, create_rest_apigw, account_id, region_name, cleanups
71+
):
72+
rest_api_name = f"apigw-{short_uid()}"
73+
bad_api_id = "UpperCaseApi"
74+
75+
set_resource_custom_id(
76+
ApigwRestApiIdentifier(account_id, region_name, rest_api_name), bad_api_id
77+
)
78+
with pytest.raises(ClientError) as exc:
79+
create_rest_apigw(name=rest_api_name)
80+
assert exc.value.response["Error"]["Code"] == "BadRequestException"
81+
assert (
82+
exc.value.response["Error"]["Message"]
83+
== f"The RestApiId '{bad_api_id}' cannot contain uppercase characters"
84+
)

0 commit comments

Comments
 (0)