Skip to content

Commit 2bccbd3

Browse files
bentskusimonrw
authored andcommitted
APIGW: fix binaryMediaTypes when importing/updating REST APIs (#12586)
1 parent 722d022 commit 2bccbd3

15 files changed

+425
-49
lines changed

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,15 @@ def export(
190190
self._add_paths(spec, resources, with_extension)
191191
self._add_models(spec, models["items"], "#/definitions")
192192

193-
return getattr(spec, self.export_formats.get(export_format))()
193+
response = getattr(spec, self.export_formats.get(export_format))()
194+
if (
195+
with_extension
196+
and isinstance(response, dict)
197+
and (binary_media_types := rest_api.get("binaryMediaTypes")) is not None
198+
):
199+
response[OpenAPIExt.BINARY_MEDIA_TYPES] = binary_media_types
200+
201+
return response
194202

195203

196204
class _OpenApiOAS30Exporter(_BaseOpenApiExporter):
@@ -298,8 +306,16 @@ def export(
298306
self._add_models(spec, models["items"], "#/components/schemas")
299307

300308
response = getattr(spec, self.export_formats.get(export_format))()
301-
if isinstance(response, dict) and "components" not in response:
302-
response["components"] = {}
309+
if isinstance(response, dict):
310+
if "components" not in response:
311+
response["components"] = {}
312+
313+
if (
314+
with_extension
315+
and (binary_media_types := rest_api.get("binaryMediaTypes")) is not None
316+
):
317+
response[OpenAPIExt.BINARY_MEDIA_TYPES] = binary_media_types
318+
303319
return response
304320

305321

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,10 @@ def import_api_from_openapi_spec(
492492
region_name = context.region
493493

494494
# TODO:
495-
# 1. validate the "mode" property of the spec document, "merge" or "overwrite"
495+
# 1. validate the "mode" property of the spec document, "merge" or "overwrite", and properly apply it
496+
# for now, it only considers it for the binaryMediaTypes
496497
# 2. validate the document type, "swagger" or "openapi"
498+
mode = request.get("mode", "merge")
497499

498500
rest_api.version = (
499501
str(version) if (version := resolved_schema.get("info", {}).get("version")) else None
@@ -948,7 +950,14 @@ def create_method_resource(child, method, method_schema):
948950
get_or_create_path(base_path + path, base_path=base_path)
949951

950952
# binary types
951-
rest_api.binaryMediaTypes = resolved_schema.get(OpenAPIExt.BINARY_MEDIA_TYPES, [])
953+
if mode == "merge":
954+
existing_binary_media_types = rest_api.binaryMediaTypes or []
955+
else:
956+
existing_binary_media_types = []
957+
958+
rest_api.binaryMediaTypes = existing_binary_media_types + resolved_schema.get(
959+
OpenAPIExt.BINARY_MEDIA_TYPES, []
960+
)
952961

953962
policy = resolved_schema.get(OpenAPIExt.POLICY)
954963
if policy:

tests/aws/files/pets.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
"schemes": [
88
"https"
99
],
10+
"x-amazon-apigateway-binary-media-types": [
11+
"image/png",
12+
"image/jpg"
13+
],
1014
"paths": {
1115
"/pets": {
1216
"get": {

tests/aws/services/apigateway/test_apigateway_basic.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
THIS_FOLDER = os.path.dirname(os.path.realpath(__file__))
7979
TEST_SWAGGER_FILE_JSON = os.path.join(THIS_FOLDER, "../../files/swagger.json")
8080
TEST_SWAGGER_FILE_YAML = os.path.join(THIS_FOLDER, "../../files/swagger.yaml")
81-
TEST_IMPORT_REST_API_FILE = os.path.join(THIS_FOLDER, "../../files/pets.json")
8281
TEST_IMPORT_MOCK_INTEGRATION = os.path.join(THIS_FOLDER, "../../files/openapi-mock.json")
8382
TEST_IMPORT_REST_API_ASYNC_LAMBDA = os.path.join(THIS_FOLDER, "../../files/api_definition.yaml")
8483

tests/aws/services/apigateway/test_apigateway_extended.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ def _create(**kwargs):
4343
[TEST_IMPORT_PETSTORE_SWAGGER, TEST_IMPORT_PETS],
4444
ids=["TEST_IMPORT_PETSTORE_SWAGGER", "TEST_IMPORT_PETS"],
4545
)
46-
@markers.snapshot.skip_snapshot_verify(paths=["$..body.host"])
46+
@markers.snapshot.skip_snapshot_verify(
47+
paths=[
48+
"$..body.host",
49+
# TODO: not returned by LS
50+
"$..endpointConfiguration.ipAddressType",
51+
]
52+
)
4753
def test_export_swagger_openapi(aws_client, snapshot, import_apigw, import_file, region_name):
4854
snapshot.add_transformer(
4955
[
@@ -82,7 +88,13 @@ def test_export_swagger_openapi(aws_client, snapshot, import_apigw, import_file,
8288
[TEST_IMPORT_PETSTORE_SWAGGER, TEST_IMPORT_PETS],
8389
ids=["TEST_IMPORT_PETSTORE_SWAGGER", "TEST_IMPORT_PETS"],
8490
)
85-
@markers.snapshot.skip_snapshot_verify(paths=["$..body.servers..url"])
91+
@markers.snapshot.skip_snapshot_verify(
92+
paths=[
93+
"$..body.servers..url",
94+
# TODO: not returned by LS
95+
"$..endpointConfiguration.ipAddressType",
96+
]
97+
)
8698
def test_export_oas30_openapi(aws_client, snapshot, import_apigw, region_name, import_file):
8799
snapshot.add_transformer(
88100
[

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

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
{
22
"tests/aws/services/apigateway/test_apigateway_extended.py::test_export_swagger_openapi[TEST_IMPORT_PETSTORE_SWAGGER]": {
3-
"recorded-date": "15-04-2024, 21:43:25",
3+
"recorded-date": "06-05-2025, 18:20:26",
44
"recorded-content": {
55
"import-api": {
66
"apiKeySource": "HEADER",
77
"createdDate": "datetime",
88
"description": "Your first API with Amazon API Gateway. This is a sample API that integrates via HTTP with our demo Pet Store endpoints",
99
"disableExecuteApiEndpoint": false,
1010
"endpointConfiguration": {
11+
"ipAddressType": "ipv4",
1112
"types": [
1213
"EDGE"
1314
]
@@ -638,13 +639,18 @@
638639
}
639640
},
640641
"tests/aws/services/apigateway/test_apigateway_extended.py::test_export_swagger_openapi[TEST_IMPORT_PETS]": {
641-
"recorded-date": "15-04-2024, 21:43:56",
642+
"recorded-date": "06-05-2025, 18:21:08",
642643
"recorded-content": {
643644
"import-api": {
644645
"apiKeySource": "HEADER",
646+
"binaryMediaTypes": [
647+
"image/png",
648+
"image/jpg"
649+
],
645650
"createdDate": "datetime",
646651
"disableExecuteApiEndpoint": false,
647652
"endpointConfiguration": {
653+
"ipAddressType": "ipv4",
648654
"types": [
649655
"EDGE"
650656
]
@@ -727,15 +733,15 @@
727733
}
728734
},
729735
"x-amazon-apigateway-integration": {
736+
"type": "http",
730737
"httpMethod": "GET",
731738
"uri": "http://petstore-demo-endpoint.execute-api.com/petstore/pets",
732739
"responses": {
733740
"default": {
734741
"statusCode": "200"
735742
}
736743
},
737-
"passthroughBehavior": "when_no_match",
738-
"type": "http"
744+
"passthroughBehavior": "when_no_match"
739745
}
740746
}
741747
},
@@ -755,6 +761,7 @@
755761
}
756762
},
757763
"x-amazon-apigateway-integration": {
764+
"type": "http",
758765
"httpMethod": "GET",
759766
"uri": "http://petstore-demo-endpoint.execute-api.com/petstore/pets/{id}",
760767
"responses": {
@@ -765,12 +772,15 @@
765772
"requestParameters": {
766773
"integration.request.path.id": "method.request.path.petId"
767774
},
768-
"passthroughBehavior": "when_no_match",
769-
"type": "http"
775+
"passthroughBehavior": "when_no_match"
770776
}
771777
}
772778
}
773-
}
779+
},
780+
"x-amazon-apigateway-binary-media-types": [
781+
"image/png",
782+
"image/jpg"
783+
]
774784
},
775785
"contentDisposition": "attachment; filename=\"swagger_1.0.0.json\"",
776786
"contentType": "application/octet-stream",
@@ -782,14 +792,15 @@
782792
}
783793
},
784794
"tests/aws/services/apigateway/test_apigateway_extended.py::test_export_oas30_openapi[TEST_IMPORT_PETSTORE_SWAGGER]": {
785-
"recorded-date": "15-04-2024, 21:45:03",
795+
"recorded-date": "06-05-2025, 18:34:11",
786796
"recorded-content": {
787797
"import-api": {
788798
"apiKeySource": "HEADER",
789799
"createdDate": "datetime",
790800
"description": "Your first API with Amazon API Gateway. This is a sample API that integrates via HTTP with our demo Pet Store endpoints",
791801
"disableExecuteApiEndpoint": false,
792802
"endpointConfiguration": {
803+
"ipAddressType": "ipv4",
793804
"types": [
794805
"EDGE"
795806
]
@@ -1140,6 +1151,7 @@
11401151
}
11411152
},
11421153
"x-amazon-apigateway-integration": {
1154+
"type": "http",
11431155
"httpMethod": "GET",
11441156
"uri": "http://petstore.execute-api.<region>.amazonaws.com/petstore/pets",
11451157
"responses": {
@@ -1154,8 +1166,7 @@
11541166
"integration.request.querystring.page": "method.request.querystring.page",
11551167
"integration.request.querystring.type": "method.request.querystring.type"
11561168
},
1157-
"passthroughBehavior": "when_no_match",
1158-
"type": "http"
1169+
"passthroughBehavior": "when_no_match"
11591170
}
11601171
},
11611172
"post": {
@@ -1190,6 +1201,7 @@
11901201
}
11911202
},
11921203
"x-amazon-apigateway-integration": {
1204+
"type": "http",
11931205
"httpMethod": "POST",
11941206
"uri": "http://petstore.execute-api.<region>.amazonaws.com/petstore/pets",
11951207
"responses": {
@@ -1200,8 +1212,7 @@
12001212
}
12011213
}
12021214
},
1203-
"passthroughBehavior": "when_no_match",
1204-
"type": "http"
1215+
"passthroughBehavior": "when_no_match"
12051216
}
12061217
},
12071218
"options": {
@@ -1235,6 +1246,7 @@
12351246
}
12361247
},
12371248
"x-amazon-apigateway-integration": {
1249+
"type": "mock",
12381250
"responses": {
12391251
"default": {
12401252
"statusCode": "200",
@@ -1248,8 +1260,7 @@
12481260
"requestTemplates": {
12491261
"application/json": "{\"statusCode\": 200}"
12501262
},
1251-
"passthroughBehavior": "when_no_match",
1252-
"type": "mock"
1263+
"passthroughBehavior": "when_no_match"
12531264
}
12541265
}
12551266
},
@@ -1286,6 +1297,7 @@
12861297
}
12871298
},
12881299
"x-amazon-apigateway-integration": {
1300+
"type": "http",
12891301
"httpMethod": "GET",
12901302
"uri": "http://petstore.execute-api.<region>.amazonaws.com/petstore/pets/{petId}",
12911303
"responses": {
@@ -1299,8 +1311,7 @@
12991311
"requestParameters": {
13001312
"integration.request.path.petId": "method.request.path.petId"
13011313
},
1302-
"passthroughBehavior": "when_no_match",
1303-
"type": "http"
1314+
"passthroughBehavior": "when_no_match"
13041315
}
13051316
},
13061317
"options": {
@@ -1344,6 +1355,7 @@
13441355
}
13451356
},
13461357
"x-amazon-apigateway-integration": {
1358+
"type": "mock",
13471359
"responses": {
13481360
"default": {
13491361
"statusCode": "200",
@@ -1357,8 +1369,7 @@
13571369
"requestTemplates": {
13581370
"application/json": "{\"statusCode\": 200}"
13591371
},
1360-
"passthroughBehavior": "when_no_match",
1361-
"type": "mock"
1372+
"passthroughBehavior": "when_no_match"
13621373
}
13631374
}
13641375
},
@@ -1378,6 +1389,7 @@
13781389
}
13791390
},
13801391
"x-amazon-apigateway-integration": {
1392+
"type": "mock",
13811393
"responses": {
13821394
"default": {
13831395
"statusCode": "200",
@@ -1392,8 +1404,7 @@
13921404
"requestTemplates": {
13931405
"application/json": "{\"statusCode\": 200}"
13941406
},
1395-
"passthroughBehavior": "when_no_match",
1396-
"type": "mock"
1407+
"passthroughBehavior": "when_no_match"
13971408
}
13981409
}
13991410
}
@@ -1468,13 +1479,18 @@
14681479
}
14691480
},
14701481
"tests/aws/services/apigateway/test_apigateway_extended.py::test_export_oas30_openapi[TEST_IMPORT_PETS]": {
1471-
"recorded-date": "15-04-2024, 21:45:07",
1482+
"recorded-date": "06-05-2025, 18:34:49",
14721483
"recorded-content": {
14731484
"import-api": {
14741485
"apiKeySource": "HEADER",
1486+
"binaryMediaTypes": [
1487+
"image/png",
1488+
"image/jpg"
1489+
],
14751490
"createdDate": "datetime",
14761491
"disableExecuteApiEndpoint": false,
14771492
"endpointConfiguration": {
1493+
"ipAddressType": "ipv4",
14781494
"types": [
14791495
"EDGE"
14801496
]
@@ -1620,7 +1636,11 @@
16201636
}
16211637
}
16221638
},
1623-
"components": {}
1639+
"components": {},
1640+
"x-amazon-apigateway-binary-media-types": [
1641+
"image/png",
1642+
"image/jpg"
1643+
]
16241644
},
16251645
"contentDisposition": "attachment; filename=\"oas30_1.0.0.json\"",
16261646
"contentType": "application/octet-stream",

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
"last_validated_date": "2024-10-10T18:54:41+00:00"
77
},
88
"tests/aws/services/apigateway/test_apigateway_extended.py::test_export_oas30_openapi[TEST_IMPORT_PETSTORE_SWAGGER]": {
9-
"last_validated_date": "2024-04-15T21:45:02+00:00"
9+
"last_validated_date": "2025-05-06T18:34:11+00:00"
1010
},
1111
"tests/aws/services/apigateway/test_apigateway_extended.py::test_export_oas30_openapi[TEST_IMPORT_PETS]": {
12-
"last_validated_date": "2024-04-15T21:45:04+00:00"
12+
"last_validated_date": "2025-05-06T18:34:17+00:00"
1313
},
1414
"tests/aws/services/apigateway/test_apigateway_extended.py::test_export_swagger_openapi[TEST_IMPORT_PETSTORE_SWAGGER]": {
15-
"last_validated_date": "2024-04-15T21:43:24+00:00"
15+
"last_validated_date": "2025-05-06T18:20:25+00:00"
1616
},
1717
"tests/aws/services/apigateway/test_apigateway_extended.py::test_export_swagger_openapi[TEST_IMPORT_PETS]": {
18-
"last_validated_date": "2024-04-15T21:43:30+00:00"
18+
"last_validated_date": "2025-05-06T18:20:36+00:00"
1919
}
2020
}

0 commit comments

Comments
 (0)