Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -695,9 +695,21 @@ protected boolean isSelfReference(String name, Schema subSchema) {
* @return Schema
*/
public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {
return normalizeSchema(schema, visitedSchemas, false);
}

/**
* Normalizes a schema with optional composition context
*
* @param schema Schema
* @param visitedSchemas a set of visited schemas
* @param isInComposition true if schema is inside oneOf/anyOf composition
* @return Schema
*/
protected Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas, boolean isInComposition) {
// normalize reference schema
if (StringUtils.isNotEmpty(schema.get$ref())) {
normalizeReferenceSchema(schema);
normalizeReferenceSchema(schema, isInComposition);
}

if (skipNormalization(schema, visitedSchemas)) {
Expand Down Expand Up @@ -770,9 +782,15 @@ public Schema normalizeSchema(Schema schema, Set<Schema> visitedSchemas) {
/**
* Normalize reference schema with allOf to support sibling properties
*
* @param schema Schema
*
* @param schema Schema
* @param isInComposition true if schema is inside oneOf/anyOf
*/
protected void normalizeReferenceSchema(Schema schema) {
protected void normalizeReferenceSchema(Schema schema, boolean isInComposition) {
if (isInComposition) {
return;
}

if (schema.getTitle() != null || schema.getDescription() != null
|| schema.getNullable() != null || schema.getDefault() != null || schema.getDeprecated() != null
|| schema.getMaximum() != null || schema.getMinimum() != null
Expand Down Expand Up @@ -860,7 +878,7 @@ protected void normalizeProperties(Map<String, Schema> properties, Set<Schema> v
}
for (Map.Entry<String, Schema> propertiesEntry : properties.entrySet()) {
Schema property = propertiesEntry.getValue();

// remove x-internal if needed (same logic as normalizeComponentsSchemas)
if (property.getExtensions() != null && getRule(REMOVE_X_INTERNAL)) {
Object xInternalValue = property.getExtensions().get(X_INTERNAL);
Expand Down Expand Up @@ -1019,8 +1037,8 @@ protected Schema normalizeOneOf(Schema schema, Set<Schema> visitedSchemas) {
throw new RuntimeException("Error! oneOf schema is not of the type Schema: " + item);
}

// update sub-schema with the updated schema
schema.getOneOf().set(i, normalizeSchema((Schema) item, visitedSchemas));
// update sub-schema with the updated schema, passing isInComposition=true
schema.getOneOf().set(i, normalizeSchema((Schema) item, visitedSchemas, true));
}
} else {
// normalize it as it's no longer an oneOf
Expand Down Expand Up @@ -1049,8 +1067,8 @@ protected Schema normalizeAnyOf(Schema schema, Set<Schema> visitedSchemas) {
throw new RuntimeException("Error! anyOf schema is not of the type Schema: " + item);
}

// update sub-schema with the updated schema
schema.getAnyOf().set(i, normalizeSchema((Schema) item, visitedSchemas));
// update sub-schema with the updated schema, passing isInComposition=true
schema.getAnyOf().set(i, normalizeSchema((Schema) item, visitedSchemas, true));
}

// process rules here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1266,36 +1266,90 @@ public void testRemoveXInternalFromInlineProperties() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/inline_x_internal_test.yaml");
Schema parentSchema = openAPI.getComponents().getSchemas().get("ParentSchema");
Schema inlineProperty = (Schema) parentSchema.getProperties().get("inlineXInternalProperty");

// Before normalization: x-internal should be present on inline property
assertNotNull(inlineProperty.getExtensions());
assertEquals(inlineProperty.getExtensions().get("x-internal"), true);

// Run normalizer with REMOVE_X_INTERNAL=true
Map<String, String> options = new HashMap<>();
options.put("REMOVE_X_INTERNAL", "true");
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
openAPINormalizer.normalize();

// After normalization: x-internal should be removed from inline property
Schema parentSchemaAfter = openAPI.getComponents().getSchemas().get("ParentSchema");
Schema inlinePropertyAfter = (Schema) parentSchemaAfter.getProperties().get("inlineXInternalProperty");

// x-internal extension should be removed (null or not present in map)
if (inlinePropertyAfter.getExtensions() != null) {
assertNull(inlinePropertyAfter.getExtensions().get("x-internal"));
}

// The property itself should still exist (we're removing the flag, not the property)
assertNotNull(inlinePropertyAfter);
assertEquals(inlinePropertyAfter.getType(), "object");

// Nested properties should still exist
assertNotNull(inlinePropertyAfter.getProperties());
assertNotNull(inlinePropertyAfter.getProperties().get("nestedField"));
assertNotNull(inlinePropertyAfter.getProperties().get("nestedNumber"));
}

/**
* Test oneOf items with only title (discriminator label) are NOT wrapped in allOf
*/
@Test
public void testOneOfWithOnlyTitleDoesNotNormalize() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/oneOf.yaml");

Schema fruitSchema = openAPI.getComponents().getSchemas().get("fruit");
assertNotNull(fruitSchema.getOneOf());
assertEquals(fruitSchema.getOneOf().size(), 3);

Schema item1Before = (Schema) fruitSchema.getOneOf().get(0);
assertEquals(item1Before.get$ref(), "#/components/schemas/apple");
assertNull(item1Before.getAllOf());

OpenAPINormalizer normalizer = new OpenAPINormalizer(openAPI, new HashMap<>());
normalizer.normalize();

Schema normalizedFruit = openAPI.getComponents().getSchemas().get("fruit");
assertNotNull(normalizedFruit.getOneOf());
assertEquals(normalizedFruit.getOneOf().size(), 3);

Schema normalizedItem1 = (Schema) normalizedFruit.getOneOf().get(0);
assertEquals(normalizedItem1.get$ref(), "#/components/schemas/apple");
assertNull(normalizedItem1.getAllOf());
}

/**
* Test anyOf items with only title (discriminator label) are NOT wrapped in allOf
*/
@Test
public void testAnyOfWithOnlyTitleDoesNotNormalize() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/anyOf.yaml");

Schema fruitSchema = openAPI.getComponents().getSchemas().get("fruit");
assertNotNull(fruitSchema.getAnyOf());
assertEquals(fruitSchema.getAnyOf().size(), 2);

Schema item1Before = (Schema) fruitSchema.getAnyOf().get(0);
assertEquals(item1Before.get$ref(), "#/components/schemas/apple");
assertNull(item1Before.getAllOf());

OpenAPINormalizer normalizer = new OpenAPINormalizer(openAPI, new HashMap<>());
normalizer.normalize();

Schema normalizedFruit = openAPI.getComponents().getSchemas().get("fruit");
assertNotNull(normalizedFruit.getAnyOf());
assertEquals(normalizedFruit.getAnyOf().size(), 2);

Schema normalizedItem1 = (Schema) normalizedFruit.getAnyOf().get(0);
assertEquals(normalizedItem1.get$ref(), "#/components/schemas/apple");
assertNull(normalizedItem1.getAllOf());
}

public static class RemoveRequiredNormalizer extends OpenAPINormalizer {

public RemoveRequiredNormalizer(OpenAPI openAPI, Map<String, String> inputRules) {
Expand Down
34 changes: 34 additions & 0 deletions modules/openapi-generator/src/test/resources/3_1/anyOf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
openapi: 3.1.0
info:
title: fruity
version: 0.0.1
paths:
/:
get:
responses:
'200':
description: desc
content:
application/json:
schema:
$ref: '#/components/schemas/fruit'
components:
schemas:
fruit:
title: fruit
type: object
anyOf:
- title: apple
$ref: '#/components/schemas/apple'
- title: banana
$ref: '#/components/schemas/banana'
apple:
type: object
properties:
kind:
type: string
banana:
type: object
properties:
count:
type: number
42 changes: 42 additions & 0 deletions modules/openapi-generator/src/test/resources/3_1/oneOf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
openapi: 3.1.0
info:
title: fruity
version: 0.0.1
paths:
/:
get:
responses:
'200':
description: desc
content:
application/json:
schema:
$ref: '#/components/schemas/fruit'
components:
schemas:
fruit:
oneOf:
- title: appleChoice
$ref: '#/components/schemas/apple'
- title: bananaChoice
$ref: '#/components/schemas/banana'
- title: orangeChoice
$ref: '#/components/schemas/orange'
apple:
title: apple
type: object
properties:
kind:
type: string
banana:
title: banana
type: object
properties:
count:
type: number
orange:
title: orange
type: object
properties:
sweet:
type: boolean
Loading