Skip to content

Commit e63615c

Browse files
committed
add implementation to detect schemas matching PageModel shape and replace with generic
1 parent 159b367 commit e63615c

4 files changed

Lines changed: 35 additions & 78 deletions

File tree

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

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,8 +1147,9 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
11471147
codegenOperation.returnBaseType = "PagedModel";
11481148
// Clear any container flag — PagedModel is not itself a List/array
11491149
codegenOperation.returnContainer = null;
1150-
// Remove stale import for the suppressed paged schema and add PagedModel
1151-
codegenOperation.imports.remove(detected.schemaName);
1150+
// Keep the paged schema import (needed for @ApiResponse / springdoc annotations)
1151+
// Add item type import (needed for PagedModel<User> in method signature)
1152+
codegenOperation.imports.add(detected.itemSchemaName);
11521153
codegenOperation.imports.add("PagedModel");
11531154
LOGGER.info("substituteGenericPagedModel: operation '{}': replacing return type '{}' with PagedModel<{}>",
11541155
codegenOperation.operationId, oldType, detected.itemSchemaName);
@@ -1308,31 +1309,8 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
13081309
objs = super.postProcessAllModels(objs);
13091310

13101311
if (substituteGenericPagedModel && !pagedModelRegistry.isEmpty()) {
1311-
// Collect the pagination metadata schema names to suppress (deduplicated)
1312-
Set<String> metaSchemasToSuppress = new HashSet<>();
1313-
for (PagedModelScanUtils.DetectedPagedModel detected : pagedModelRegistry.values()) {
1314-
if (detected.metaSchemaName != null) {
1315-
metaSchemasToSuppress.add(detected.metaSchemaName);
1316-
}
1317-
}
1318-
1319-
// Suppress each detected paged schema
1320-
for (Map.Entry<String, PagedModelScanUtils.DetectedPagedModel> entry : pagedModelRegistry.entrySet()) {
1321-
String schemaName = entry.getKey();
1322-
PagedModelScanUtils.DetectedPagedModel detected = entry.getValue();
1323-
if (objs.remove(schemaName) != null) {
1324-
LOGGER.info("substituteGenericPagedModel: suppressing model '{}' — replaced by PagedModel<{}>",
1325-
schemaName, detected.itemSchemaName);
1326-
}
1327-
}
1328-
1329-
// Suppress the pagination metadata schema(s)
1330-
for (String metaName : metaSchemasToSuppress) {
1331-
if (objs.remove(metaName) != null) {
1332-
LOGGER.info("substituteGenericPagedModel: suppressing pagination metadata model '{}'"
1333-
+ " — replaced by PagedModel.PageMetadata", metaName);
1334-
}
1335-
}
1312+
LOGGER.info("substituteGenericPagedModel: detected {} paged-model schema(s): {} — models kept for @ApiResponse annotations",
1313+
pagedModelRegistry.size(), pagedModelRegistry.keySet());
13361314
}
13371315

13381316
return objs;

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

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,8 +1357,9 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
13571357
codegenOperation.returnBaseType = "PagedModel";
13581358
// Clear any container flag — PagedModel is not itself a List/array
13591359
codegenOperation.returnContainer = null;
1360-
// Remove stale import for the suppressed paged schema and add PagedModel
1361-
codegenOperation.imports.remove(detected.schemaName);
1360+
// Keep the paged schema import (needed for @ApiResponse / springdoc annotations)
1361+
// Add item type import (needed for PagedModel<User> in method signature)
1362+
codegenOperation.imports.add(detected.itemSchemaName);
13621363
codegenOperation.imports.add("PagedModel");
13631364
LOGGER.info("substituteGenericPagedModel: operation '{}': replacing return type '{}' with PagedModel<{}>",
13641365
codegenOperation.operationId, oldType, detected.itemSchemaName);
@@ -1420,31 +1421,8 @@ public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs)
14201421
}
14211422

14221423
if (substituteGenericPagedModel && !pagedModelRegistry.isEmpty()) {
1423-
// Collect the pagination metadata schema names to suppress (deduplicated across detections)
1424-
Set<String> metaSchemasToSuppress = new HashSet<>();
1425-
for (PagedModelScanUtils.DetectedPagedModel detected : pagedModelRegistry.values()) {
1426-
if (detected.metaSchemaName != null) {
1427-
metaSchemasToSuppress.add(detected.metaSchemaName);
1428-
}
1429-
}
1430-
1431-
// Suppress each detected paged schema
1432-
for (Map.Entry<String, PagedModelScanUtils.DetectedPagedModel> entry : pagedModelRegistry.entrySet()) {
1433-
String schemaName = entry.getKey();
1434-
PagedModelScanUtils.DetectedPagedModel detected = entry.getValue();
1435-
if (objs.remove(schemaName) != null) {
1436-
LOGGER.info("substituteGenericPagedModel: suppressing model '{}' — replaced by PagedModel<{}>",
1437-
schemaName, detected.itemSchemaName);
1438-
}
1439-
}
1440-
1441-
// Suppress the pagination metadata schema(s)
1442-
for (String metaName : metaSchemasToSuppress) {
1443-
if (objs.remove(metaName) != null) {
1444-
LOGGER.info("substituteGenericPagedModel: suppressing pagination metadata model '{}'"
1445-
+ " — replaced by PagedModel.PageMetadata", metaName);
1446-
}
1447-
}
1424+
LOGGER.info("substituteGenericPagedModel: detected {} paged-model schema(s): {} — models kept for @ApiResponse annotations",
1425+
pagedModelRegistry.size(), pagedModelRegistry.keySet());
14481426
}
14491427

14501428
return objs;

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

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7158,27 +7158,27 @@ public void substituteGenericPagedModel_isDisabledByDefault() throws IOException
71587158
}
71597159

71607160
@Test
7161-
public void substituteGenericPagedModel_suppressesPagedSchemas() throws IOException {
7161+
public void substituteGenericPagedModel_keepsPagedSchemas() throws IOException {
7162+
// Paged schema classes must still be generated — springdoc @ApiResponse annotations reference them
71627163
Map<String, Object> props = commonPagedModelProps();
71637164

71647165
Map<String, File> files = generateFromContract(
71657166
"src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_BOOT, props);
71667167

7167-
// Detected paged schemas must NOT appear as generated model files
7168-
assertThat(files).doesNotContainKey("UserPage.java");
7169-
assertThat(files).doesNotContainKey("OrderPage.java");
7170-
assertThat(files).doesNotContainKey("PetPageAllOf.java");
7168+
assertThat(files).containsKey("UserPage.java");
7169+
assertThat(files).containsKey("OrderPage.java");
7170+
assertThat(files).containsKey("PetPageAllOf.java");
71717171
}
71727172

71737173
@Test
7174-
public void substituteGenericPagedModel_suppressesPaginationMetadataSchema() throws IOException {
7174+
public void substituteGenericPagedModel_keepsPaginationMetadataSchema() throws IOException {
7175+
// The shared pagination-metadata schema must also remain generated
71757176
Map<String, Object> props = commonPagedModelProps();
71767177

71777178
Map<String, File> files = generateFromContract(
71787179
"src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_BOOT, props);
71797180

7180-
// The shared pagination-metadata schema (PageMeta) must also be suppressed
7181-
assertThat(files).doesNotContainKey("PageMeta.java");
7181+
assertThat(files).containsKey("PageMeta.java");
71827182
}
71837183

71847184
@Test
@@ -7211,13 +7211,12 @@ public void substituteGenericPagedModel_replacesReturnTypeInOperation() throws I
72117211

72127212
@Test
72137213
public void substituteGenericPagedModel_replacesExternalRefPagedSchema() throws IOException {
7214-
// OrderPage uses PageMetadata from an external file — must still be detected and replaced
7214+
// OrderPage uses PageMetadata from an external file — must still be detected and return type replaced
72157215
Map<String, Object> props = commonPagedModelProps();
72167216

72177217
Map<String, File> files = generateFromContract(
72187218
"src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_BOOT, props);
72197219

7220-
assertThat(files).doesNotContainKey("OrderPage.java");
72217220
JavaFileAssert.assertThat(files.get("OrderApi.java"))
72227221
.assertMethod("listOrders")
72237222
.hasReturnType("ResponseEntity<PagedModel<Order>>");
@@ -7231,22 +7230,22 @@ public void substituteGenericPagedModel_replacesAllOfPagedSchema() throws IOExce
72317230
Map<String, File> files = generateFromContract(
72327231
"src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_BOOT, props);
72337232

7234-
assertThat(files).doesNotContainKey("PetPageAllOf.java");
72357233
JavaFileAssert.assertThat(files.get("PetApi.java"))
72367234
.assertMethod("listPetsPaged")
72377235
.hasReturnType("ResponseEntity<PagedModel<Pet>>");
72387236
}
72397237

72407238
@Test
7241-
public void substituteGenericPagedModel_importsPagedModelInApiFile() throws IOException {
7239+
public void substituteGenericPagedModel_importsPagedModelAndItemTypeInApiFile() throws IOException {
72427240
Map<String, Object> props = commonPagedModelProps();
72437241

72447242
Map<String, File> files = generateFromContract(
72457243
"src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_BOOT, props);
72467244

7247-
// The api file must import PagedModel
7245+
// The api file must import both PagedModel and the item type
72487246
JavaFileAssert.assertThat(files.get("UserApi.java"))
7249-
.fileContains("import org.springframework.data.web.PagedModel");
7247+
.fileContains("import org.springframework.data.web.PagedModel")
7248+
.fileContains("import org.openapitools.model.User");
72507249
}
72517250

72527251
@Test

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5527,22 +5527,25 @@ public void substituteGenericPagedModel_isDisabledByDefault() throws IOException
55275527
assertThat(files).containsKey("PageMeta.kt");
55285528
}
55295529

5530+
55305531
@Test
5531-
public void substituteGenericPagedModel_suppressesPagedSchemas() throws IOException {
5532+
public void substituteGenericPagedModel_keepsPagedSchemas() throws IOException {
5533+
// Paged schema classes must still be generated — springdoc @ApiResponse annotations reference them
55325534
Map<String, File> files = generateFromContract(
55335535
"src/test/resources/3_0/spring/petstore-paged-model.yaml", commonKotlinPagedModelProps());
55345536

5535-
assertThat(files).doesNotContainKey("UserPage.kt");
5536-
assertThat(files).doesNotContainKey("OrderPage.kt");
5537-
assertThat(files).doesNotContainKey("PetPageAllOf.kt");
5537+
assertThat(files).containsKey("UserPage.kt");
5538+
assertThat(files).containsKey("OrderPage.kt");
5539+
assertThat(files).containsKey("PetPageAllOf.kt");
55385540
}
55395541

55405542
@Test
5541-
public void substituteGenericPagedModel_suppressesPaginationMetadataSchema() throws IOException {
5543+
public void substituteGenericPagedModel_keepsPaginationMetadataSchema() throws IOException {
5544+
// The shared pagination-metadata schema must also remain generated
55425545
Map<String, File> files = generateFromContract(
55435546
"src/test/resources/3_0/spring/petstore-paged-model.yaml", commonKotlinPagedModelProps());
55445547

5545-
assertThat(files).doesNotContainKey("PageMeta.kt");
5548+
assertThat(files).containsKey("PageMeta.kt");
55465549
}
55475550

55485551
@Test
@@ -5571,11 +5574,10 @@ public void substituteGenericPagedModel_replacesReturnTypeInOperation() throws I
55715574

55725575
@Test
55735576
public void substituteGenericPagedModel_replacesExternalRefPagedSchema() throws IOException {
5574-
// OrderPage uses PageMetadata from an external file — must still be detected
5577+
// OrderPage uses PageMetadata from an external file — must still be detected and return type replaced
55755578
Map<String, File> files = generateFromContract(
55765579
"src/test/resources/3_0/spring/petstore-paged-model.yaml", commonKotlinPagedModelProps());
55775580

5578-
assertThat(files).doesNotContainKey("OrderPage.kt");
55795581
File orderApi = files.get("OrderApi.kt");
55805582
assertThat(orderApi).isNotNull();
55815583
String content = Files.readString(orderApi.toPath());
@@ -5587,22 +5589,22 @@ public void substituteGenericPagedModel_replacesAllOfPagedSchema() throws IOExce
55875589
Map<String, File> files = generateFromContract(
55885590
"src/test/resources/3_0/spring/petstore-paged-model.yaml", commonKotlinPagedModelProps());
55895591

5590-
assertThat(files).doesNotContainKey("PetPageAllOf.kt");
55915592
File petApi = files.get("PetApi.kt");
55925593
assertThat(petApi).isNotNull();
55935594
String content = Files.readString(petApi.toPath());
55945595
assertThat(content).contains("PagedModel<Pet>");
55955596
}
55965597

55975598
@Test
5598-
public void substituteGenericPagedModel_importsPagedModelInApiFile() throws IOException {
5599+
public void substituteGenericPagedModel_importsPagedModelAndItemTypeInApiFile() throws IOException {
55995600
Map<String, File> files = generateFromContract(
56005601
"src/test/resources/3_0/spring/petstore-paged-model.yaml", commonKotlinPagedModelProps());
56015602

56025603
File userApi = files.get("UserApi.kt");
56035604
assertThat(userApi).isNotNull();
56045605
String content = Files.readString(userApi.toPath());
56055606
assertThat(content).contains("import org.springframework.data.web.PagedModel");
5607+
assertThat(content).contains("import org.openapitools.model.User");
56065608
}
56075609

56085610
@Test

0 commit comments

Comments
 (0)