Skip to content

Commit e1320f5

Browse files
fix(InlineModelResolver): do not merge untitled inline schemas differing only by description (#24192)
* fix(InlineModelResolver): do not merge untitled inline schemas differing only by description Co-authored-by: seonwooj0810 <seonwooj0810@gmail.com> * update c# samples * update python-aiohttp tests * update python tests * update tests * update C# samples * uptest * update tests * update tests * update C# samples --------- Co-authored-by: seonwooj0810 <seonwooj0810@gmail.com>
1 parent 77c2e46 commit e1320f5

128 files changed

Lines changed: 2242 additions & 193 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: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -862,9 +862,20 @@ private String matchGenerated(Schema model) {
862862
}
863863
// Structural match: compare with volatile fields stripped at every level.
864864
// 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);
865+
//
866+
// Only applied to *titled* schemas. A title denotes a named type that should be reused
867+
// wherever it appears, so parser-induced volatile differences (description, type,
868+
// example) must not split it into numbered duplicates. Anonymous/untitled inline
869+
// schemas, however, may be intentionally distinct even when structurally identical once
870+
// those volatile fields are stripped (e.g. two response properties that differ only by
871+
// description) — unifying them silently changes the generated type of one property and
872+
// breaks user code. This mirrors the titled-only guards in flatten() pre-population and
873+
// deduplicateComponents().
874+
if (model.getTitle() != null) {
875+
String structural = computeStructuralSignature(model);
876+
if (generatedStructuralSignature.containsKey(structural)) {
877+
return generatedStructuralSignature.get(structural);
878+
}
868879
}
869880
} catch (JsonProcessingException e) {
870881
e.printStackTrace();
@@ -876,7 +887,11 @@ private String matchGenerated(Schema model) {
876887
private void addGenerated(String name, Schema model) {
877888
try {
878889
generatedSignature.put(structureMapper.writeValueAsString(model), name);
879-
generatedStructuralSignature.putIfAbsent(computeStructuralSignature(model), name);
890+
// Only register the volatile-stripped structural signature for titled schemas; untitled
891+
// inline schemas must not participate in the structural-match fallback (see matchGenerated).
892+
if (model.getTitle() != null) {
893+
generatedStructuralSignature.putIfAbsent(computeStructuralSignature(model), name);
894+
}
880895
} catch (JsonProcessingException e) {
881896
e.printStackTrace();
882897
}

samples/client/others/crystal-qdrant/.openapi-generator/FILES

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ src/qdrant-api/models/facet_value.cr
122122
src/qdrant-api/models/facet_value_hit.cr
123123
src/qdrant-api/models/field_condition.cr
124124
src/qdrant-api/models/filter.cr
125+
src/qdrant-api/models/filter_must.cr
126+
src/qdrant-api/models/filter_must_not.cr
125127
src/qdrant-api/models/filter_selector.cr
126128
src/qdrant-api/models/filter_should.cr
127129
src/qdrant-api/models/float_index_params.cr
@@ -215,6 +217,7 @@ src/qdrant-api/models/points_batch.cr
215217
src/qdrant-api/models/points_list.cr
216218
src/qdrant-api/models/points_selector.cr
217219
src/qdrant-api/models/prefetch.cr
220+
src/qdrant-api/models/prefetch_prefetch.cr
218221
src/qdrant-api/models/product_quantization.cr
219222
src/qdrant-api/models/product_quantization_config.cr
220223
src/qdrant-api/models/quantization_config.cr
@@ -312,6 +315,7 @@ src/qdrant-api/models/text_index_type.cr
312315
src/qdrant-api/models/tokenizer_type.cr
313316
src/qdrant-api/models/tracker_status.cr
314317
src/qdrant-api/models/tracker_status_one_of.cr
318+
src/qdrant-api/models/tracker_status_one_of1.cr
315319
src/qdrant-api/models/tracker_telemetry.cr
316320
src/qdrant-api/models/update_collection.cr
317321
src/qdrant-api/models/update_operation.cr
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# #Qdrant API
2+
#
3+
#The version of the OpenAPI document: master
4+
#Contact: andrey@vasnetsov.com
5+
#Generated by: https://openapi-generator.tech
6+
#Generator version: 7.24.0-SNAPSHOT
7+
#
8+
9+
require "../spec_helper"
10+
11+
# Unit tests for Qdrant::Api::FilterMustNot
12+
# Automatically generated by openapi-generator (https://openapi-generator.tech)
13+
# Please update as you see appropriate
14+
Spectator.describe Qdrant::Api::FilterMustNot do
15+
describe "union (anyOf)" do
16+
it "is (de)serialisable as a union alias" do
17+
expect(Qdrant::Api::FilterMustNot.responds_to?(:from_json)).to be_true
18+
end
19+
end
20+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# #Qdrant API
2+
#
3+
#The version of the OpenAPI document: master
4+
#Contact: andrey@vasnetsov.com
5+
#Generated by: https://openapi-generator.tech
6+
#Generator version: 7.24.0-SNAPSHOT
7+
#
8+
9+
require "../spec_helper"
10+
11+
# Unit tests for Qdrant::Api::FilterMust
12+
# Automatically generated by openapi-generator (https://openapi-generator.tech)
13+
# Please update as you see appropriate
14+
Spectator.describe Qdrant::Api::FilterMust do
15+
describe "union (anyOf)" do
16+
it "is (de)serialisable as a union alias" do
17+
expect(Qdrant::Api::FilterMust.responds_to?(:from_json)).to be_true
18+
end
19+
end
20+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# #Qdrant API
2+
#
3+
#The version of the OpenAPI document: master
4+
#Contact: andrey@vasnetsov.com
5+
#Generated by: https://openapi-generator.tech
6+
#Generator version: 7.24.0-SNAPSHOT
7+
#
8+
9+
require "../spec_helper"
10+
11+
# Unit tests for Qdrant::Api::PrefetchPrefetch
12+
# Automatically generated by openapi-generator (https://openapi-generator.tech)
13+
# Please update as you see appropriate
14+
Spectator.describe Qdrant::Api::PrefetchPrefetch do
15+
describe "union (anyOf)" do
16+
it "is (de)serialisable as a union alias" do
17+
expect(Qdrant::Api::PrefetchPrefetch.responds_to?(:from_json)).to be_true
18+
end
19+
end
20+
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# #Qdrant API
2+
#
3+
#The version of the OpenAPI document: master
4+
#Contact: andrey@vasnetsov.com
5+
#Generated by: https://openapi-generator.tech
6+
#Generator version: 7.24.0-SNAPSHOT
7+
#
8+
9+
require "../spec_helper"
10+
11+
# Unit tests for Qdrant::Api::TrackerStatusOneOf1
12+
# Automatically generated by openapi-generator (https://openapi-generator.tech)
13+
# Please update as you see appropriate
14+
Spectator.describe Qdrant::Api::TrackerStatusOneOf1 do
15+
describe "required-field enforcement" do
16+
# A required, non-nilable property without a default makes JSON::Serializable
17+
# reject a document that omits it. (Assumes at least one required field has no
18+
# default; models where every required field has a default are not present in
19+
# the generated samples.)
20+
it "raises when required properties are missing" do
21+
expect { Qdrant::Api::TrackerStatusOneOf1.from_json("{}") }.to raise_error(JSON::SerializableError)
22+
end
23+
end
24+
end

samples/client/others/crystal-qdrant/src/qdrant-api.cr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ require "./qdrant-api/models/facet_value"
119119
require "./qdrant-api/models/facet_value_hit"
120120
require "./qdrant-api/models/field_condition"
121121
require "./qdrant-api/models/filter"
122+
require "./qdrant-api/models/filter_must"
123+
require "./qdrant-api/models/filter_must_not"
122124
require "./qdrant-api/models/filter_selector"
123125
require "./qdrant-api/models/filter_should"
124126
require "./qdrant-api/models/float_index_params"
@@ -212,6 +214,7 @@ require "./qdrant-api/models/points_batch"
212214
require "./qdrant-api/models/points_list"
213215
require "./qdrant-api/models/points_selector"
214216
require "./qdrant-api/models/prefetch"
217+
require "./qdrant-api/models/prefetch_prefetch"
215218
require "./qdrant-api/models/product_quantization"
216219
require "./qdrant-api/models/product_quantization_config"
217220
require "./qdrant-api/models/quantization_config"
@@ -309,6 +312,7 @@ require "./qdrant-api/models/text_index_type"
309312
require "./qdrant-api/models/tokenizer_type"
310313
require "./qdrant-api/models/tracker_status"
311314
require "./qdrant-api/models/tracker_status_one_of"
315+
require "./qdrant-api/models/tracker_status_one_of1"
312316
require "./qdrant-api/models/tracker_telemetry"
313317
require "./qdrant-api/models/update_collection"
314318
require "./qdrant-api/models/update_operation"

samples/client/others/crystal-qdrant/src/qdrant-api/models/filter.cr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ module Qdrant::Api
2222
property min_should : MinShould?
2323

2424
@[JSON::Field(key: "must", emit_null: false)]
25-
property must : FilterShould?
25+
property must : FilterMust?
2626

2727
@[JSON::Field(key: "must_not", emit_null: false)]
28-
property must_not : FilterShould?
28+
property must_not : FilterMustNot?
2929

3030

3131
# Initializes the object
3232
# @param [Hash] attributes Model attributes in the form of hash
33-
def initialize(@should : FilterShould? = nil, @min_should : MinShould? = nil, @must : FilterShould? = nil, @must_not : FilterShould? = nil)
33+
def initialize(@should : FilterShould? = nil, @min_should : MinShould? = nil, @must : FilterMust? = nil, @must_not : FilterMustNot? = nil)
3434
end
3535

3636
# Show invalid properties with the reasons. Usually used together with valid?
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# #Qdrant API
2+
#
3+
#The version of the OpenAPI document: master
4+
#Contact: andrey@vasnetsov.com
5+
#Generated by: https://openapi-generator.tech
6+
#Generator version: 7.24.0-SNAPSHOT
7+
#
8+
9+
module Qdrant::Api
10+
# All conditions must match
11+
# FilterMust (OpenAPI anyOf): a value matching at least one of the listed schemas.
12+
# Implemented as a thin wrapper that (de)serialises by trying each member in order
13+
# (the first that parses wins), so it transparently handles scalar, array and object members.
14+
class FilterMust
15+
getter value
16+
17+
def initialize(@value : Array(Condition))
18+
end
19+
def initialize(@value : Condition)
20+
end
21+
22+
# List of classes defined in anyOf (OpenAPI v3)
23+
def self.openapi_any_of
24+
[
25+
Array(Condition), Condition
26+
]
27+
end
28+
29+
def self.from_json(string_or_io) : self
30+
from_json_any(JSON.parse(string_or_io))
31+
end
32+
33+
def self.new(pull : JSON::PullParser)
34+
from_json_any(JSON::Any.new(pull))
35+
end
36+
37+
def self.from_json_any(data : JSON::Any) : self
38+
json = data.to_json
39+
begin
40+
return new(Array(Condition).from_json(json))
41+
rescue JSON::ParseException | ArgumentError | TypeCastError
42+
end
43+
begin
44+
return new(Condition.from_json(json))
45+
rescue JSON::ParseException | ArgumentError | TypeCastError
46+
end
47+
raise JSON::ParseException.new("`#{json}` doesn't match any schema listed in FilterMust (anyOf)", 0, 0)
48+
end
49+
50+
def self.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node)
51+
begin
52+
return new(Array(Condition).new(ctx, node))
53+
rescue YAML::ParseException | ArgumentError | TypeCastError
54+
end
55+
begin
56+
return new(Condition.new(ctx, node))
57+
rescue YAML::ParseException | ArgumentError | TypeCastError
58+
end
59+
raise YAML::ParseException.new("doesn't match any schema listed in FilterMust (anyOf)", 0, 0)
60+
end
61+
62+
def to_json(builder : JSON::Builder)
63+
@value.to_json(builder)
64+
end
65+
66+
def to_yaml(builder : YAML::Nodes::Builder)
67+
@value.to_yaml(builder)
68+
end
69+
end
70+
71+
end
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# #Qdrant API
2+
#
3+
#The version of the OpenAPI document: master
4+
#Contact: andrey@vasnetsov.com
5+
#Generated by: https://openapi-generator.tech
6+
#Generator version: 7.24.0-SNAPSHOT
7+
#
8+
9+
module Qdrant::Api
10+
# All conditions must NOT match
11+
# FilterMustNot (OpenAPI anyOf): a value matching at least one of the listed schemas.
12+
# Implemented as a thin wrapper that (de)serialises by trying each member in order
13+
# (the first that parses wins), so it transparently handles scalar, array and object members.
14+
class FilterMustNot
15+
getter value
16+
17+
def initialize(@value : Array(Condition))
18+
end
19+
def initialize(@value : Condition)
20+
end
21+
22+
# List of classes defined in anyOf (OpenAPI v3)
23+
def self.openapi_any_of
24+
[
25+
Array(Condition), Condition
26+
]
27+
end
28+
29+
def self.from_json(string_or_io) : self
30+
from_json_any(JSON.parse(string_or_io))
31+
end
32+
33+
def self.new(pull : JSON::PullParser)
34+
from_json_any(JSON::Any.new(pull))
35+
end
36+
37+
def self.from_json_any(data : JSON::Any) : self
38+
json = data.to_json
39+
begin
40+
return new(Array(Condition).from_json(json))
41+
rescue JSON::ParseException | ArgumentError | TypeCastError
42+
end
43+
begin
44+
return new(Condition.from_json(json))
45+
rescue JSON::ParseException | ArgumentError | TypeCastError
46+
end
47+
raise JSON::ParseException.new("`#{json}` doesn't match any schema listed in FilterMustNot (anyOf)", 0, 0)
48+
end
49+
50+
def self.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node)
51+
begin
52+
return new(Array(Condition).new(ctx, node))
53+
rescue YAML::ParseException | ArgumentError | TypeCastError
54+
end
55+
begin
56+
return new(Condition.new(ctx, node))
57+
rescue YAML::ParseException | ArgumentError | TypeCastError
58+
end
59+
raise YAML::ParseException.new("doesn't match any schema listed in FilterMustNot (anyOf)", 0, 0)
60+
end
61+
62+
def to_json(builder : JSON::Builder)
63+
@value.to_json(builder)
64+
end
65+
66+
def to_yaml(builder : YAML::Nodes::Builder)
67+
@value.to_yaml(builder)
68+
end
69+
end
70+
71+
end

0 commit comments

Comments
 (0)