Skip to content

Commit 13bb48b

Browse files
authored
Guard OpenAPI path items (#3397)
* Guard OpenAPI path items * Use fixtures for OpenAPI path item guard test * Avoid infer type fixture scan
1 parent 6b9072e commit 13bb48b

5 files changed

Lines changed: 80 additions & 5 deletions

File tree

src/datamodel_code_generator/parser/openapi.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -633,15 +633,19 @@ def _process_path_items( # noqa: PLR0913
633633
for operation_name, raw_operation in methods.items():
634634
if operation_name not in OPERATION_NAMES:
635635
continue
636+
operation = raw_operation
636637
if item_parameters:
638+
operation = operation.copy()
637639
if "parameters" in raw_operation:
638-
raw_operation["parameters"].extend(item_parameters)
640+
operation["parameters"] = [*raw_operation["parameters"], *item_parameters]
639641
else:
640-
raw_operation["parameters"] = item_parameters.copy()
641-
if security is not None and "security" not in raw_operation:
642+
operation["parameters"] = item_parameters.copy()
643+
if security is not None and "security" not in operation:
642644
# fastapi-code-generator depends on inherited global security being materialized here.
643-
raw_operation["security"] = security
644-
self.parse_operation(raw_operation, [*path, operation_name])
645+
if operation is raw_operation:
646+
operation = operation.copy()
647+
operation["security"] = security
648+
self.parse_operation(operation, [*path, operation_name])
645649

646650
def parse_schema(
647651
self,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# generated by datamodel-codegen:
2+
# filename: openapi.yaml
3+
4+
from __future__ import annotations
5+
6+
from pydantic import BaseModel
7+
8+
9+
class Item(BaseModel):
10+
name: str | None = None
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
openapi: 3.1.0
2+
info:
3+
title: Path Item Mutation Guard
4+
version: 1.0.0
5+
security:
6+
- api_key: []
7+
paths:
8+
/cached:
9+
$ref: ./path-item.yml#/paths/~1items
10+
components:
11+
schemas:
12+
Item:
13+
type: object
14+
properties:
15+
name:
16+
type: string
17+
securitySchemes:
18+
api_key:
19+
type: apiKey
20+
in: header
21+
name: X-API-Key
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
paths:
2+
/items:
3+
parameters:
4+
- name: pathLevel
5+
in: query
6+
schema:
7+
type: string
8+
get:
9+
parameters:
10+
- name: operationLevel
11+
in: query
12+
schema:
13+
type: integer
14+
responses:
15+
'200':
16+
description: Success
17+
content:
18+
application/json:
19+
schema:
20+
$ref: ./openapi.yaml#/components/schemas/Item

tests/main/openapi/test_main_openapi.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
generate,
2727
get_version,
2828
inferred_message,
29+
load_data_from_path,
2930
)
3031
from datamodel_code_generator.__main__ import Exit
3132
from datamodel_code_generator.config import GenerateConfig
@@ -49,6 +50,7 @@
4950
MSGSPEC_LEGACY_BLACK_SKIP,
5051
OPEN_API_DATA_PATH,
5152
TIMESTAMP,
53+
run_generate_file_and_assert,
5254
run_main_and_assert,
5355
run_main_url_and_assert,
5456
run_main_with_args,
@@ -4218,6 +4220,24 @@ def test_main_openapi_non_operations_and_security(output_file: Path) -> None:
42184220
)
42194221

42204222

4223+
def test_generate_openapi_keeps_referenced_path_item_original_unchanged(output_file: Path) -> None:
4224+
"""Keep cached referenced OpenAPI path items unchanged while inheriting operation metadata."""
4225+
input_path = OPEN_API_DATA_PATH / "referenced_path_item_mutation_guard" / "openapi.yaml"
4226+
path_item_path = OPEN_API_DATA_PATH / "referenced_path_item_mutation_guard" / "path-item.yml"
4227+
cached_path_item = load_data_from_path(path_item_path.resolve(), "utf-8")
4228+
4229+
run_generate_file_and_assert(
4230+
input_path=input_path,
4231+
output_path=output_file,
4232+
input_file_type=InputFileType.OpenAPI,
4233+
assert_func=assert_file_content,
4234+
expected_file="referenced_path_item_mutation_guard.py",
4235+
disable_timestamp=True,
4236+
openapi_scopes=[OpenAPIScope.Schemas, OpenAPIScope.Paths],
4237+
unchanged_inputs={"cached path-item.yml": cached_path_item},
4238+
)
4239+
4240+
42214241
def test_main_openapi_webhooks_with_parameters(output_file: Path) -> None:
42224242
"""Test OpenAPI generation with webhook-level and operation-level parameters."""
42234243
run_main_and_assert(

0 commit comments

Comments
 (0)