feat(experimental/openapi): replace $ref in additionalProperties; add tests (#1735)

Co-authored-by: Hsiao, Cooper <cooper.hsiao@commscope.com>
Co-authored-by: Cooper <xcooper1030@gmail.com>
This commit is contained in:
Jeremiah Lowin 2025-09-19 13:35:45 -04:00 committed by GitHub
commit 39dee1bdb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 107 additions and 0 deletions

View file

@ -79,6 +79,7 @@ def _replace_ref_with_defs(
Examples:
- {"type": "object", "properties": {"$ref": "#/components/schemas/..."}}
- {"type": "object", "additionalProperties": {"$ref": "#/components/schemas/..."}, "properties": {...}}
- {"$ref": "#/components/schemas/..."}
- {"items": {"$ref": "#/components/schemas/..."}}
- {"anyOf": [{"$ref": "#/components/schemas/..."}]}
@ -117,6 +118,11 @@ def _replace_ref_with_defs(
for section in ["anyOf", "allOf", "oneOf"]:
for i, item in enumerate(schema.get(section, [])):
schema[section][i] = _replace_ref_with_defs(item)
if additionalProperties := schema.get("additionalProperties"):
if not isinstance(additionalProperties, bool):
schema["additionalProperties"] = _replace_ref_with_defs(
additionalProperties
)
if info.get("description", description) and not schema.get("description"):
schema["description"] = description
return schema

View file

@ -286,6 +286,107 @@ class TestSchemaProcessing:
]
assert array_item_prop["$ref"] == "#/$defs/RefProp"
def test_replace_ref_with_defs_in_additional_properties(self):
"""Test replacing $ref deeply in 'additionalProperties'."""
add_props_schema = {
"description": "An invoice with a fixed header and a flexible set of line items.",
"type": "object",
"properties": {
"invoice_number": {
"type": "string",
"description": "The unique identifier for the invoice.",
},
"customer_name": {
"type": "string",
"description": "The name of the customer.",
},
"total_amount": {
"type": "number",
"description": "The total amount of the invoice.",
},
},
"required": ["invoice_number", "customer_name", "total_amount"],
"additionalProperties": {"$ref": "#/components/schemas/Link"},
}
# Use our recursive replacement approach
result = _replace_ref_with_defs(add_props_schema)
# Check additional properties
add_props = result["additionalProperties"]
assert add_props["$ref"] == "#/$defs/Link"
def test_replace_ref_with_defs_with_bool_additional_properties(self):
"""Test replacing a bool 'additionalProperties'."""
add_props_schema = {
"description": "An invoice with a fixed header and a flexible set of line items.",
"type": "object",
"properties": {
"invoice_number": {
"type": "string",
"description": "The unique identifier for the invoice.",
},
"customer_name": {
"type": "string",
"description": "The name of the customer.",
},
"total_amount": {
"type": "number",
"description": "The total amount of the invoice.",
},
},
"required": ["invoice_number", "customer_name", "total_amount"],
"additionalProperties": False,
}
# Use our recursive replacement approach
result = _replace_ref_with_defs(add_props_schema)
# Check additional properties
add_props = result["additionalProperties"]
assert add_props is False
def test_replace_ref_with_defs_with_inner_schema_additional_properties(self):
"""Test replacing a inner schema 'additionalProperties'."""
add_props_schema = {
"description": "An invoice with a fixed header and a flexible set of line items.",
"type": "object",
"properties": {
"invoice_number": {
"type": "string",
"description": "The unique identifier for the invoice.",
},
"customer_name": {
"type": "string",
"description": "The name of the customer.",
},
"total_amount": {
"type": "number",
"description": "The total amount of the invoice.",
},
},
"required": ["invoice_number", "customer_name", "total_amount"],
"additionalProperties": {
"type": "integer",
"format": "int32",
"description": "The total amount of the invoice.",
},
}
# Use our recursive replacement approach
result = _replace_ref_with_defs(add_props_schema)
# Check additional properties
add_props = result["additionalProperties"]
assert add_props == {
"type": "integer",
"format": "int32",
"description": "The total amount of the invoice.",
}
def test_parameter_collision_suffixing_logic(self):
"""Test the specific logic for parameter collision suffixing."""
# Create a route that would definitely cause collisions