Skip to content

Commit 204faad

Browse files
committed
Fixed incorrect contructors with discriminator and readonly in generator.
Fixed #1206 Fixed #1208
1 parent 79b0dc2 commit 204faad

File tree

6 files changed

+320
-29
lines changed

6 files changed

+320
-29
lines changed

openapi-generator/src/main/java/io/micronaut/openapi/generator/AbstractMicronautJavaCodegen.java

+80-28
Original file line numberDiff line numberDiff line change
@@ -839,38 +839,21 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
839839

840840
var hasParent = model.getParentModel() != null;
841841
var requiredVarsWithoutDiscriminator = new ArrayList<CodegenProperty>();
842-
for (var v : model.requiredVars) {
843-
boolean isDiscriminator = false;
844-
if (hasParent) {
845-
for (var pv : model.getParentModel().getAllVars()) {
846-
if (pv.required && pv.getName().equals(v.getName())) {
847-
isDiscriminator = pv.isDiscriminator;
848-
break;
849-
}
850-
}
851-
} else {
852-
isDiscriminator = v.isDiscriminator;
853-
}
854-
if (!isDiscriminator) {
855-
requiredVarsWithoutDiscriminator.add(v);
856-
}
857-
}
842+
var requiredParentVarsWithoutDiscriminator = new ArrayList<CodegenProperty>();
858843

859-
if (hasParent) {
860-
var parentRequiredVarsWithoutDiscriminator = new ArrayList<CodegenProperty>();
861-
for (var v : model.getParentModel().vars) {
862-
if (v.required && !v.isDiscriminator) {
863-
parentRequiredVarsWithoutDiscriminator.add(v);
864-
}
865-
}
866-
model.vendorExtensions.put("requiredParentVarsWithoutDiscriminator", parentRequiredVarsWithoutDiscriminator);
867-
model.parentVars = model.getParentModel().allVars;
868-
}
844+
processParentModel(model, requiredVarsWithoutDiscriminator, requiredParentVarsWithoutDiscriminator);
869845

870-
List<CodegenProperty> requiredVars = model.vars.stream().filter(v -> v.required).collect(Collectors.toList());
846+
List<CodegenProperty> requiredVars = model.vars.stream()
847+
.filter(v -> v.required)
848+
.toList();
871849

872850
model.vendorExtensions.put("withMultipleVars", model.vars.size() > 1);
873-
model.vendorExtensions.put("requiredVarsWithoutDiscriminator", requiredVarsWithoutDiscriminator);
851+
if (!requiredParentVarsWithoutDiscriminator.isEmpty()) {
852+
model.vendorExtensions.put("requiredParentVarsWithoutDiscriminator", requiredParentVarsWithoutDiscriminator);
853+
}
854+
if (!requiredVarsWithoutDiscriminator.isEmpty()) {
855+
model.vendorExtensions.put("requiredVarsWithoutDiscriminator", requiredVarsWithoutDiscriminator);
856+
}
874857
model.vendorExtensions.put("requiredVars", requiredVars);
875858
model.vendorExtensions.put("areRequiredVarsAndReadOnlyVars", !requiredVarsWithoutDiscriminator.isEmpty() && !model.readOnlyVars.isEmpty());
876859
model.vendorExtensions.put("serialId", random.nextLong());
@@ -894,6 +877,75 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
894877
return objs;
895878
}
896879

880+
private void processParentModel(CodegenModel model, List<CodegenProperty> requiredVarsWithoutDiscriminator, List<CodegenProperty> requiredParentVarsWithoutDiscriminator) {
881+
var parent = model.getParentModel();
882+
var hasParent = parent != null;
883+
884+
for (var v : model.requiredVars) {
885+
boolean isDiscriminator = isDiscriminator(v, model);
886+
if (!isDiscriminator(v, model) && !containsProp(v, requiredVarsWithoutDiscriminator)) {
887+
requiredVarsWithoutDiscriminator.add(v);
888+
}
889+
}
890+
891+
requiredParentVarsWithoutDiscriminator(model, requiredParentVarsWithoutDiscriminator);
892+
if (hasParent) {
893+
model.parentVars = parent.allVars;
894+
}
895+
if (hasParent) {
896+
processParentModel(parent, requiredVarsWithoutDiscriminator, requiredParentVarsWithoutDiscriminator);
897+
}
898+
}
899+
900+
private void requiredParentVarsWithoutDiscriminator(CodegenModel model, List<CodegenProperty> requiredParentVarsWithoutDiscriminator) {
901+
902+
var parent = model.parentModel;
903+
if (parent == null) {
904+
return;
905+
}
906+
907+
for (var v : parent.vars) {
908+
boolean isDiscriminator = isDiscriminator(v, model);
909+
if (v.required && !isDiscriminator) {
910+
v.vendorExtensions.put("isServerOrNotReadOnly", !v.isReadOnly || isServer());
911+
if (!containsProp(v, requiredParentVarsWithoutDiscriminator)) {
912+
requiredParentVarsWithoutDiscriminator.add(v);
913+
}
914+
}
915+
}
916+
}
917+
918+
private boolean containsProp(CodegenProperty prop, List<CodegenProperty> props) {
919+
for (var p : props) {
920+
if (prop.name.equals(p.name)) {
921+
return true;
922+
}
923+
}
924+
return false;
925+
}
926+
927+
private boolean isDiscriminator(CodegenProperty prop, CodegenModel model) {
928+
var isDiscriminator = prop.isDiscriminator;
929+
if (isDiscriminator) {
930+
return true;
931+
}
932+
if (model.parentModel == null) {
933+
return false;
934+
}
935+
CodegenProperty parentProp = null;
936+
for (var pv : model.parentModel.allVars) {
937+
if (pv.required && pv.name.equals(prop.name)) {
938+
isDiscriminator = pv.isDiscriminator;
939+
parentProp = pv;
940+
break;
941+
}
942+
}
943+
if (isDiscriminator) {
944+
return true;
945+
}
946+
return parentProp != null && isDiscriminator(parentProp, model.parentModel);
947+
}
948+
897949
@Override
898950
public void setParameterExampleValue(CodegenParameter p) {
899951
p.vendorExtensions.put("groovyExample", getParameterExampleValue(p, true));

openapi-generator/src/main/resources/templates/java-micronaut/common/model/pojo.mustache

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}{{#vendorE
134134
{{#vendorExtensions.withRequiredVars}}
135135
public {{classname}}({{#vendorExtensions.requiredVarsWithoutDiscriminator}}{{#isReadOnly}}{{#vendorExtensions.isServer}}{{^-first}}, {{/-first}}{{{datatypeWithEnum}}} {{name}}{{/vendorExtensions.isServer}}{{/isReadOnly}}{{^isReadOnly}}{{^-first}}, {{/-first}}{{{datatypeWithEnum}}} {{name}}{{/isReadOnly}}{{/vendorExtensions.requiredVarsWithoutDiscriminator}}) {
136136
{{#parent}}
137-
super({{#vendorExtensions.requiredParentVarsWithoutDiscriminator}}{{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.requiredParentVarsWithoutDiscriminator}});
137+
super({{#vendorExtensions.requiredParentVarsWithoutDiscriminator}}{{#vendorExtensions.isServerOrNotReadOnly}}{{^-first}}, {{/-first}}{{name}}{{/vendorExtensions.isServerOrNotReadOnly}}{{/vendorExtensions.requiredParentVarsWithoutDiscriminator}});
138138
{{/parent}}
139139
{{#vendorExtensions.requiredVars}}
140140
{{^isDiscriminator}}

openapi-generator/src/test/java/io/micronaut/openapi/generator/MicronautClientCodegenTest.java

+23
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,27 @@ void testConfigurePathSeparator() {
268268
// Micronaut declarative http client should use the provided path separator
269269
assertFileContains(outputPath + "/src/main/java/org/openapitools/api/PetApi.java", "@Client(\"${openapi-micronaut-client.base-path}\")");
270270
}
271+
272+
@Test
273+
void testReadOnlyConstructorBug() {
274+
275+
var codegen = new JavaMicronautClientCodegen();
276+
String outputPath = generateFiles(codegen, "src/test/resources/3_0/readonlyconstructorbug.yml", CodegenConstants.MODELS);
277+
String apiPath = outputPath + "src/main/java/org/openapitools/model/";
278+
279+
assertFileContains(apiPath + "BookInfo.java", "public BookInfo(String name)");
280+
assertFileContains(apiPath + "ExtendedBookInfo.java", "public ExtendedBookInfo(String isbn, String name)", "super(name)");
281+
}
282+
283+
@Test
284+
void testDiscriminatorConstructorBug() {
285+
286+
var codegen = new JavaMicronautClientCodegen();
287+
String outputPath = generateFiles(codegen, "src/test/resources/3_0/discriminatorconstructorbug.yml", CodegenConstants.MODELS);
288+
String apiPath = outputPath + "src/main/java/org/openapitools/model/";
289+
290+
assertFileContains(apiPath + "BookInfo.java", "public BookInfo(String name)");
291+
assertFileContains(apiPath + "BasicBookInfo.java", "public BasicBookInfo(String author, String name)", "super(name)");
292+
assertFileContains(apiPath + "DetailedBookInfo.java", "public DetailedBookInfo(String isbn, String name, String author)", "super(author, name)");
293+
}
271294
}

openapi-generator/src/test/java/io/micronaut/openapi/generator/MicronautServerCodegenTest.java

+23
Original file line numberDiff line numberDiff line change
@@ -305,4 +305,27 @@ void doRepeatOperationForAllTags() {
305305
assertFileContains(apiPath + "SearchApi.java",
306306
"authorSearchGet", "bookSearchGet");
307307
}
308+
309+
@Test
310+
void testReadOnlyConstructorBug() {
311+
312+
var codegen = new JavaMicronautServerCodegen();
313+
String outputPath = generateFiles(codegen, "src/test/resources/3_0/readonlyconstructorbug.yml", CodegenConstants.MODELS);
314+
String apiPath = outputPath + "src/main/java/org/openapitools/model/";
315+
316+
assertFileContains(apiPath + "BookInfo.java", "public BookInfo(String name, String requiredReadOnly)");
317+
assertFileContains(apiPath + "ExtendedBookInfo.java", "public ExtendedBookInfo(String isbn, String name, String requiredReadOnly)", "super(name, requiredReadOnly)");
318+
}
319+
320+
@Test
321+
void testDiscriminatorConstructorBug() {
322+
323+
var codegen = new JavaMicronautServerCodegen();
324+
String outputPath = generateFiles(codegen, "src/test/resources/3_0/discriminatorconstructorbug.yml", CodegenConstants.MODELS);
325+
String apiPath = outputPath + "src/main/java/org/openapitools/model/";
326+
327+
assertFileContains(apiPath + "BookInfo.java", "public BookInfo(String name)");
328+
assertFileContains(apiPath + "BasicBookInfo.java", "public BasicBookInfo(String author, String name)", "super(name)");
329+
assertFileContains(apiPath + "DetailedBookInfo.java", "public DetailedBookInfo(String isbn, String name, String author)", "super(author, name)");
330+
}
308331
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#tag::info[]
2+
openapi: 3.0.0 # <1>
3+
info: # <2>
4+
description: This is a library API
5+
version: 1.0.0
6+
title: Library
7+
license:
8+
name: Apache-2.0
9+
url: "https://www.apache.org/licenses/LICENSE-2.0.html"
10+
tags: # <3>
11+
- name: books
12+
description: Search for books and add new ones
13+
#end::info[]
14+
#tag::paths[]
15+
paths:
16+
#end::paths[]
17+
#tag::search[]
18+
/search:
19+
get: # <1>
20+
tags:
21+
- books # <2>
22+
summary: Search for a book
23+
operationId: search # <3>
24+
parameters: # <4>
25+
- name: book-name
26+
in: query
27+
schema:
28+
type: string
29+
minLength: 3 # <5>
30+
- name: author-name
31+
in: query
32+
schema:
33+
type: string
34+
responses: # <6>
35+
"200": # <7>
36+
description: Success
37+
content:
38+
"application/json":
39+
schema:
40+
type: array,
41+
items:
42+
$ref: "#/components/schemas/BookInfo"
43+
"400": # <8>
44+
description: Bad Request
45+
#end::search[]
46+
#tag::add[]
47+
/add:
48+
post: # <1>
49+
tags: [books]
50+
summary: Add a new book
51+
operationId: addBook
52+
requestBody: # <2>
53+
required: true
54+
content:
55+
"application/json":
56+
schema:
57+
$ref: "#/components/schemas/BookInfo" # <3>
58+
responses:
59+
"200":
60+
description: Success
61+
"400":
62+
description: Bad Request
63+
#end::add[]
64+
#tag::components[]
65+
components:
66+
schemas:
67+
BookInfo:
68+
title: Book Info # <1>
69+
description: Object containg all the info about a book
70+
type: object
71+
properties: # <2>
72+
name: {type: string}
73+
type:
74+
type: string
75+
enum:
76+
- BASIC
77+
- DETAILED
78+
required: ["name", "availability", "type"]
79+
discriminator:
80+
propertyName: type
81+
mapping:
82+
BASIC: "#/components/schemas/BasicBookInfo"
83+
DETAILED: "#/components/schemas/DetailedBookInfo"
84+
BasicBookInfo:
85+
x-all-of-name: BasicBookInfo
86+
allOf:
87+
- $ref: '#/components/schemas/BookInfo'
88+
- type: object
89+
properties: # <2>
90+
author: {type: string, minLength: 3}
91+
required: ["author"]
92+
DetailedBookInfo:
93+
x-all-of-name: DetailedBookInfo
94+
allOf:
95+
- $ref: '#/components/schemas/BasicBookInfo'
96+
- type: object
97+
properties: # <2>
98+
isbn: {type: string, pattern: "[0-9]{13}"}
99+
required: ["isbn"]
100+
#end::components[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#tag::info[]
2+
openapi: 3.0.0 # <1>
3+
info: # <2>
4+
description: This is a library API
5+
version: 1.0.0
6+
title: Library
7+
license:
8+
name: Apache-2.0
9+
url: "https://www.apache.org/licenses/LICENSE-2.0.html"
10+
tags: # <3>
11+
- name: books
12+
description: Search for books and add new ones
13+
#end::info[]
14+
#tag::paths[]
15+
paths:
16+
#end::paths[]
17+
#tag::search[]
18+
/search:
19+
get: # <1>
20+
tags:
21+
- books # <2>
22+
summary: Search for a book
23+
operationId: search # <3>
24+
parameters: # <4>
25+
- name: book-name
26+
in: query
27+
schema:
28+
type: string
29+
minLength: 3 # <5>
30+
- name: author-name
31+
in: query
32+
schema:
33+
type: string
34+
responses: # <6>
35+
"200": # <7>
36+
description: Success
37+
content:
38+
"application/json":
39+
schema:
40+
type: array
41+
items:
42+
$ref: "#/components/schemas/BookInfo"
43+
"400": # <8>
44+
description: Bad Request
45+
#end::search[]
46+
#tag::add[]
47+
/add:
48+
post: # <1>
49+
tags: [books]
50+
summary: Add a new book
51+
operationId: addBook
52+
requestBody: # <2>
53+
required: true
54+
content:
55+
"application/json":
56+
schema:
57+
$ref: "#/components/schemas/BookInfo" # <3>
58+
responses:
59+
"200":
60+
description: Success
61+
"400":
62+
description: Bad Request
63+
#end::add[]
64+
#tag::components[]
65+
components:
66+
schemas:
67+
BookInfo:
68+
title: Book Info # <1>
69+
description: Object containg all the info about a book
70+
type: object
71+
properties: # <2>
72+
name: {type: string}
73+
author: {type: string, minLength: 3}
74+
optionalReadOnly: {type: string, readOnly: true}
75+
requiredReadOnly: {type: string, readOnly: true}
76+
type:
77+
type: string
78+
enum:
79+
- EXTENDED
80+
required: ["name", "requiredReadOnly"]
81+
discriminator:
82+
propertyName: type
83+
mapping:
84+
EXTENDED: "#/components/schemas/ExtendedBookInfo"
85+
ExtendedBookInfo:
86+
x-all-of-name: ExtendedBookInfo
87+
allOf:
88+
- $ref: '#/components/schemas/BookInfo'
89+
- type: object
90+
properties: # <2>
91+
isbn: {type: string, pattern: "[0-9]{13}"}
92+
required: ["isbn"]
93+
#end::components[]

0 commit comments

Comments
 (0)