Skip to content

Commit e87bb24

Browse files
committed
fix(InlineModelResolver): do not merge distinct inline enums sharing the same values
The structural-signature fallback added in #23856 strips 'description', 'type' and 'example' before comparing schemas so that multi-file $ref object schemas mutated by the parser still deduplicate. For enum schemas this is too aggressive: two enums that list the same values but mean different things are distinguished only by their description, so stripping it wrongly unifies them and the second usage silently reuses the first enum's type (regression in 7.23.0, worked in 7.22.0). Skip the structural fallback for enum schemas in matchGenerated() so enums deduplicate on exact content only. Object-schema multi-file dedup is unaffected. Fixes #23978
1 parent a179190 commit e87bb24

70 files changed

Lines changed: 1776 additions & 117 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,10 @@ private String resolveModelName(String title, String modelName) {
849849
}
850850
}
851851

852+
private static boolean isEnumSchema(Schema model) {
853+
return model != null && model.getEnum() != null && !model.getEnum().isEmpty();
854+
}
855+
852856
private String matchGenerated(Schema model) {
853857
if (skipSchemaReuse) { // skip reusing schema
854858
return null;
@@ -862,9 +866,18 @@ private String matchGenerated(Schema model) {
862866
}
863867
// Structural match: compare with volatile fields stripped at every level.
864868
// See generatedStructuralSignature field for a full explanation of why this is needed.
865-
String structural = computeStructuralSignature(model);
866-
if (generatedStructuralSignature.containsKey(structural)) {
867-
return generatedStructuralSignature.get(structural);
869+
//
870+
// Skip this fallback for enum schemas: the structural matcher strips 'description',
871+
// but for an enum the description is the only thing distinguishing two schemas that
872+
// share the same values yet represent different things (e.g. two properties whose
873+
// enums happen to list the same values but mean different things). Stripping it would
874+
// wrongly unify them and make the second usage silently reuse the first enum's type
875+
// (#23978). Enum schemas therefore dedup on exact content only.
876+
if (!isEnumSchema(model)) {
877+
String structural = computeStructuralSignature(model);
878+
if (generatedStructuralSignature.containsKey(structural)) {
879+
return generatedStructuralSignature.get(structural);
880+
}
868881
}
869882
} catch (JsonProcessingException e) {
870883
e.printStackTrace();

modules/openapi-generator/src/test/java/org/openapitools/codegen/InlineModelResolverTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,45 @@ public void resolveInlineModelDeduplicatesWhenParserMutatesPropertyTypes() {
13851385
schemas.get("StorageBackend_1"));
13861386
}
13871387

1388+
@Test
1389+
public void doNotMergeDistinctInlineEnumsSharingTheSameValues() {
1390+
// Regression test for #23978: two inline enum properties that share the same enum values
1391+
// but represent different things (distinguished only by their description) must each be
1392+
// promoted to their own schema. The structural-signature fallback in matchGenerated()
1393+
// strips 'description', so without an enum guard it would wrongly unify them and the
1394+
// second property would silently reuse the first enum's type (regression in 7.23.0).
1395+
OpenAPI openapi = new OpenAPI();
1396+
openapi.setComponents(new Components());
1397+
openapi.setPaths(new Paths());
1398+
1399+
StringSchema statusEnum = new StringSchema();
1400+
statusEnum.setDescription("Lifecycle status of the order");
1401+
statusEnum.setEnum(java.util.Arrays.asList("ACTIVE", "INACTIVE"));
1402+
1403+
StringSchema visibilityEnum = new StringSchema();
1404+
visibilityEnum.setDescription("Whether the order is visible to the customer");
1405+
visibilityEnum.setEnum(java.util.Arrays.asList("ACTIVE", "INACTIVE"));
1406+
1407+
openapi.getComponents().addSchemas("Order", new ObjectSchema()
1408+
.title("Order")
1409+
.addProperty("status", statusEnum)
1410+
.addProperty("visibility", visibilityEnum));
1411+
1412+
InlineModelResolver resolver = new InlineModelResolver();
1413+
Map<String, String> options = new HashMap<>();
1414+
options.put("RESOLVE_INLINE_ENUMS", "true");
1415+
resolver.setInlineSchemaOptions(options);
1416+
resolver.flatten(openapi);
1417+
1418+
Schema order = openapi.getComponents().getSchemas().get("Order");
1419+
String statusRef = ((Schema) order.getProperties().get("status")).get$ref();
1420+
String visibilityRef = ((Schema) order.getProperties().get("visibility")).get$ref();
1421+
assertNotNull("status enum must be promoted to its own schema", statusRef);
1422+
assertNotNull("visibility enum must be promoted to its own schema", visibilityRef);
1423+
assertFalse("Distinct inline enums sharing the same values must not be merged (#23978)",
1424+
statusRef.equals(visibilityRef));
1425+
}
1426+
13881427
@Test
13891428
public void deduplicateComponentsRemovesNumberedDuplicateOfTitledSchemaAndRewritesRefs() {
13901429
// Regression test: when flattening creates a numbered duplicate of a titled component

samples/client/petstore/csharp/generichost/net10/FormModels/.openapi-generator/FILES

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ docs/models/TestCollectionEndingWithWordList.md
121121
docs/models/TestCollectionEndingWithWordListObject.md
122122
docs/models/TestDescendants.md
123123
docs/models/TestDescendantsObjectType.md
124+
docs/models/TestEnumParametersEnumHeaderStringParameter.md
124125
docs/models/TestEnumParametersEnumQueryDoubleParameter.md
125126
docs/models/TestEnumParametersEnumQueryIntegerParameter.md
126127
docs/models/TestEnumParametersRequestEnumFormString.md
@@ -140,6 +141,7 @@ docs/models/ZeroBasedEnumClassZeroBasedEnum.md
140141
docs/scripts/git_push.ps1
141142
docs/scripts/git_push.sh
142143
src/Org.OpenAPITools.Test/Api/DependencyInjectionTests.cs
144+
src/Org.OpenAPITools.Test/Model/TestEnumParametersEnumHeaderStringParameterTests.cs
143145
src/Org.OpenAPITools.Test/Org.OpenAPITools.Test.csproj
144146
src/Org.OpenAPITools.Test/README.md
145147
src/Org.OpenAPITools/Api/AnotherFakeApi.cs
@@ -289,6 +291,7 @@ src/Org.OpenAPITools/Model/TestCollectionEndingWithWordList.cs
289291
src/Org.OpenAPITools/Model/TestCollectionEndingWithWordListObject.cs
290292
src/Org.OpenAPITools/Model/TestDescendants.cs
291293
src/Org.OpenAPITools/Model/TestDescendantsObjectType.cs
294+
src/Org.OpenAPITools/Model/TestEnumParametersEnumHeaderStringParameter.cs
292295
src/Org.OpenAPITools/Model/TestEnumParametersEnumQueryDoubleParameter.cs
293296
src/Org.OpenAPITools/Model/TestEnumParametersEnumQueryIntegerParameter.cs
294297
src/Org.OpenAPITools/Model/TestEnumParametersRequestEnumFormString.cs

samples/client/petstore/csharp/generichost/net10/FormModels/api/openapi.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ paths:
772772
name: enum_header_string
773773
required: false
774774
schema:
775-
$ref: "#/components/schemas/testEnumParameters_request_enum_form_string"
775+
$ref: "#/components/schemas/testEnumParameters_enum_header_string_parameter"
776776
style: simple
777777
- description: Query parameter enum test (string array)
778778
explode: true
@@ -790,7 +790,7 @@ paths:
790790
name: enum_query_string
791791
required: false
792792
schema:
793-
$ref: "#/components/schemas/testEnumParameters_request_enum_form_string"
793+
$ref: "#/components/schemas/testEnumParameters_enum_header_string_parameter"
794794
style: form
795795
- description: Query parameter enum test (double)
796796
explode: true
@@ -2819,6 +2819,13 @@ components:
28192819
enum_form_string:
28202820
$ref: "#/components/schemas/testEnumParameters_request_enum_form_string"
28212821
type: object
2822+
testEnumParameters_enum_header_string_parameter:
2823+
default: -efg
2824+
enum:
2825+
- _abc
2826+
- -efg
2827+
- (xyz)
2828+
type: string
28222829
testEnumParameters_enum_query_integer_parameter:
28232830
enum:
28242831
- 1

samples/client/petstore/csharp/generichost/net10/FormModels/docs/apis/FakeApi.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ void (empty response body)
483483

484484
<a id="testenumparameters"></a>
485485
# **TestEnumParameters**
486-
> void TestEnumParameters (TestEnumParametersRequestEnumFormString enumFormString = null, List<TestEnumParametersRequestEnumFormStringArrayInner> enumFormStringArray = null, TestEnumParametersRequestEnumFormString enumHeaderString = null, List<TestEnumParametersRequestEnumFormStringArrayInner> enumHeaderStringArray = null, TestEnumParametersEnumQueryDoubleParameter enumQueryDouble = null, TestEnumParametersEnumQueryIntegerParameter enumQueryInteger = null, TestEnumParametersRequestEnumFormString enumQueryString = null, List<TestEnumParametersRequestEnumFormStringArrayInner> enumQueryStringArray = null)
486+
> void TestEnumParameters (TestEnumParametersRequestEnumFormString enumFormString = null, List<TestEnumParametersRequestEnumFormStringArrayInner> enumFormStringArray = null, TestEnumParametersEnumHeaderStringParameter enumHeaderString = null, List<TestEnumParametersRequestEnumFormStringArrayInner> enumHeaderStringArray = null, TestEnumParametersEnumQueryDoubleParameter enumQueryDouble = null, TestEnumParametersEnumQueryIntegerParameter enumQueryInteger = null, TestEnumParametersEnumHeaderStringParameter enumQueryString = null, List<TestEnumParametersRequestEnumFormStringArrayInner> enumQueryStringArray = null)
487487
488488
To test enum parameters
489489

@@ -496,11 +496,11 @@ To test enum parameters
496496
|------|------|-------------|-------|
497497
| **enumFormString** | **TestEnumParametersRequestEnumFormString** | | [optional] |
498498
| **enumFormStringArray** | [**List&lt;TestEnumParametersRequestEnumFormStringArrayInner&gt;**](TestEnumParametersRequestEnumFormStringArrayInner.md) | Form parameter enum test (string array) | [optional] |
499-
| **enumHeaderString** | **TestEnumParametersRequestEnumFormString** | Header parameter enum test (string) | [optional] |
499+
| **enumHeaderString** | **TestEnumParametersEnumHeaderStringParameter** | Header parameter enum test (string) | [optional] |
500500
| **enumHeaderStringArray** | [**List&lt;TestEnumParametersRequestEnumFormStringArrayInner&gt;**](TestEnumParametersRequestEnumFormStringArrayInner.md) | Header parameter enum test (string array) | [optional] |
501501
| **enumQueryDouble** | **TestEnumParametersEnumQueryDoubleParameter** | Query parameter enum test (double) | [optional] |
502502
| **enumQueryInteger** | **TestEnumParametersEnumQueryIntegerParameter** | Query parameter enum test (double) | [optional] |
503-
| **enumQueryString** | **TestEnumParametersRequestEnumFormString** | Query parameter enum test (string) | [optional] |
503+
| **enumQueryString** | **TestEnumParametersEnumHeaderStringParameter** | Query parameter enum test (string) | [optional] |
504504
| **enumQueryStringArray** | [**List&lt;TestEnumParametersRequestEnumFormStringArrayInner&gt;**](TestEnumParametersRequestEnumFormStringArrayInner.md) | Query parameter enum test (string array) | [optional] |
505505

506506
### Return type
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Org.OpenAPITools.Model.TestEnumParametersEnumHeaderStringParameter
2+
3+
## Properties
4+
5+
Name | Type | Description | Notes
6+
------------ | ------------- | ------------- | -------------
7+
8+
[[Back to Model list]](../../README.md#documentation-for-models) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to README]](../../README.md)
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* OpenAPI Petstore
3+
*
4+
* This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
5+
*
6+
* The version of the OpenAPI document: 1.0.0
7+
* Generated by: https://github.com/openapitools/openapi-generator.git
8+
*/
9+
10+
11+
using Xunit;
12+
13+
using System;
14+
using System.Linq;
15+
using System.IO;
16+
using System.Collections.Generic;
17+
using Org.OpenAPITools.Model;
18+
using Org.OpenAPITools.Client;
19+
using System.Reflection;
20+
21+
namespace Org.OpenAPITools.Test.Model
22+
{
23+
/// <summary>
24+
/// Class for testing TestEnumParametersEnumHeaderStringParameter
25+
/// </summary>
26+
/// <remarks>
27+
/// This file is automatically generated by OpenAPI Generator (https://openapi-generator.tech).
28+
/// Please update the test case below to test the model.
29+
/// </remarks>
30+
public class TestEnumParametersEnumHeaderStringParameterTests : IDisposable
31+
{
32+
// TODO uncomment below to declare an instance variable for TestEnumParametersEnumHeaderStringParameter
33+
//private TestEnumParametersEnumHeaderStringParameter instance;
34+
35+
public TestEnumParametersEnumHeaderStringParameterTests()
36+
{
37+
// TODO uncomment below to create an instance of TestEnumParametersEnumHeaderStringParameter
38+
//instance = new TestEnumParametersEnumHeaderStringParameter();
39+
}
40+
41+
public void Dispose()
42+
{
43+
// Cleanup when everything is done.
44+
}
45+
46+
/// <summary>
47+
/// Test an instance of TestEnumParametersEnumHeaderStringParameter
48+
/// </summary>
49+
[Fact]
50+
public void TestEnumParametersEnumHeaderStringParameterInstanceTest()
51+
{
52+
// TODO uncomment below to test "IsType" TestEnumParametersEnumHeaderStringParameter
53+
//Assert.IsType<TestEnumParametersEnumHeaderStringParameter>(instance);
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)