Skip to content

Commit faa1326

Browse files
committed
fix generation suppresion logic and regenerate samples
1 parent c69ea08 commit faa1326

8 files changed

Lines changed: 242 additions & 10 deletions

File tree

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,12 +1316,13 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
13161316
if (getAnnotationLibrary() == AnnotationLibrary.NONE) {
13171317
// No @ApiResponse annotations are generated when annotationLibrary=none,
13181318
// so paged schemas are not referenced anywhere → safe to suppress.
1319-
Set<String> metaSchemasToSuppress = new HashSet<>();
1319+
Set<String> metaSchemasToCheck = new HashSet<>();
13201320
for (PagedModelScanUtils.DetectedPagedModel detected : pagedModelRegistry.values()) {
13211321
if (detected.metaSchemaName != null) {
1322-
metaSchemasToSuppress.add(detected.metaSchemaName);
1322+
metaSchemasToCheck.add(detected.metaSchemaName);
13231323
}
13241324
}
1325+
// Remove paged schemas first so reference checks below reflect the post-suppression state.
13251326
for (Map.Entry<String, PagedModelScanUtils.DetectedPagedModel> entry : pagedModelRegistry.entrySet()) {
13261327
String schemaName = entry.getKey();
13271328
PagedModelScanUtils.DetectedPagedModel detected = entry.getValue();
@@ -1330,8 +1331,17 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
13301331
schemaName, detected.itemSchemaName);
13311332
}
13321333
}
1333-
for (String metaName : metaSchemasToSuppress) {
1334-
if (objs.remove(metaName) != null) {
1334+
// Suppress meta schemas only when no remaining (non-suppressed) schema references them.
1335+
// Example: if SearchResult has a 'page: PageMeta' property, PageMeta must be kept.
1336+
for (String metaName : metaSchemasToCheck) {
1337+
boolean referencedElsewhere = objs.values().stream()
1338+
.flatMap(mm -> mm.getModels().stream())
1339+
.map(ModelMap::getModel)
1340+
.anyMatch(cm -> cm.imports.contains(metaName));
1341+
if (referencedElsewhere) {
1342+
LOGGER.info("substituteGenericPagedModel: keeping pagination metadata model '{}'"
1343+
+ " — referenced by a non-paged schema", metaName);
1344+
} else if (objs.remove(metaName) != null) {
13351345
LOGGER.info("substituteGenericPagedModel: suppressing pagination metadata model '{}'"
13361346
+ " — replaced by PagedModel.PageMetadata", metaName);
13371347
}

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,12 +1428,13 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
14281428
if (getAnnotationLibrary() == AnnotationLibrary.NONE) {
14291429
// No @ApiResponse annotations are generated when annotationLibrary=none,
14301430
// so paged schemas are not referenced anywhere → safe to suppress.
1431-
Set<String> metaSchemasToSuppress = new HashSet<>();
1431+
Set<String> metaSchemasToCheck = new HashSet<>();
14321432
for (PagedModelScanUtils.DetectedPagedModel detected : pagedModelRegistry.values()) {
14331433
if (detected.metaSchemaName != null) {
1434-
metaSchemasToSuppress.add(detected.metaSchemaName);
1434+
metaSchemasToCheck.add(detected.metaSchemaName);
14351435
}
14361436
}
1437+
// Remove paged schemas first so reference checks below reflect the post-suppression state.
14371438
for (Map.Entry<String, PagedModelScanUtils.DetectedPagedModel> entry : pagedModelRegistry.entrySet()) {
14381439
String schemaName = entry.getKey();
14391440
PagedModelScanUtils.DetectedPagedModel detected = entry.getValue();
@@ -1442,8 +1443,17 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
14421443
schemaName, detected.itemSchemaName);
14431444
}
14441445
}
1445-
for (String metaName : metaSchemasToSuppress) {
1446-
if (objs.remove(metaName) != null) {
1446+
// Suppress meta schemas only when no remaining (non-suppressed) schema references them.
1447+
// Example: if SearchResult has a 'page: PageMeta' property, PageMeta must be kept.
1448+
for (String metaName : metaSchemasToCheck) {
1449+
boolean referencedElsewhere = objs.values().stream()
1450+
.flatMap(mm -> mm.getModels().stream())
1451+
.map(ModelMap::getModel)
1452+
.anyMatch(cm -> cm.imports.contains(metaName));
1453+
if (referencedElsewhere) {
1454+
LOGGER.info("substituteGenericPagedModel: keeping pagination metadata model '{}'"
1455+
+ " — referenced by a non-paged schema", metaName);
1456+
} else if (objs.remove(metaName) != null) {
14471457
LOGGER.info("substituteGenericPagedModel: suppressing pagination metadata model '{}'"
14481458
+ " — replaced by PagedModel.PageMetadata", metaName);
14491459
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7297,7 +7297,9 @@ public void substituteGenericPagedModel_suppressesPageMetaWhenNoAnnotations() th
72977297
Map<String, File> files = generateFromContract(
72987298
"src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_BOOT, noAnnotationPagedModelProps());
72997299

7300-
assertThat(files).doesNotContainKey("PageMeta.java");
7300+
// PageMetadata is only referenced by OrderPage (which is suppressed) → suppressed
73017301
assertThat(files).doesNotContainKey("PageMetadata.java");
7302+
// PageMeta is referenced by SearchResult (a non-paged schema) → must be kept
7303+
assertThat(files).containsKey("PageMeta.java");
73027304
}
73037305
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5656,7 +5656,9 @@ public void substituteGenericPagedModel_suppressesPageMetaWhenNoAnnotations() th
56565656
Map<String, File> files = generateFromContract(
56575657
"src/test/resources/3_0/spring/petstore-paged-model.yaml", noAnnotationKotlinPagedModelProps());
56585658

5659-
assertThat(files).doesNotContainKey("PageMeta.kt");
5659+
// PageMetadata is only referenced by OrderPage (which is suppressed) → suppressed
56605660
assertThat(files).doesNotContainKey("PageMetadata.kt");
5661+
// PageMeta is referenced by SearchResult (a non-paged schema) → must be kept
5662+
assertThat(files).containsKey("PageMeta.kt");
56615663
}
56625664
}

samples/server/petstore/kotlin-springboot-paged-model/.openapi-generator/FILES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ src/main/kotlin/org/openapitools/api/PetApi.kt
1313
src/main/kotlin/org/openapitools/api/UserApi.kt
1414
src/main/kotlin/org/openapitools/configuration/EnumConverterConfiguration.kt
1515
src/main/kotlin/org/openapitools/model/Order.kt
16+
src/main/kotlin/org/openapitools/model/PageMeta.kt
1617
src/main/kotlin/org/openapitools/model/Pet.kt
1718
src/main/kotlin/org/openapitools/model/PetSort.kt
1819
src/main/kotlin/org/openapitools/model/SearchResult.kt
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.openapitools.model
2+
3+
import java.util.Objects
4+
import com.fasterxml.jackson.annotation.JsonProperty
5+
import jakarta.validation.constraints.DecimalMax
6+
import jakarta.validation.constraints.DecimalMin
7+
import jakarta.validation.constraints.Email
8+
import jakarta.validation.constraints.Max
9+
import jakarta.validation.constraints.Min
10+
import jakarta.validation.constraints.NotNull
11+
import jakarta.validation.constraints.Pattern
12+
import jakarta.validation.constraints.Size
13+
import jakarta.validation.Valid
14+
15+
/**
16+
* Shared pagination metadata schema
17+
* @param propertySize
18+
* @param number
19+
* @param totalElements
20+
* @param totalPages
21+
*/
22+
data class PageMeta(
23+
24+
@get:JsonProperty("size", required = true) val propertySize: kotlin.Long,
25+
26+
@get:JsonProperty("number", required = true) val number: kotlin.Long,
27+
28+
@get:JsonProperty("totalElements", required = true) val totalElements: kotlin.Long,
29+
30+
@get:JsonProperty("totalPages", required = true) val totalPages: kotlin.Long
31+
) : java.io.Serializable {
32+
33+
companion object {
34+
private const val serialVersionUID: kotlin.Long = 1
35+
}
36+
}
37+

samples/server/petstore/springboot-paged-model/.openapi-generator/FILES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ src/main/java/org/openapitools/api/PetApi.java
66
src/main/java/org/openapitools/api/UserApi.java
77
src/main/java/org/openapitools/configuration/EnumConverterConfiguration.java
88
src/main/java/org/openapitools/model/Order.java
9+
src/main/java/org/openapitools/model/PageMeta.java
910
src/main/java/org/openapitools/model/Pet.java
1011
src/main/java/org/openapitools/model/PetSort.java
1112
src/main/java/org/openapitools/model/SearchResult.java
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package org.openapitools.model;
2+
3+
import java.net.URI;
4+
import java.util.Objects;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import com.fasterxml.jackson.annotation.JsonCreator;
7+
import org.springframework.lang.Nullable;
8+
import org.openapitools.jackson.nullable.JsonNullable;
9+
import java.io.Serializable;
10+
import java.time.OffsetDateTime;
11+
import jakarta.validation.Valid;
12+
import jakarta.validation.constraints.*;
13+
14+
15+
import java.util.*;
16+
import jakarta.annotation.Generated;
17+
18+
/**
19+
* Shared pagination metadata schema
20+
*/
21+
22+
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.22.0-SNAPSHOT")
23+
public class PageMeta implements Serializable {
24+
25+
private static final long serialVersionUID = 1L;
26+
27+
private Long size;
28+
29+
private Long number;
30+
31+
private Long totalElements;
32+
33+
private Long totalPages;
34+
35+
public PageMeta() {
36+
super();
37+
}
38+
39+
/**
40+
* Constructor with only required parameters
41+
*/
42+
public PageMeta(Long size, Long number, Long totalElements, Long totalPages) {
43+
this.size = size;
44+
this.number = number;
45+
this.totalElements = totalElements;
46+
this.totalPages = totalPages;
47+
}
48+
49+
public PageMeta size(Long size) {
50+
this.size = size;
51+
return this;
52+
}
53+
54+
/**
55+
* Get size
56+
* @return size
57+
*/
58+
@NotNull
59+
@JsonProperty("size")
60+
public Long getSize() {
61+
return size;
62+
}
63+
64+
@JsonProperty("size")
65+
public void setSize(Long size) {
66+
this.size = size;
67+
}
68+
69+
public PageMeta number(Long number) {
70+
this.number = number;
71+
return this;
72+
}
73+
74+
/**
75+
* Get number
76+
* @return number
77+
*/
78+
@NotNull
79+
@JsonProperty("number")
80+
public Long getNumber() {
81+
return number;
82+
}
83+
84+
@JsonProperty("number")
85+
public void setNumber(Long number) {
86+
this.number = number;
87+
}
88+
89+
public PageMeta totalElements(Long totalElements) {
90+
this.totalElements = totalElements;
91+
return this;
92+
}
93+
94+
/**
95+
* Get totalElements
96+
* @return totalElements
97+
*/
98+
@NotNull
99+
@JsonProperty("totalElements")
100+
public Long getTotalElements() {
101+
return totalElements;
102+
}
103+
104+
@JsonProperty("totalElements")
105+
public void setTotalElements(Long totalElements) {
106+
this.totalElements = totalElements;
107+
}
108+
109+
public PageMeta totalPages(Long totalPages) {
110+
this.totalPages = totalPages;
111+
return this;
112+
}
113+
114+
/**
115+
* Get totalPages
116+
* @return totalPages
117+
*/
118+
@NotNull
119+
@JsonProperty("totalPages")
120+
public Long getTotalPages() {
121+
return totalPages;
122+
}
123+
124+
@JsonProperty("totalPages")
125+
public void setTotalPages(Long totalPages) {
126+
this.totalPages = totalPages;
127+
}
128+
129+
@Override
130+
public boolean equals(Object o) {
131+
if (this == o) {
132+
return true;
133+
}
134+
if (o == null || getClass() != o.getClass()) {
135+
return false;
136+
}
137+
PageMeta pageMeta = (PageMeta) o;
138+
return Objects.equals(this.size, pageMeta.size) &&
139+
Objects.equals(this.number, pageMeta.number) &&
140+
Objects.equals(this.totalElements, pageMeta.totalElements) &&
141+
Objects.equals(this.totalPages, pageMeta.totalPages);
142+
}
143+
144+
@Override
145+
public int hashCode() {
146+
return Objects.hash(size, number, totalElements, totalPages);
147+
}
148+
149+
@Override
150+
public String toString() {
151+
StringBuilder sb = new StringBuilder();
152+
sb.append("class PageMeta {\n");
153+
sb.append(" size: ").append(toIndentedString(size)).append("\n");
154+
sb.append(" number: ").append(toIndentedString(number)).append("\n");
155+
sb.append(" totalElements: ").append(toIndentedString(totalElements)).append("\n");
156+
sb.append(" totalPages: ").append(toIndentedString(totalPages)).append("\n");
157+
sb.append("}");
158+
return sb.toString();
159+
}
160+
161+
/**
162+
* Convert the given object to string with each line indented by 4 spaces
163+
* (except the first line).
164+
*/
165+
private String toIndentedString(@Nullable Object o) {
166+
return o == null ? "null" : o.toString().replace("\n", "\n ");
167+
}
168+
}
169+

0 commit comments

Comments
 (0)