Skip to content

Commit b3c4c49

Browse files
[Fix] [Regression] Resolve the discriminator type from a 3.1 sibling (#22634)
* Add support for resolving the discriminator type from a 3.1 sibling * Set gradlew to be executable * Generate sample FILES again
1 parent dff00c8 commit b3c4c49

File tree

115 files changed

+8571
-49
lines changed

Some content is hidden

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

115 files changed

+8571
-49
lines changed

.github/workflows/samples-java-client-jdk17.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
- samples/client/petstore/java/webclient-jakarta/**
88
- samples/client/petstore/java/restclient-*/**
99
- samples/client/others/java/webclient-sealedInterface/**
10+
- samples/client/others/java/webclient-sealedInterface_3_1/**
1011
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
1112
- samples/client/others/java/restclient-enum-in-multipart/**
1213
pull_request:
@@ -15,6 +16,7 @@ on:
1516
- samples/client/petstore/java/webclient-jakarta/**
1617
- samples/client/petstore/java/restclient-*/**
1718
- samples/client/others/java/webclient-sealedInterface/**
19+
- samples/client/others/java/webclient-sealedInterface_3_1/**
1820
- samples/client/petstore/java/webclient-useSingleRequestParameter/**
1921
- samples/client/others/java/restclient-enum-in-multipart/**
2022
jobs:
@@ -34,6 +36,7 @@ jobs:
3436
- samples/client/petstore/java/restclient-useSingleRequestParameter
3537
- samples/client/petstore/java/restclient-useSingleRequestParameter-static
3638
- samples/client/others/java/webclient-sealedInterface
39+
- samples/client/others/java/webclient-sealedInterface_3_1
3740
- samples/client/petstore/java/webclient-useSingleRequestParameter
3841
- samples/client/others/java/restclient-enum-in-multipart
3942
steps:
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
generatorName: java
2+
outputDir: samples/client/others/java/webclient-sealedInterface_3_1
3+
library: webclient
4+
inputSpec: modules/openapi-generator/src/test/resources/3_1/oneof_polymorphism_and_inheritance.yaml
5+
templateDir: modules/openapi-generator/src/main/resources/Java
6+
additionalProperties:
7+
artifactId: sealed-interface-webclient
8+
hideGenerationTimestamp: "true"
9+
useOneOfInterfaces: true
10+
useSealedOneOfInterfaces: true

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

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3301,7 +3301,7 @@ private CodegenProperty discriminatorFound(String composedSchemaName, Schema sc,
33013301
}
33023302

33033303
if (refSchema.getProperties() != null && refSchema.getProperties().get(discPropName) != null) {
3304-
Schema discSchema = ModelUtils.getReferencedSchema(openAPI, (Schema) refSchema.getProperties().get(discPropName));
3304+
Schema discSchema = ModelUtils.getReferencedSchema(openAPI, getDiscriminatorSchema(refSchema, discPropName));
33053305
CodegenProperty cp = new CodegenProperty();
33063306
if (ModelUtils.isStringSchema(discSchema)) {
33073307
cp.isString = true;
@@ -3632,14 +3632,7 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch
36323632
// FIXME: there are other ways to define the type of the discriminator property (inline
36333633
// for example). Handling those scenarios is too complicated for me, I'm leaving it for
36343634
// the future..
3635-
String propertyType =
3636-
Optional.ofNullable(schema.getProperties())
3637-
.map(p -> (Schema<?>) p.get(discriminatorPropertyName))
3638-
.map(Schema::get$ref)
3639-
.map(ModelUtils::getSimpleRef)
3640-
.map(this::toModelName)
3641-
.orElseGet(() -> typeMapping.get("string"));
3642-
discriminator.setPropertyType(propertyType);
3635+
discriminator.setPropertyType(getDiscriminatorPropertyType(schema, discriminatorPropertyName));
36433636

36443637
// check to see if the discriminator property is an enum string
36453638
boolean isEnum = Optional
@@ -3703,6 +3696,39 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch
37033696
return discriminator;
37043697
}
37053698

3699+
/**
3700+
* Get the Schema for the discriminator type. Requires special handling due to siblings from OAS 3.1.
3701+
* An example of a sibling is an enum-ref that has its own description. This will lead to the enum being
3702+
* referenced as an allOf that in turn has a ref, rather than a regular ref directly to the enum.
3703+
*
3704+
* @param schema The input OAS schema.
3705+
* @param discriminatorName The name of the discriminator property.
3706+
*/
3707+
protected Schema getDiscriminatorSchema(Schema schema, String discriminatorName) {
3708+
if (schema.getProperties() == null) {
3709+
return null;
3710+
}
3711+
Schema discSchema = (Schema) schema.getProperties().get(discriminatorName);
3712+
if (ModelUtils.isAllOf(discSchema)) {
3713+
discSchema = (Schema) discSchema.getAllOf().get(0);
3714+
}
3715+
return discSchema;
3716+
}
3717+
3718+
/**
3719+
* Get the property type for the discriminator
3720+
*
3721+
* @param schema The input OAS schema.
3722+
* @param discriminatorPropertyName The name of the discriminator property.
3723+
*/
3724+
protected String getDiscriminatorPropertyType(Schema schema, String discriminatorPropertyName) {
3725+
return Optional.ofNullable(getDiscriminatorSchema(schema, discriminatorPropertyName))
3726+
.map(Schema::get$ref)
3727+
.map(ModelUtils::getSimpleRef)
3728+
.map(this::toModelName)
3729+
.orElseGet(() -> typeMapping.get("string"));
3730+
}
3731+
37063732
/**
37073733
* Handle the model for the 'additionalProperties' keyword in the OAS schema.
37083734
*

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,23 @@ public void testDiscriminatorWithCustomMapping() {
11311131
assertTrue(personModel.getHasDiscriminatorWithNonEmptyMapping());
11321132
}
11331133

1134+
@Test
1135+
public void testEnumDiscriminatorWithDescriptionOverridden3_1() {
1136+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_1/oneof_polymorphism_and_inheritance.yaml");
1137+
new OpenAPINormalizer(openAPI, Map.of()).normalize();
1138+
DefaultCodegen codegen = new DefaultCodegen();
1139+
codegen.setUseOneOfInterfaces(true);
1140+
1141+
Schema fruit = openAPI.getComponents().getSchemas().get("Fruit");
1142+
codegen.setOpenAPI(openAPI);
1143+
CodegenModel fruitModel = codegen.fromModel("Fruit", fruit);
1144+
assertTrue(fruitModel.getHasDiscriminatorWithNonEmptyMapping());
1145+
assertTrue(fruitModel.discriminator.getIsEnum());
1146+
assertEquals("FruitType", fruitModel.discriminator.getPropertyType());
1147+
assertEquals("test", fruitModel.getVars().get(0).description);
1148+
assertTrue(fruitModel.getVars().get(0).isEnumRef);
1149+
}
1150+
11341151
@Test
11351152
public void testParentName() {
11361153
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/allOf.yaml");

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4063,4 +4063,13 @@ public void testOneOfInterfaceWithAnnotation() {
40634063
.isInterface()
40644064
.assertTypeAnnotations().containsWithName("SuppressWarnings");
40654065
}
4066+
4067+
@Test
4068+
public void testOneOfInterfaceWithEnumDiscriminatorHavingCustomDescription3_1() {
4069+
final Map<String, File> files = generateFromContract("src/test/resources/3_1/oneof_polymorphism_and_inheritance.yaml", WEBCLIENT,
4070+
Map.of(USE_ONE_OF_INTERFACES, "true"));
4071+
JavaFileAssert.assertThat(files.get("Fruit.java"))
4072+
.isInterface()
4073+
.fileContains("public FruitType getFruitType()");
4074+
}
40664075
}
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
openapi: 3.1.0
2+
info:
3+
title: ByRefOrValue
4+
description: >
5+
This tests for a oneOf interface representation
6+
version: 0.0.1
7+
servers:
8+
- url: "http://localhost:8080"
9+
tags:
10+
- name: Foo
11+
- name: Bar
12+
paths:
13+
/foo:
14+
get:
15+
tags:
16+
- Foo
17+
summary: GET all Foos
18+
operationId: getAllFoos
19+
responses:
20+
'200':
21+
$ref: '#/components/responses/200FooArray'
22+
post:
23+
tags:
24+
- Foo
25+
summary: Create a Foo
26+
operationId: createFoo
27+
requestBody:
28+
$ref: '#/components/requestBodies/Foo'
29+
responses:
30+
'201':
31+
$ref: '#/components/responses/201Foo'
32+
/bar:
33+
post:
34+
tags:
35+
- Bar
36+
summary: Create a Bar
37+
operationId: createBar
38+
requestBody:
39+
required: true
40+
content:
41+
'application/json':
42+
schema:
43+
$ref: '#/components/schemas/Bar_Create'
44+
responses:
45+
200:
46+
description: Bar created
47+
content:
48+
'application/json':
49+
schema:
50+
$ref: '#/components/schemas/Bar'
51+
52+
components:
53+
schemas:
54+
Addressable:
55+
type: object
56+
properties:
57+
href:
58+
type: string
59+
description: Hyperlink reference
60+
id:
61+
type: string
62+
description: unique identifier
63+
description: Base schema for addressable entities
64+
Extensible:
65+
type: object
66+
properties:
67+
"@schemaLocation":
68+
type: string
69+
description: A URI to a JSON-Schema file that defines additional attributes
70+
and relationships
71+
"@baseType":
72+
type: string
73+
description: When sub-classing, this defines the super-class
74+
"@type":
75+
type: string
76+
description: When sub-classing, this defines the sub-class Extensible name
77+
required:
78+
- '@type'
79+
Entity:
80+
type: object
81+
discriminator:
82+
propertyName: '@type'
83+
allOf:
84+
- "$ref": "#/components/schemas/Addressable"
85+
- "$ref": "#/components/schemas/Extensible"
86+
EntityRef:
87+
type: object
88+
discriminator:
89+
propertyName: '@type'
90+
description: Entity reference schema to be use for all entityRef class.
91+
properties:
92+
name:
93+
type: string
94+
description: Name of the related entity.
95+
'@referredType':
96+
type: string
97+
description: The actual type of the target instance when needed for disambiguation.
98+
allOf:
99+
- $ref: '#/components/schemas/Addressable'
100+
- "$ref": "#/components/schemas/Extensible"
101+
FooRefOrValue:
102+
type: object
103+
oneOf:
104+
- $ref: "#/components/schemas/Foo"
105+
- $ref: "#/components/schemas/FooRef"
106+
discriminator:
107+
propertyName: "@type"
108+
Foo:
109+
type: object
110+
properties:
111+
fooPropA:
112+
type: string
113+
fooPropB:
114+
type: string
115+
allOf:
116+
- $ref: '#/components/schemas/Entity'
117+
FooRef:
118+
type: object
119+
properties:
120+
foorefPropA:
121+
type: string
122+
allOf:
123+
- $ref: '#/components/schemas/EntityRef'
124+
BarRef:
125+
type: object
126+
allOf:
127+
- $ref: '#/components/schemas/EntityRef'
128+
Bar_Create:
129+
type: object
130+
properties:
131+
barPropA:
132+
type: string
133+
fooPropB:
134+
type: string
135+
foo:
136+
$ref: '#/components/schemas/FooRefOrValue'
137+
allOf:
138+
- $ref: '#/components/schemas/Entity'
139+
Bar:
140+
type: object
141+
required:
142+
- id
143+
properties:
144+
id:
145+
type: string
146+
barPropA:
147+
type: string
148+
fooPropB:
149+
type: string
150+
foo:
151+
$ref: '#/components/schemas/FooRefOrValue'
152+
allOf:
153+
- $ref: '#/components/schemas/Entity'
154+
BarRefOrValue:
155+
type: object
156+
oneOf:
157+
- $ref: "#/components/schemas/Bar"
158+
- $ref: "#/components/schemas/BarRef"
159+
Pizza:
160+
type: object
161+
properties:
162+
pizzaSize:
163+
type: number
164+
allOf:
165+
- $ref: '#/components/schemas/Entity'
166+
Pasta:
167+
type: object
168+
properties:
169+
vendor:
170+
type: string
171+
allOf:
172+
- $ref: '#/components/schemas/Entity'
173+
PizzaSpeziale:
174+
type: object
175+
properties:
176+
toppings:
177+
type: string
178+
allOf:
179+
- $ref: '#/components/schemas/Pizza'
180+
FruitType:
181+
type: string
182+
enum: [APPLE, BANANA]
183+
Fruit:
184+
type: object
185+
properties:
186+
fruitType:
187+
$ref: "#/components/schemas/FruitType"
188+
description: "test"
189+
required:
190+
- fruitType
191+
oneOf:
192+
- $ref: '#/components/schemas/Apple'
193+
- $ref: '#/components/schemas/Banana'
194+
discriminator:
195+
propertyName: fruitType
196+
mapping:
197+
APPLE: '#/components/schemas/Apple'
198+
BANANA: '#/components/schemas/Banana'
199+
Apple:
200+
type: object
201+
required:
202+
- seeds
203+
properties:
204+
seeds:
205+
type: integer
206+
Banana:
207+
type: object
208+
required:
209+
- length
210+
properties:
211+
length:
212+
type: integer
213+
Animal:
214+
oneOf:
215+
- $ref: '#/components/schemas/Dog'
216+
- $ref: '#/components/schemas/Cat'
217+
Cat:
218+
type: object
219+
properties:
220+
declawed:
221+
type: boolean
222+
Dog:
223+
type: object
224+
properties:
225+
bark:
226+
type: boolean
227+
228+
requestBodies:
229+
Foo:
230+
description: The Foo to be created
231+
content:
232+
application/json;charset=utf-8:
233+
schema:
234+
$ref: '#/components/schemas/Foo'
235+
236+
responses:
237+
'204':
238+
description: Deleted
239+
content: { }
240+
201Foo:
241+
description: Error
242+
content:
243+
application/json:
244+
schema:
245+
$ref: '#/components/schemas/FooRefOrValue'
246+
200FooArray:
247+
description: Success
248+
content:
249+
application/json;charset=utf-8:
250+
schema:
251+
type: array
252+
items:
253+
$ref: '#/components/schemas/FooRefOrValue'

0 commit comments

Comments
 (0)