diff --git a/docs/generators/java-camel.md b/docs/generators/java-camel.md index 2bd68f57cb33..42c864eed0f8 100644 --- a/docs/generators/java-camel.md +++ b/docs/generators/java-camel.md @@ -104,7 +104,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| |sourceFolder|source folder for generated code| |src/main/java| |springApiVersion|Value for 'version' attribute in @RequestMapping (for Spring 7 and above).| |null| -|substituteGenericPagedModel|Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' pagination-metadata property) and replace their generated references with PagedModel<T>. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata schema are suppressed from code generation. Only applies when library=spring-boot or spring-http-interface.| |false| +|substituteGenericPagedModel|Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' pagination-metadata property) and replace their generated references with PagedModel<T>. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata schema are suppressed from code generation.| |false| |testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi| |title|server title name or client service name| |OpenAPI Spring| |unhandledException|Declare operation methods to throw a generic exception and allow unhandled exceptions (useful for Spring `@ControllerAdvice` directives).| |false| diff --git a/docs/generators/kotlin-spring.md b/docs/generators/kotlin-spring.md index 1d0e83db8717..b9ede8f82135 100644 --- a/docs/generators/kotlin-spring.md +++ b/docs/generators/kotlin-spring.md @@ -58,7 +58,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |null| |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |null| |sourceFolder|source folder for generated code| |src/main/kotlin| -|substituteGenericPagedModel|Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' pagination-metadata property) and replace their generated references with PagedModel<T>. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata schema are suppressed from code generation. Only applies when library=spring-boot or spring-declarative-http-interface.| |false| +|substituteGenericPagedModel|Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' pagination-metadata property) and replace their generated references with PagedModel<T>. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata schema are suppressed from code generation.| |false| |title|server title name or client service name| |OpenAPI Kotlin Spring| |useBeanValidation|Use BeanValidation API annotations to validate data types| |true| |useFeignClientUrl|Whether to generate Feign client with url parameter.| |true| diff --git a/docs/generators/spring.md b/docs/generators/spring.md index 0e897c145fdc..e1a4d7771e61 100644 --- a/docs/generators/spring.md +++ b/docs/generators/spring.md @@ -97,7 +97,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true| |sourceFolder|source folder for generated code| |src/main/java| |springApiVersion|Value for 'version' attribute in @RequestMapping (for Spring 7 and above).| |null| -|substituteGenericPagedModel|Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' pagination-metadata property) and replace their generated references with PagedModel<T>. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata schema are suppressed from code generation. Only applies when library=spring-boot or spring-http-interface.| |false| +|substituteGenericPagedModel|Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' pagination-metadata property) and replace their generated references with PagedModel<T>. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata schema are suppressed from code generation.| |false| |testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi| |title|server title name or client service name| |OpenAPI Spring| |unhandledException|Declare operation methods to throw a generic exception and allow unhandled exceptions (useful for Spring `@ControllerAdvice` directives).| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java index 6d2a86061991..1fb3c78a9e89 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinSpringServerCodegen.java @@ -308,7 +308,7 @@ public KotlinSpringServerCodegen() { "Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' " + "pagination-metadata property) and replace their generated references with " + "PagedModel. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata " - + "schema are suppressed from code generation. Only applies when library=spring-boot or spring-declarative-http-interface.", + + "schema are suppressed from code generation.", substituteGenericPagedModel); addSwitch(COMPANION_OBJECT, "Whether to generate companion objects in data classes, enabling companion extensions.", companionObject); supportedLibraries.put(SPRING_BOOT, "Spring-boot Server application."); @@ -750,7 +750,7 @@ public void processOpts() { this.setGeneratePageableConstraintValidation(convertPropertyToBoolean(GENERATE_PAGEABLE_CONSTRAINT_VALIDATION)); } writePropertyBack(GENERATE_PAGEABLE_CONSTRAINT_VALIDATION, generatePageableConstraintValidation); - if (additionalProperties.containsKey(SUBSTITUTE_GENERIC_PAGED_MODEL) && (library.equals(SPRING_BOOT) || library.equals(SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY))) { + if (additionalProperties.containsKey(SUBSTITUTE_GENERIC_PAGED_MODEL)) { this.setSubstituteGenericPagedModel(convertPropertyToBoolean(SUBSTITUTE_GENERIC_PAGED_MODEL)); } writePropertyBack(SUBSTITUTE_GENERIC_PAGED_MODEL, substituteGenericPagedModel); @@ -1211,7 +1211,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } } - if ((SPRING_BOOT.equals(library) || SPRING_DECLARATIVE_HTTP_INTERFACE_LIBRARY.equals(library)) && substituteGenericPagedModel) { + if (substituteGenericPagedModel) { pagedModelRegistry = PagedModelScanUtils.scanPagedModels(openAPI); if (!pagedModelRegistry.isEmpty()) { boolean customMapping = importMapping.containsKey("PagedModel"); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index 76e20d09be36..9329e8a1b5c5 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -380,7 +380,7 @@ public SpringCodegen() { "Detect schemas that represent paginated responses (an object with a 'content' array property and a 'page' " + "pagination-metadata property) and replace their generated references with " + "PagedModel. By default this uses a generated type in the config package (default 'org.openapitools.configuration'), but `importMappings.PagedModel` can override it to a custom/FQCN-mapped type. The detected page schemas and the pagination metadata " - + "schema are suppressed from code generation. Only applies when library=spring-boot or spring-http-interface.", + + "schema are suppressed from code generation.", substituteGenericPagedModel)); } @@ -591,9 +591,7 @@ public void processOpts() { convertPropertyToBooleanAndWriteBack(ADDITIONAL_NOT_NULL_ANNOTATIONS, this::setAdditionalNotNullAnnotations); - if (SPRING_BOOT.equals(library) || SPRING_HTTP_INTERFACE.equals(library)) { - convertPropertyToBooleanAndWriteBack(SUBSTITUTE_GENERIC_PAGED_MODEL, this::setSubstituteGenericPagedModel); - } + convertPropertyToBooleanAndWriteBack(SUBSTITUTE_GENERIC_PAGED_MODEL, this::setSubstituteGenericPagedModel); if (SPRING_BOOT.equals(library)) { convertPropertyToBooleanAndWriteBack(AUTO_X_SPRING_PAGINATED, this::setAutoXSpringPaginated); @@ -873,7 +871,7 @@ public void preprocessOpenAPI(OpenAPI openAPI) { } } - if ((SPRING_BOOT.equals(library) || SPRING_HTTP_INTERFACE.equals(library)) && substituteGenericPagedModel) { + if (substituteGenericPagedModel) { pagedModelRegistry = PagedModelScanUtils.scanPagedModels(openAPI); if (!pagedModelRegistry.isEmpty()) { boolean customMapping = importMapping.containsKey("PagedModel"); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index b5d0467b75fa..430a8f473db8 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -7498,6 +7498,61 @@ private Map springHttpInterfacePagedModelProps() { return props; } + // ------------------------------------------------------------------------- + // substituteGenericPagedModel — spring-cloud + // ------------------------------------------------------------------------- + + @Test + public void substituteGenericPagedModel_springCloud_replacesReturnTypeInOperation() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_CLOUD_LIBRARY, + springCloudPagedModelProps()); + + JavaFileAssert.assertThat(files.get("UserApi.java")) + .assertMethod("listUsers") + .hasReturnType("ResponseEntity>"); + } + + @Test + public void substituteGenericPagedModel_springCloud_generatesPagedModelSupportingFile() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_CLOUD_LIBRARY, + springCloudPagedModelProps()); + + assertThat(files).containsKey("PagedModel.java"); + } + + @Test + public void substituteGenericPagedModel_springCloud_doesNotGeneratePagedModelFileWhenCustomMapping() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_CLOUD_LIBRARY, + springCloudPagedModelProps(), + configurator -> configurator.addImportMapping("PagedModel", "com.example.custom.MyPagedModel")); + + assertThat(files).doesNotContainKey("PagedModel.java"); + } + + @Test + public void substituteGenericPagedModel_springCloud_respectsCustomImportMappingClassName() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-paged-model.yaml", SPRING_CLOUD_LIBRARY, + springCloudPagedModelProps(), + configurator -> configurator.addImportMapping("PagedModel", "com.example.custom.MyPagedModel")); + + JavaFileAssert.assertThat(files.get("UserApi.java")) + .hasImports("com.example.custom.MyPagedModel") + .assertMethod("listUsers") + .hasReturnType("ResponseEntity>"); + } + + /** Common properties for substituteGenericPagedModel tests using spring-cloud. */ + private Map springCloudPagedModelProps() { + Map props = new HashMap<>(); + props.put(SpringCodegen.USE_TAGS, "true"); + props.put(SpringCodegen.SUBSTITUTE_GENERIC_PAGED_MODEL, "true"); + return props; + } + @DataProvider(name = "replaceOneOf") public Object[][] replaceOneOf() { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java index 95c8024deccc..9f17ad216091 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java @@ -5833,6 +5833,73 @@ private Map commonDeclarativeHttpInterfacePagedModelProps() { return props; } + // ------------------------------------------------------------------------- + // substituteGenericPagedModel — spring-cloud + // ------------------------------------------------------------------------- + + @Test + public void substituteGenericPagedModel_springCloud_replacesReturnTypeInOperation() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-paged-model.yaml", + springCloudKotlinPagedModelProps(), + new HashMap<>(), + configurator -> configurator.setLibrary(SPRING_CLOUD_LIBRARY)); + + File userApi = files.get("UserApi.kt"); + assertThat(userApi).isNotNull(); + String content = Files.readString(userApi.toPath()); + assertThat(content).contains("PagedModel"); + } + + @Test + public void substituteGenericPagedModel_springCloud_generatesPagedModelSupportingFile() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-paged-model.yaml", + springCloudKotlinPagedModelProps(), + new HashMap<>(), + configurator -> configurator.setLibrary(SPRING_CLOUD_LIBRARY)); + + assertThat(files).containsKey("PagedModel.kt"); + } + + @Test + public void substituteGenericPagedModel_springCloud_doesNotGeneratePagedModelFileWhenCustomMapping() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-paged-model.yaml", + springCloudKotlinPagedModelProps(), + new HashMap<>(), + configurator -> configurator + .setLibrary(SPRING_CLOUD_LIBRARY) + .addImportMapping("PagedModel", "com.example.custom.MyPagedModel")); + + assertThat(files).doesNotContainKey("PagedModel.kt"); + } + + @Test + public void substituteGenericPagedModel_springCloud_respectsCustomImportMappingClassName() throws IOException { + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-paged-model.yaml", + springCloudKotlinPagedModelProps(), + new HashMap<>(), + configurator -> configurator + .setLibrary(SPRING_CLOUD_LIBRARY) + .addImportMapping("PagedModel", "com.example.custom.MyPagedModel")); + + File userApi = files.get("UserApi.kt"); + assertThat(userApi).isNotNull(); + String content = Files.readString(userApi.toPath()); + assertThat(content).contains("MyPagedModel"); + assertThat(content).contains("import com.example.custom.MyPagedModel"); + } + + /** Common properties for substituteGenericPagedModel tests using spring-cloud. */ + private Map springCloudKotlinPagedModelProps() { + Map props = new HashMap<>(); + props.put(USE_TAGS, "true"); + props.put(SUBSTITUTE_GENERIC_PAGED_MODEL, "true"); + return props; + } + @Test(description = "oneOf with discriminator generates thin sealed interface with Jackson annotations") public void testOneOfWithDiscriminatorGeneratesThinInterface() throws IOException { File output = Files.createTempDirectory("test").toFile().getCanonicalFile();