Skip to content

Commit d676d6a

Browse files
authored
APIGW: fix OpenAPI import StatusCode string casting (#12137)
1 parent 20f919b commit d676d6a

File tree

5 files changed

+194
-1
lines changed

5 files changed

+194
-1
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ def add_path_methods(rel_path: str, parts: List[str], parent_id=""):
725725
# Create the `MethodResponse` for the previously created `Method`
726726
method_responses = field_schema.get("responses", {})
727727
for method_status_code, method_response in method_responses.items():
728+
method_status_code = str(method_status_code)
728729
method_response_model = None
729730
model_ref = None
730731
# separating the two different versions, Swagger (2.0) and OpenAPI 3.0
@@ -822,7 +823,7 @@ def add_path_methods(rel_path: str, parts: List[str], parent_id=""):
822823
)
823824

824825
integration_response = integration.create_integration_response(
825-
status_code=integration_responses.get("statusCode", 200),
826+
status_code=str(integration_responses.get("statusCode", 200)),
826827
selection_pattern=pattern if pattern != "default" else None,
827828
response_templates=integration_response_templates,
828829
response_parameters=integration_response_parameters,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
openapi: 3.0.1
2+
info:
3+
title: test-import-oas-int
4+
version: '2.0'
5+
paths:
6+
"/test":
7+
get:
8+
responses:
9+
# the responses are grouped under the 200 integer status code. In the JSON format, this has to be a string.101:
10+
# AWS accepts integer status code in YAML.
11+
200:
12+
description: 200 response
13+
headers:
14+
Access-Control-Allow-Origin:
15+
schema:
16+
type: string
17+
content:
18+
application/json:
19+
schema:
20+
"$ref": "#/components/schemas/Empty"
21+
x-amazon-apigateway-integration:
22+
responses:
23+
default:
24+
# this represents the IntegrationResponse status code. In the YAML format, AWS accepts an integer, but the
25+
# "official" type is string and should be cast as such.
26+
statusCode: 200
27+
responseParameters:
28+
method.response.header.Access-Control-Allow-Origin: "'*'"
29+
requestParameters:
30+
integration.request.header.X-Amz-Invocation-Type: "'Event'"
31+
requestTemplates:
32+
application/json: '{"statusCode": 200}'
33+
passthroughBehavior: when_no_match
34+
type: mock
35+
36+
components:
37+
schemas:
38+
Empty:
39+
title: Empty Schema
40+
type: object

tests/aws/services/apigateway/test_apigateway_import.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
)
5050
OAS_30_STAGE_VARIABLES = os.path.join(PARENT_DIR, "../../files/openapi.spec.stage-variables.json")
5151
OAS30_HTTP_METHOD_INT = os.path.join(PARENT_DIR, "../../files/openapi-http-method-integration.json")
52+
OAS30_HTTP_STATUS_INT = os.path.join(PARENT_DIR, "../../files/openapi-method-int.spec.yaml")
5253
TEST_LAMBDA_PYTHON_ECHO = os.path.join(PARENT_DIR, "../lambda_/functions/lambda_echo.py")
5354

5455

@@ -895,3 +896,32 @@ def test_import_with_cognito_auth_identity_source(
895896
# this fixture will iterate over every resource and match its method, methodResponse, integration and
896897
# integrationResponse
897898
apigw_snapshot_imported_resources(rest_api_id=rest_api_id, resources=response)
899+
900+
@markers.aws.validated
901+
@markers.snapshot.skip_snapshot_verify(
902+
paths=[
903+
"$.resources.items..resourceMethods.GET",
904+
]
905+
)
906+
def test_import_with_integer_http_status_code(
907+
self,
908+
import_apigw,
909+
aws_client,
910+
apigw_snapshot_imported_resources,
911+
snapshot,
912+
):
913+
# the following YAML file contains integer status code for the Method and IntegrationResponse
914+
# when importing the API, we need to properly cast them into string to avoid any typing issue when serializing
915+
# responses. Most typed languages would fail when parsing.
916+
snapshot.add_transformer(snapshot.transform.key_value("uri"))
917+
spec_file = load_file(OAS30_HTTP_STATUS_INT)
918+
import_resp, root_id = import_apigw(body=spec_file, failOnWarnings=True)
919+
rest_api_id = import_resp["id"]
920+
921+
response = aws_client.apigateway.get_resources(restApiId=rest_api_id)
922+
response["items"] = sorted(response["items"], key=itemgetter("path"))
923+
snapshot.match("resources", response)
924+
925+
# this fixture will iterate over every resource and match its method, methodResponse, integration and
926+
# integrationResponse
927+
apigw_snapshot_imported_resources(rest_api_id=rest_api_id, resources=response)

tests/aws/services/apigateway/test_apigateway_import.snapshot.json

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5190,5 +5190,124 @@
51905190
}
51915191
}
51925192
}
5193+
},
5194+
"tests/aws/services/apigateway/test_apigateway_import.py::TestApiGatewayImportRestApi::test_import_with_integer_http_status_code": {
5195+
"recorded-date": "14-01-2025, 14:09:57",
5196+
"recorded-content": {
5197+
"resources": {
5198+
"items": [
5199+
{
5200+
"id": "<id:1>",
5201+
"path": "/"
5202+
},
5203+
{
5204+
"id": "<id:2>",
5205+
"parentId": "<id:1>",
5206+
"path": "/test",
5207+
"pathPart": "test",
5208+
"resourceMethods": {
5209+
"GET": {}
5210+
}
5211+
}
5212+
],
5213+
"ResponseMetadata": {
5214+
"HTTPHeaders": {},
5215+
"HTTPStatusCode": 200
5216+
}
5217+
},
5218+
"method-test-get": {
5219+
"apiKeyRequired": false,
5220+
"authorizationType": "NONE",
5221+
"httpMethod": "GET",
5222+
"methodIntegration": {
5223+
"cacheKeyParameters": [],
5224+
"cacheNamespace": "<id:2>",
5225+
"integrationResponses": {
5226+
"200": {
5227+
"responseParameters": {
5228+
"method.response.header.Access-Control-Allow-Origin": "'*'"
5229+
},
5230+
"statusCode": "200"
5231+
}
5232+
},
5233+
"passthroughBehavior": "WHEN_NO_MATCH",
5234+
"requestParameters": {
5235+
"integration.request.header.X-Amz-Invocation-Type": "'Event'"
5236+
},
5237+
"requestTemplates": {
5238+
"application/json": {
5239+
"statusCode": 200
5240+
}
5241+
},
5242+
"timeoutInMillis": 29000,
5243+
"type": "MOCK"
5244+
},
5245+
"methodResponses": {
5246+
"200": {
5247+
"responseModels": {
5248+
"application/json": "Empty"
5249+
},
5250+
"responseParameters": {
5251+
"method.response.header.Access-Control-Allow-Origin": false
5252+
},
5253+
"statusCode": "200"
5254+
}
5255+
},
5256+
"ResponseMetadata": {
5257+
"HTTPHeaders": {},
5258+
"HTTPStatusCode": 200
5259+
}
5260+
},
5261+
"method-response-test-get": {
5262+
"responseModels": {
5263+
"application/json": "Empty"
5264+
},
5265+
"responseParameters": {
5266+
"method.response.header.Access-Control-Allow-Origin": false
5267+
},
5268+
"statusCode": "200",
5269+
"ResponseMetadata": {
5270+
"HTTPHeaders": {},
5271+
"HTTPStatusCode": 200
5272+
}
5273+
},
5274+
"integration-test-get": {
5275+
"cacheKeyParameters": [],
5276+
"cacheNamespace": "<id:2>",
5277+
"integrationResponses": {
5278+
"200": {
5279+
"responseParameters": {
5280+
"method.response.header.Access-Control-Allow-Origin": "'*'"
5281+
},
5282+
"statusCode": "200"
5283+
}
5284+
},
5285+
"passthroughBehavior": "WHEN_NO_MATCH",
5286+
"requestParameters": {
5287+
"integration.request.header.X-Amz-Invocation-Type": "'Event'"
5288+
},
5289+
"requestTemplates": {
5290+
"application/json": {
5291+
"statusCode": 200
5292+
}
5293+
},
5294+
"timeoutInMillis": 29000,
5295+
"type": "MOCK",
5296+
"ResponseMetadata": {
5297+
"HTTPHeaders": {},
5298+
"HTTPStatusCode": 200
5299+
}
5300+
},
5301+
"integration-response-test-get": {
5302+
"responseParameters": {
5303+
"method.response.header.Access-Control-Allow-Origin": "'*'"
5304+
},
5305+
"statusCode": "200",
5306+
"ResponseMetadata": {
5307+
"HTTPHeaders": {},
5308+
"HTTPStatusCode": 200
5309+
}
5310+
}
5311+
}
51935312
}
51945313
}

tests/aws/services/apigateway/test_apigateway_import.validation.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
"tests/aws/services/apigateway/test_apigateway_import.py::TestApiGatewayImportRestApi::test_import_with_http_method_integration": {
4545
"last_validated_date": "2024-04-15T21:38:57+00:00"
4646
},
47+
"tests/aws/services/apigateway/test_apigateway_import.py::TestApiGatewayImportRestApi::test_import_with_integer_http_status_code": {
48+
"last_validated_date": "2025-01-14T14:09:57+00:00"
49+
},
4750
"tests/aws/services/apigateway/test_apigateway_import.py::TestApiGatewayImportRestApi::test_import_with_stage_variables": {
4851
"last_validated_date": "2024-08-12T13:42:13+00:00"
4952
}

0 commit comments

Comments
 (0)