diff --git a/docs/generators/kotlin-spring.md b/docs/generators/kotlin-spring.md index 0c4e836ad7fa..e03dfd102929 100644 --- a/docs/generators/kotlin-spring.md +++ b/docs/generators/kotlin-spring.md @@ -24,6 +24,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |apiSuffix|suffix for api classes| |Api| |artifactId|Generated artifact id (name of jar).| |openapi-spring| |artifactVersion|Generated artifact's package version.| |1.0.0| +|autoXSpringPaginated|Automatically add x-spring-paginated to operations that have 'page', 'size', and 'sort' query parameters. When enabled, operations with all three parameters will have Pageable support automatically applied. Operations with x-spring-paginated explicitly set to false will not be auto-detected.| |false| |basePackage|base package (invokerPackage) for generated code| |org.openapitools| |beanQualifiers|Whether to add fully-qualifier class names as bean qualifiers in @Component and @RestController annotations. May be used to prevent bean names clash if multiple generated libraries (contexts) added to single project.| |false| |configPackage|configuration package for generated code| |org.openapitools.configuration| @@ -73,12 +74,14 @@ These options may be applied as additional-properties (cli) or configOptions (pl |x-content-type|Specify custom value for 'Content-Type' header for operation|OPERATION|null |x-discriminator-value|Used with model inheritance to specify value for discriminator that identifies current model|MODEL| |x-field-extra-annotation|List of custom annotations to be added to property|FIELD, OPERATION_PARAMETER|null +|x-operation-extra-annotation|List of custom annotations to be added to operation|OPERATION|null |x-pattern-message|Add this property whenever you need to customize the invalidation error message for the regex pattern of a variable|FIELD, OPERATION_PARAMETER|null |x-size-message|Add this property whenever you need to customize the invalidation error message for the size or length of a variable|FIELD, OPERATION_PARAMETER|null |x-minimum-message|Add this property whenever you need to customize the invalidation error message for the minimum value of a variable|FIELD, OPERATION_PARAMETER|null |x-maximum-message|Add this property whenever you need to customize the invalidation error message for the maximum value of a variable|FIELD, OPERATION_PARAMETER|null |x-kotlin-implements|Ability to specify interfaces that model must implement|MODEL|empty array |x-kotlin-implements-fields|Specify attributes that are implemented by the interface(s) added via `x-kotlin-implements`|MODEL|empty array +|x-spring-paginated|Add `org.springframework.data.domain.Pageable` to controller method. Can be used to handle `page`, `size` and `sort` query parameters. If these query parameters are also specified in the operation spec, they will be removed from the controller method as their values can be obtained from the `Pageable` object.|OPERATION|false ## IMPORT MAPPING 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 98e1f003b019..55abde3ad6c5 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 @@ -45,6 +45,7 @@ import java.net.URL; import java.util.*; import java.util.regex.Matcher; +import java.util.stream.Collectors; import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER; import static org.openapitools.codegen.utils.StringUtils.camelize; @@ -95,6 +96,7 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen public static final String REQUEST_MAPPING_OPTION = "requestMappingMode"; public static final String USE_REQUEST_MAPPING_ON_CONTROLLER = "useRequestMappingOnController"; public static final String USE_REQUEST_MAPPING_ON_INTERFACE = "useRequestMappingOnInterface"; + public static final String AUTO_X_SPRING_PAGINATED = "autoXSpringPaginated"; @Getter public enum DeclarativeInterfaceReactiveMode { @@ -158,6 +160,7 @@ public String getDescription() { @Setter private boolean beanQualifiers = false; @Setter private DeclarativeInterfaceReactiveMode declarativeInterfaceReactiveMode = DeclarativeInterfaceReactiveMode.coroutines; @Setter private boolean useResponseEntity = true; + @Setter private boolean autoXSpringPaginated = false; @Getter @Setter protected boolean useSpringBoot3 = false; @@ -251,6 +254,7 @@ public KotlinSpringServerCodegen() { addOption(X_KOTLIN_IMPLEMENTS_FIELDS_SKIP, "A list of fields per schema name that should NOT be created with `override` keyword despite their presence in vendor extension `x-kotlin-implements-fields` for the schema. Example: yaml `xKotlinImplementsFieldsSkip: Pet: [photoUrls]` skips `override` for `photoUrls` in schema `Pet`", "empty map"); addOption(SCHEMA_IMPLEMENTS, "A map of single interface or a list of interfaces per schema name that should be implemented (serves similar purpose as `x-kotlin-implements`, but is fully decoupled from the api spec). Example: yaml `schemaImplements: {Pet: com.some.pack.WithId, Category: [com.some.pack.CategoryInterface], Dog: [com.some.pack.Canine, com.some.pack.OtherInterface]}` implements interfaces in schemas `Pet` (interface `com.some.pack.WithId`), `Category` (interface `com.some.pack.CategoryInterface`), `Dog`(interfaces `com.some.pack.Canine`, `com.some.pack.OtherInterface`)", "empty map"); addOption(SCHEMA_IMPLEMENTS_FIELDS, "A map of single field or a list of fields per schema name that should be prepended with `override` (serves similar purpose as `x-kotlin-implements-fields`, but is fully decoupled from the api spec). Example: yaml `schemaImplementsFields: {Pet: id, Category: [name, id], Dog: [bark, breed]}` marks fields to be prepended with `override` in schemas `Pet` (field `id`), `Category` (fields `name`, `id`) and `Dog` (fields `bark`, `breed`)", "empty map"); + addSwitch(AUTO_X_SPRING_PAGINATED, "Automatically add x-spring-paginated to operations that have 'page', 'size', and 'sort' query parameters. When enabled, operations with all three parameters will have Pageable support automatically applied. Operations with x-spring-paginated explicitly set to false will not be auto-detected.", autoXSpringPaginated); supportedLibraries.put(SPRING_BOOT, "Spring-boot Server application."); supportedLibraries.put(SPRING_CLOUD_LIBRARY, "Spring-Cloud-Feign client with Spring-Boot auto-configured settings."); @@ -447,6 +451,12 @@ public void processOpts() { additionalProperties.put(DOCUMENTATION_PROVIDER, DocumentationProvider.NONE); additionalProperties.put(ANNOTATION_LIBRARY, AnnotationLibrary.NONE); } + if (additionalProperties.containsKey(USE_SPRING_BOOT3)) { + this.setUseSpringBoot3(convertPropertyToBoolean(USE_SPRING_BOOT3)); + } + if (additionalProperties.containsKey(INCLUDE_HTTP_REQUEST_CONTEXT)) { + this.setIncludeHttpRequestContext(convertPropertyToBoolean(INCLUDE_HTTP_REQUEST_CONTEXT)); + } if (isModelMutable()) { typeMapping.put("array", "kotlin.collections.MutableList"); @@ -470,6 +480,14 @@ public void processOpts() { // used later in recursive import in postProcessingModels importMapping.put("com.fasterxml.jackson.annotation.JsonProperty", "com.fasterxml.jackson.annotation.JsonCreator"); + // Spring-specific import mappings for x-spring-paginated support + importMapping.put("ApiIgnore", "springfox.documentation.annotations.ApiIgnore"); + importMapping.put("ParameterObject", "org.springdoc.api.annotations.ParameterObject"); + importMapping.put("PageableAsQueryParam", "org.springdoc.core.converters.models.PageableAsQueryParam"); + if (useSpringBoot3) { + importMapping.put("ParameterObject", "org.springdoc.core.annotations.ParameterObject"); + } + if (!additionalProperties.containsKey(CodegenConstants.LIBRARY)) { additionalProperties.put(CodegenConstants.LIBRARY, library); } @@ -642,13 +660,10 @@ public void processOpts() { if (additionalProperties.containsKey(USE_TAGS)) { this.setUseTags(Boolean.parseBoolean(additionalProperties.get(USE_TAGS).toString())); } - - if (additionalProperties.containsKey(USE_SPRING_BOOT3)) { - this.setUseSpringBoot3(convertPropertyToBoolean(USE_SPRING_BOOT3)); - } - if (additionalProperties.containsKey(INCLUDE_HTTP_REQUEST_CONTEXT)) { - this.setIncludeHttpRequestContext(convertPropertyToBoolean(INCLUDE_HTTP_REQUEST_CONTEXT)); + if (additionalProperties.containsKey(AUTO_X_SPRING_PAGINATED) && library.equals(SPRING_BOOT)) { + this.setAutoXSpringPaginated(convertPropertyToBoolean(AUTO_X_SPRING_PAGINATED)); } + writePropertyBack(AUTO_X_SPRING_PAGINATED, autoXSpringPaginated); if (isUseSpringBoot3()) { if (DocumentationProvider.SPRINGFOX.equals(getDocumentationProvider())) { throw new IllegalArgumentException(DocumentationProvider.SPRINGFOX.getPropertyName() + " is not supported with Spring Boot > 3.x"); @@ -802,7 +817,7 @@ public void processOpts() { gradleWrapperPackage.replace(".", File.separator), "gradle-wrapper.jar")); } - apiTemplateFiles.put("apiInterface.mustache", "Client.kt"); + apiTemplateFiles.put("apiInterface.mustache", ".kt"); apiTestTemplateFiles.clear(); } @@ -872,6 +887,108 @@ public void addOperationToGroup(String tag, String resourcePath, Operation opera } } + /** + * Processes operations to support the x-spring-paginated vendor extension. + * + * When x-spring-paginated is set to true on an operation, this method: + * - Adds org.springframework.data.domain.Pageable parameter to the method signature + * - Removes the default Spring Data Web pagination query parameters (page, size, sort) + * - Adds appropriate imports (Pageable, ApiIgnore for springfox, ParameterObject for springdoc) + * + * Auto-detection (when autoXSpringPaginated is enabled): + * - Automatically detects operations with 'page', 'size', and 'sort' query parameters (case-sensitive) + * - Applies x-spring-paginated behavior to these operations automatically + * - Respects manual x-spring-paginated: false setting (manual override takes precedence) + * - Only applies when library is spring-boot + * + * Note: x-spring-paginated is ONLY applied for server-side libraries (spring-boot). + * Client libraries (spring-cloud, spring-declarative-http-interface) need actual query parameters + * to send over HTTP, so the extension is ignored for them. + * + * Parameter ordering in generated methods: + * 1. Regular OpenAPI parameters (allParams) + * 2. Optional HttpServletRequest/ServerWebExchange (if includeHttpRequestContext is enabled) + * 3. Pageable parameter (if x-spring-paginated is true and library is spring-boot) + * + * This implementation mirrors the behavior in SpringCodegen for consistency. + * + * @param path the operation path + * @param httpMethod the HTTP method + * @param operation the OpenAPI operation + * @param servers the list of servers + * @return the processed CodegenOperation + */ + @Override + public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, List servers) { + // #8315 Spring Data Web default query params recognized by Pageable + List defaultPageableQueryParams = Arrays.asList("page", "size", "sort"); + + CodegenOperation codegenOperation = super.fromOperation(path, httpMethod, operation, servers); + + // Check if operation has all three pagination query parameters (case-sensitive) + boolean hasParamsForPageable = codegenOperation.queryParams.stream() + .map(p -> p.baseName) + .collect(Collectors.toSet()) + .containsAll(defaultPageableQueryParams); + // Auto-detect pagination parameters and add x-spring-paginated if autoXSpringPaginated is enabled + // Only for spring-boot library, respect manual x-spring-paginated: false setting + if (SPRING_BOOT.equals(library) && autoXSpringPaginated) { + // Check if x-spring-paginated is not explicitly set to false + if (operation.getExtensions() == null || !Boolean.FALSE.equals(operation.getExtensions().get("x-spring-paginated"))) { + + + if (hasParamsForPageable) { + // Automatically add x-spring-paginated to the operation + if (operation.getExtensions() == null) { + operation.setExtensions(new HashMap<>()); + } + operation.getExtensions().put("x-spring-paginated", Boolean.TRUE); + codegenOperation.vendorExtensions.put("x-spring-paginated", Boolean.TRUE); + } + } + } + + // Only process x-spring-paginated for server-side libraries (spring-boot) + // Client libraries (spring-cloud, spring-declarative-http-interface) need actual query parameters for HTTP requests + if (SPRING_BOOT.equals(library)) { + // add Pageable import only if x-spring-paginated explicitly used AND it's a server library + // this allows to use a custom Pageable schema without importing Spring Pageable. + if (operation.getExtensions() != null && Boolean.TRUE.equals(operation.getExtensions().get("x-spring-paginated"))) { + importMapping.putIfAbsent("Pageable", "org.springframework.data.domain.Pageable"); + } + + // add org.springframework.data.domain.Pageable import when needed (server libraries only) + if (operation.getExtensions() != null && Boolean.TRUE.equals(operation.getExtensions().get("x-spring-paginated"))) { + codegenOperation.imports.add("Pageable"); + if (DocumentationProvider.SPRINGFOX.equals(getDocumentationProvider())) { + codegenOperation.imports.add("ApiIgnore"); + } + if (DocumentationProvider.SPRINGDOC.equals(getDocumentationProvider())) { + codegenOperation.imports.add("PageableAsQueryParam"); + // Prepend @PageableAsQueryParam to existing x-operation-extra-annotation if present + // Use getObjectAsStringList to properly handle both list and string formats: + // - YAML list: ['@Ann1', '@Ann2'] -> List of annotations + // - Single string: '@Ann1 @Ann2' -> Single-element list + // - Nothing/null -> Empty list + Object existingAnnotation = codegenOperation.vendorExtensions.get("x-operation-extra-annotation"); + List annotations = DefaultCodegen.getObjectAsStringList(existingAnnotation); + + // Prepend @PageableAsQueryParam to the beginning of the list + List updatedAnnotations = new ArrayList<>(); + updatedAnnotations.add("@PageableAsQueryParam"); + updatedAnnotations.addAll(annotations); + + codegenOperation.vendorExtensions.put("x-operation-extra-annotation", updatedAnnotations); + } + + // #8315 Remove matching Spring Data Web default query params if 'x-spring-paginated' with Pageable is used + codegenOperation.queryParams.removeIf(param -> defaultPageableQueryParams.contains(param.baseName)); + codegenOperation.allParams.removeIf(param -> param.isQueryParam && defaultPageableQueryParams.contains(param.baseName)); + } + } + return codegenOperation; + } + @Override public void preprocessOpenAPI(OpenAPI openAPI) { super.preprocessOpenAPI(openAPI); @@ -1117,12 +1234,14 @@ public List getSupportedVendorExtensions() { extensions.add(VendorExtension.X_CONTENT_TYPE); extensions.add(VendorExtension.X_DISCRIMINATOR_VALUE); extensions.add(VendorExtension.X_FIELD_EXTRA_ANNOTATION); + extensions.add(VendorExtension.X_OPERATION_EXTRA_ANNOTATION); extensions.add(VendorExtension.X_PATTERN_MESSAGE); extensions.add(VendorExtension.X_SIZE_MESSAGE); extensions.add(VendorExtension.X_MINIMUM_MESSAGE); extensions.add(VendorExtension.X_MAXIMUM_MESSAGE); extensions.add(VendorExtension.X_KOTLIN_IMPLEMENTS); extensions.add(VendorExtension.X_KOTLIN_IMPLEMENTS_FIELDS); + extensions.add(VendorExtension.X_SPRING_PAGINATED); return extensions; } diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache index 07d45a3e505c..284cb4aecedb 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/api.mustache @@ -84,6 +84,9 @@ class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) v authorizations = [{{#authMethods}}Authorization(value = "{{name}}"{{#isOAuth}}, scopes = [{{#scopes}}AuthorizationScope(scope = "{{scope}}", description = "{{description}}"){{^-last}}, {{/-last}}{{/scopes}}]{{/isOAuth}}){{^-last}}, {{/-last}}{{/authMethods}}]{{/hasAuthMethods}}) @ApiResponses( value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}{{/responses}}]){{/swagger1AnnotationLibrary}} + {{#vendorExtensions.x-operation-extra-annotation}} + {{{.}}} + {{/vendorExtensions.x-operation-extra-annotation}} @RequestMapping( method = [RequestMethod.{{httpMethod}}], // "{{#lambdaEscapeInNormalString}}{{{path}}}{{/lambdaEscapeInNormalString}}" @@ -95,7 +98,9 @@ class {{classname}}Controller({{#serviceInterface}}@Autowired(required = true) v ) {{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}} {{>queryParams}}{{>pathParams}}{{>headerParams}}{{>cookieParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, - {{/hasParams}}{{#swagger1AnnotationLibrary}}@ApiParam(hidden = true) {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true) {{/swagger2AnnotationLibrary}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#hasParams}} + {{/hasParams}}{{#swagger1AnnotationLibrary}}@ApiParam(hidden = true) {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true) {{/swagger2AnnotationLibrary}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, + {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, + {{/includeHttpRequestContext}}{{/hasParams}}{{#swagger1AnnotationLibrary}}@ApiParam(hidden = true) {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true) {{/swagger2AnnotationLibrary}}pageable: Pageable{{/vendorExtensions.x-spring-paginated}}{{#hasParams}} {{/hasParams}}): {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}} { return {{>returnValue}} } diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache index 5ae576a08bec..c317d43bf56e 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/apiDelegate.mustache @@ -34,7 +34,9 @@ interface {{classname}}Delegate { */ {{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}{{#isBodyParam}}Flow<{{{baseType}}}>{{/isBodyParam}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{/isArray}}{{/reactive}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, - {{/hasParams}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}): {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}}{{^skipDefaultDelegateInterface}} { + {{/hasParams}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, + {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, + {{/includeHttpRequestContext}}{{/hasParams}}pageable: Pageable{{/vendorExtensions.x-spring-paginated}}): {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}}{{^skipDefaultDelegateInterface}} { {{>methodBody}}{{! prevent indent}} }{{/skipDefaultDelegateInterface}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache index 67a9c8f4d096..3b130aadbc58 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/apiInterface.mustache @@ -99,6 +99,9 @@ interface {{classname}} { @ApiResponses( value = [{{#responses}}ApiResponse(code = {{{code}}}, message = "{{{message}}}"{{#baseType}}, response = {{{.}}}::class{{/baseType}}{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}}, {{/-last}}{{/responses}}] ){{/swagger1AnnotationLibrary}} + {{#vendorExtensions.x-operation-extra-annotation}} + {{{.}}} + {{/vendorExtensions.x-operation-extra-annotation}} @RequestMapping( method = [RequestMethod.{{httpMethod}}], // "{{#lambdaEscapeInNormalString}}{{{path}}}{{/lambdaEscapeInNormalString}}" @@ -110,13 +113,15 @@ interface {{classname}} { ) {{#reactive}}{{^isArray}}suspend {{/isArray}}{{#isArray}}{{^useFlowForArrayReturnType}}suspend {{/useFlowForArrayReturnType}}{{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}} {{>queryParams}}{{>pathParams}}{{>headerParams}}{{>cookieParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, - {{/hasParams}}{{#swagger1AnnotationLibrary}}@ApiParam(hidden = true) {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true) {{/swagger2AnnotationLibrary}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#hasParams}} + {{/hasParams}}{{#swagger1AnnotationLibrary}}@ApiParam(hidden = true) {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true) {{/swagger2AnnotationLibrary}}{{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, + {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, + {{/includeHttpRequestContext}}{{/hasParams}}{{#swagger1AnnotationLibrary}}@ApiParam(hidden = true) {{/swagger1AnnotationLibrary}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true) {{/swagger2AnnotationLibrary}}pageable: Pageable{{/vendorExtensions.x-spring-paginated}}{{#hasParams}} {{/hasParams}}): {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}}{{^skipDefaultApiInterface}} { {{^isDelegate}} return {{>returnValue}} {{/isDelegate}} {{#isDelegate}} - return getDelegate().{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#reactive}}exchange{{/reactive}}{{^reactive}}request{{/reactive}}{{/includeHttpRequestContext}}) + return getDelegate().{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#reactive}}exchange{{/reactive}}{{^reactive}}request{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, {{/includeHttpRequestContext}}{{/hasParams}}pageable{{/vendorExtensions.x-spring-paginated}}) {{/isDelegate}} }{{/skipDefaultApiInterface}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/api_test.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/api_test.mustache index eda2afcd0c18..8950633ae979 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/api_test.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/api_test.mustache @@ -35,7 +35,8 @@ class {{classname}}Test { val {{{paramName}}}: {{>optionalDataType}} = TODO() {{/allParams}} {{#includeHttpRequestContext}}val {{#reactive}}exchange: org.springframework.web.server.ServerWebExchange{{/reactive}}{{^reactive}}request: {{javaxPackage}}.servlet.http.HttpServletRequest{{/reactive}} = TODO(){{/includeHttpRequestContext}} - val response: {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}} = api.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#reactive}}exchange{{/reactive}}{{^reactive}}request{{/reactive}}{{/includeHttpRequestContext}}) + {{#vendorExtensions.x-spring-paginated}}val pageable: Pageable = TODO(){{/vendorExtensions.x-spring-paginated}} + val response: {{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}} = api.{{operationId}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#reactive}}exchange{{/reactive}}{{^reactive}}request{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, {{/includeHttpRequestContext}}{{/hasParams}}pageable{{/vendorExtensions.x-spring-paginated}}) // TODO: test validations } diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/buildGradle-sb3-Kts.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/buildGradle-sb3-Kts.mustache index 1cca74ad1ede..5e33e8653404 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/buildGradle-sb3-Kts.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/buildGradle-sb3-Kts.mustache @@ -49,6 +49,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") {{#useBeanValidation}} implementation("jakarta.validation:jakarta.validation-api"){{/useBeanValidation}} implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/buildGradleKts.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/buildGradleKts.mustache index f3c3109f4e29..7b72c8568112 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/buildGradleKts.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/buildGradleKts.mustache @@ -56,6 +56,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") {{#useBeanValidation}} implementation("javax.validation:validation-api"){{/useBeanValidation}} implementation("javax.annotation:javax.annotation-api:1.3.2") diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/pom-sb3.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/pom-sb3.mustache index a933ddd54863..79b278b33217 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/pom-sb3.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/pom-sb3.mustache @@ -129,6 +129,10 @@ kotlinx-coroutines-reactor ${kotlinx-coroutines.version} {{/reactive}} + + org.springframework.data + spring-data-commons + {{#springDocDocumentationProvider}} {{#useSwaggerUI}} diff --git a/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/pom.mustache b/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/pom.mustache index 76dfa9767896..1aaf9f6158ec 100644 --- a/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/pom.mustache +++ b/modules/openapi-generator/src/main/resources/kotlin-spring/libraries/spring-boot/pom.mustache @@ -116,6 +116,10 @@ kotlinx-coroutines-reactor ${kotlinx-coroutines.version} {{/reactive}} + + org.springframework.data + spring-data-commons + {{#springDocDocumentationProvider}} {{#useSwaggerUI}} 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 12d52f58de59..353c5141c910 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 @@ -1215,7 +1215,7 @@ public void generateHttpInterfaceReactiveWithReactorResponseEntity() throws Exce generator.opts(input).generate(); - Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApiClient.kt"); + Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApi.kt"); assertFileContains( path, "import reactor.core.publisher.Flux\n" @@ -1287,7 +1287,7 @@ public void generateHttpInterfaceReactiveWithCoroutinesResponseEntity() throws E generator.opts(input).generate(); - Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApiClient.kt"); + Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApi.kt"); assertFileContains( path, " suspend fun getInventory(\n" @@ -1329,7 +1329,7 @@ public void generateHttpInterfaceReactiveWithReactor() throws Exception { generator.opts(input).generate(); - Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApiClient.kt"); + Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApi.kt"); assertFileContains( path, "import reactor.core.publisher.Flux\n" @@ -1377,7 +1377,7 @@ public void generateHttpInterfaceReactiveWithCoroutines() throws Exception { generator.opts(input).generate(); - Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApiClient.kt"); + Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApi.kt"); assertFileContains( path, " suspend fun getInventory(\n" @@ -1418,7 +1418,7 @@ public void generateHttpInterfaceResponseEntity() throws Exception { generator.opts(input).generate(); - Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApiClient.kt"); + Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApi.kt"); assertFileContains( path, " fun getInventory(\n" @@ -1463,7 +1463,7 @@ public void generateHttpInterface() throws Exception { generator.opts(input).generate(); - Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApiClient.kt"); + Path path = Paths.get(outputPath + "/src/main/kotlin/org/openapitools/api/StoreApi.kt"); // Note: We cannot use property placeholders as HttpServiceProxyFactory does not resolve them by default. assertFileContains( path, @@ -3666,6 +3666,445 @@ public void testXMinimumMessageAndXMaximumMessage_long() throws IOException { .hasNotAttributes(List.of("message")); } + @Test + public void springPaginatedWithSpringDoc() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + assertFileContains(petApi.toPath(), "@Parameter(hidden = true) pageable: Pageable"); + } + + @Test + public void springPaginatedWithSpringDocAndSpringBoot3() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(USE_SPRING_BOOT3, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + } + + @Test + public void springPaginatedWithSpringFox() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springfox"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApi.toPath(), "import springfox.documentation.annotations.ApiIgnore"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + assertFileContains(petApi.toPath(), "@ApiParam(hidden = true) pageable: Pageable"); + } + + @Test + public void springPaginatedQueryParamsRemoved() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Test that page, size, and sort query params are removed but other params remain + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "tags: kotlin.collections.List"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + assertFileNotContains(petApi.toPath(), "page:"); + assertFileNotContains(petApi.toPath(), "sort:"); + // Header param size should remain, query param size should be removed + assertFileContains(petApi.toPath(), "@RequestHeader(value = \"size\""); + } + + @Test + public void springPaginatedWithReactive() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(REACTIVE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Test that pageable works in reactive mode + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + } + + @Test + public void springPaginatedWithIncludeHttpRequestContext() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(INCLUDE_HTTP_REQUEST_CONTEXT, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Test that pageable comes after request parameter + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApi.toPath(), "request: javax.servlet.http.HttpServletRequest"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + } + + @Test + public void springPaginatedWithReactiveAndIncludeHttpRequestContext() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(REACTIVE, "true"); + additionalProperties.put(INCLUDE_HTTP_REQUEST_CONTEXT, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Test that pageable comes after exchange parameter in reactive mode + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApi.toPath(), "exchange: org.springframework.web.server.ServerWebExchange"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + } + + @Test + public void springPaginatedWithDelegate() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(DELEGATE_PATTERN, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Test that pageable is in delegate interface + File petApiDelegate = files.get("PetApiDelegate.kt"); + assertFileContains(petApiDelegate.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApiDelegate.toPath(), "pageable: Pageable"); + } + + @Test + public void customPageableSchemaNotOverridden_issue13052() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/bugs/issue_13052.yaml", additionalProperties); + + // Custom Pageable model should be used instead of Spring's Pageable + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.openapitools.model.Pageable"); + assertFileNotContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileNotContains(petApi.toPath(), "import org.springdoc.core.annotations.ParameterObject"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + } + + @Test + public void springPaginatedWithNoDocumentationProvider() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "none"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Pageable should be added but no annotation imports + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + assertFileNotContains(petApi.toPath(), "import springfox.documentation.annotations.ApiIgnore"); + assertFileNotContains(petApi.toPath(), "import org.springdoc.api.annotations.ParameterObject"); + assertFileNotContains(petApi.toPath(), "@ApiIgnore pageable"); + assertFileNotContains(petApi.toPath(), "@ParameterObject pageable"); + } + + @Test + public void springPaginatedWithSwagger1AnnotationLibrary() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springfox"); + additionalProperties.put(ANNOTATION_LIBRARY, "swagger1"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Test with swagger1 annotations + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petApi.toPath(), "import springfox.documentation.annotations.ApiIgnore"); + assertFileContains(petApi.toPath(), "@ApiParam(hidden = true) pageable: Pageable"); + } + + @Test + public void springPaginatedWithSpringDocUsesPageableAsQueryParam() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Verify @PageableAsQueryParam annotation is present at method level + assertFileContains(petApi.toPath(), "import org.springdoc.core.converters.models.PageableAsQueryParam"); + assertFileContains(petApi.toPath(), "@PageableAsQueryParam"); + + // Verify Pageable parameter has @Parameter(hidden = true) + assertFileContains(petApi.toPath(), "@Parameter(hidden = true) pageable: Pageable"); + + // Verify the annotation appears before @RequestMapping for findPetsByStatus + int findPetsByStatusStart = content.indexOf("fun findPetsByStatus("); + Assert.assertTrue(findPetsByStatusStart > 0, "findPetsByStatus method should exist"); + + String methodBlock = content.substring(Math.max(0, findPetsByStatusStart - 1000), findPetsByStatusStart); + int pageableAsQueryParamPos = methodBlock.lastIndexOf("@PageableAsQueryParam"); + int requestMappingPos = methodBlock.lastIndexOf("@RequestMapping"); + + Assert.assertTrue(pageableAsQueryParamPos > 0, "@PageableAsQueryParam should be present before method"); + Assert.assertTrue(requestMappingPos > pageableAsQueryParamPos, + "@PageableAsQueryParam should appear before @RequestMapping"); + + // Verify page, size, sort parameters are NOT in the method signature + String methodSignature = content.substring(findPetsByStatusStart, + content.indexOf("): ResponseEntity", findPetsByStatusStart)); + Assert.assertFalse(methodSignature.contains("page:"), + "page parameter should be removed from method signature"); + Assert.assertFalse(methodSignature.contains("size:") && methodSignature.contains("@RequestParam"), + "size query parameter should be removed from method signature"); + Assert.assertFalse(methodSignature.contains("sort:"), + "sort parameter should be removed from method signature"); + } + + @Test + public void springPaginatedNoParamsNoContext() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Test operation listAllPets which has no parameters except pageable + File petApi = files.get("PetApi.kt"); + assertFileContains(petApi.toPath(), "fun listAllPets(@Parameter(hidden = true) pageable: Pageable)"); + } + + @Test + public void springPaginatedWithSpringDocPrependsToExistingAnnotation() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Verify that both annotations are imported + assertFileContains(petApi.toPath(), "import org.springdoc.core.converters.models.PageableAsQueryParam"); + assertFileContains(petApi.toPath(), "import org.springframework.validation.annotation.Validated"); + + // Find the listAllPets method + int listAllPetsStart = content.indexOf("fun listAllPets("); + Assert.assertTrue(listAllPetsStart > 0, "listAllPets method should exist"); + + // Check the annotations appear before the method in the correct order + String methodBlock = content.substring(Math.max(0, listAllPetsStart - 1000), listAllPetsStart); + + int pageableAsQueryParamPos = methodBlock.lastIndexOf("@PageableAsQueryParam"); + int validatedPos = methodBlock.lastIndexOf("@org.springframework.validation.annotation.Validated"); + int requestMappingPos = methodBlock.lastIndexOf("@RequestMapping"); + + Assert.assertTrue(pageableAsQueryParamPos > 0, "@PageableAsQueryParam should be present before listAllPets method"); + Assert.assertTrue(validatedPos > 0, "@Validated should be present before listAllPets method"); + + // Verify @PageableAsQueryParam comes before @Validated (prepended) + Assert.assertTrue(pageableAsQueryParamPos < validatedPos, + "@PageableAsQueryParam should be prepended (appear before) existing @Validated annotation"); + + // Verify both annotations come before @RequestMapping + Assert.assertTrue(validatedPos < requestMappingPos, + "Both annotations should appear before @RequestMapping"); + + // Verify the Pageable parameter still has @Parameter(hidden = true) + assertFileContains(petApi.toPath(), "@Parameter(hidden = true) pageable: Pageable"); + } + + @Test + public void springPaginatedWithSpringDocPrependsToExistingAnnotationArray() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Verify that PageableAsQueryParam is imported + assertFileContains(petApi.toPath(), "import org.springdoc.core.converters.models.PageableAsQueryParam"); + + // Find the findPetsByStatus method + int findPetsByStatusStart = content.indexOf("fun findPetsByStatus("); + Assert.assertTrue(findPetsByStatusStart > 0, "findPetsByStatus method should exist"); + + // Check the annotations appear before the method in the correct order + String methodBlock = content.substring(Math.max(0, findPetsByStatusStart - 1500), findPetsByStatusStart); + + int pageableAsQueryParamPos = methodBlock.lastIndexOf("@PageableAsQueryParam"); + int validatedPos = methodBlock.lastIndexOf("@org.springframework.validation.annotation.Validated"); + int preAuthorizePos = methodBlock.lastIndexOf("@org.springframework.security.access.prepost.PreAuthorize"); + int requestMappingPos = methodBlock.lastIndexOf("@RequestMapping"); + + Assert.assertTrue(pageableAsQueryParamPos > 0, "@PageableAsQueryParam should be present before findPetsByStatus method"); + Assert.assertTrue(validatedPos > 0, "@Validated should be present before findPetsByStatus method"); + Assert.assertTrue(preAuthorizePos > 0, "@PreAuthorize should be present before findPetsByStatus method"); + + // Verify @PageableAsQueryParam comes first (prepended to the array) + Assert.assertTrue(pageableAsQueryParamPos < validatedPos, + "@PageableAsQueryParam should be prepended (appear before) @Validated annotation"); + + // Verify the original array order is preserved after @PageableAsQueryParam + Assert.assertTrue(validatedPos < preAuthorizePos, + "@Validated should appear before @PreAuthorize (original array order preserved)"); + + // Verify all annotations come before @RequestMapping + Assert.assertTrue(preAuthorizePos < requestMappingPos, + "All annotations should appear before @RequestMapping"); + + // Verify the Pageable parameter still has @Parameter(hidden = true) + assertFileContains(petApi.toPath(), "@Parameter(hidden = true) pageable: Pageable"); + } + + @Test + public void springPaginatedMixedOperations() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + + // Operation with x-spring-paginated should have Pageable + assertFileContains(petApi.toPath(), "fun findPetsByStatus("); + assertFileContains(petApi.toPath(), "pageable: Pageable"); + + // Operation without x-spring-paginated should NOT have Pageable + assertFileContains(petApi.toPath(), "fun addPet("); + // Verify addPet doesn't have pageable (it has body param only) + String content = Files.readString(petApi.toPath()); + String addPetMethod = content.substring( + content.indexOf("fun addPet("), + content.indexOf(")", content.indexOf("fun addPet(")) + 1 + ); + Assert.assertFalse(addPetMethod.contains("pageable"), + "addPet should not have pageable parameter"); + } + + @Test + public void springPaginatedWithServiceInterface() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(SERVICE_INTERFACE, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Test that pageable is in service interface + File petService = files.get("PetService.kt"); + if (petService != null) { + assertFileContains(petService.toPath(), "import org.springframework.data.domain.Pageable"); + assertFileContains(petService.toPath(), "pageable: Pageable"); + } + } + + @Test + public void springPaginatedParameterOrdering() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(INCLUDE_HTTP_REQUEST_CONTEXT, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Verify exact parameter ordering: allParams -> request -> pageable + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Find findPetsByStatus method + int methodStart = content.indexOf("fun findPetsByStatus("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + // Verify order: status param comes before request, request comes before pageable + int statusPos = methodSignature.indexOf("status:"); + int requestPos = methodSignature.indexOf("request:"); + int pageablePos = methodSignature.indexOf("pageable:"); + + Assert.assertTrue(statusPos > 0, "status parameter should exist"); + Assert.assertTrue(requestPos > statusPos, "request should come after status"); + Assert.assertTrue(pageablePos > requestPos, "pageable should come after request"); + } + + @Test + public void springPaginatedDelegateCallPassesPageable() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(DELEGATE_PATTERN, "true"); + additionalProperties.put(INTERFACE_ONLY, "false"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml", additionalProperties); + + // Verify that interface method calls delegate with pageable parameter + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Check for delegate call pattern with pageable + if (content.contains("getDelegate().findPetsByStatus")) { + assertFileContains(petApi.toPath(), "getDelegate().findPetsByStatus("); + assertFileContains(petApi.toPath(), "pageable)"); + } + } + private Map generateFromContract(String url) throws IOException { return generateFromContract(url, new HashMap<>(), new HashMap<>()); } @@ -3716,4 +4155,313 @@ private Map generateFromContract( return generator.opts(input).generate().stream() .collect(Collectors.toMap(File::getName, Function.identity())); } + + // ========== AUTO X-SPRING-PAGINATED TESTS ========== + + @Test + public void autoXSpringPaginatedDetectsAllThreeParams() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Operation with all three params (page, size, sort) should have Pageable auto-detected + assertFileContains(petApi.toPath(), "fun findPetsWithAutoDetect("); + assertFileContains(petApi.toPath(), "import org.springframework.data.domain.Pageable"); + + // Extract findPetsWithAutoDetect method + int methodStart = content.indexOf("fun findPetsWithAutoDetect("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + // Should have pageable parameter + Assert.assertTrue(methodSignature.contains("pageable: Pageable"), + "findPetsWithAutoDetect should have pageable parameter when autoXSpringPaginated is enabled"); + + // Should NOT have page, size, sort query params (they should be removed) + Assert.assertFalse(methodSignature.contains("page:"), + "page query param should be removed when pageable is added"); + Assert.assertFalse(methodSignature.contains("size:"), + "size query param should be removed when pageable is added"); + Assert.assertFalse(methodSignature.contains("sort:"), + "sort query param should be removed when pageable is added"); + + // Should still have the status parameter + Assert.assertTrue(methodSignature.contains("status:"), + "status parameter should remain"); + } + + @Test + public void autoXSpringPaginatedNoDetectionWhenMissingPage() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Operation missing 'page' param should NOT have Pageable + int methodStart = content.indexOf("fun findPetsMissingPage("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertFalse(methodSignature.contains("pageable: Pageable"), + "findPetsMissingPage should NOT have pageable when 'page' param is missing"); + + // Should still have the other params + Assert.assertTrue(methodSignature.contains("size:"), + "size param should remain"); + Assert.assertTrue(methodSignature.contains("sort:"), + "sort param should remain"); + } + + @Test + public void autoXSpringPaginatedNoDetectionWhenMissingSize() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Operation missing 'size' param should NOT have Pageable + int methodStart = content.indexOf("fun findPetsMissingSize("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertFalse(methodSignature.contains("pageable: Pageable"), + "findPetsMissingSize should NOT have pageable when 'size' param is missing"); + + // Should still have the other params + Assert.assertTrue(methodSignature.contains("page:"), + "page param should remain"); + Assert.assertTrue(methodSignature.contains("sort:"), + "sort param should remain"); + } + + @Test + public void autoXSpringPaginatedNoDetectionWhenMissingSort() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Operation missing 'sort' param should NOT have Pageable + int methodStart = content.indexOf("fun findPetsMissingSort("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertFalse(methodSignature.contains("pageable: Pageable"), + "findPetsMissingSort should NOT have pageable when 'sort' param is missing"); + + // Should still have the other params + Assert.assertTrue(methodSignature.contains("page:"), + "page param should remain"); + Assert.assertTrue(methodSignature.contains("size:"), + "size param should remain"); + } + + @Test + public void autoXSpringPaginatedManualFalseTakesPrecedence() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Operation with x-spring-paginated: false should NOT have Pageable (manual override) + int methodStart = content.indexOf("fun findPetsManualFalse("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertFalse(methodSignature.contains("pageable: Pageable"), + "findPetsManualFalse should NOT have pageable when x-spring-paginated is explicitly set to false"); + + // Should still have all three params + Assert.assertTrue(methodSignature.contains("page:"), + "page param should remain when x-spring-paginated: false"); + Assert.assertTrue(methodSignature.contains("size:"), + "size param should remain when x-spring-paginated: false"); + Assert.assertTrue(methodSignature.contains("sort:"), + "sort param should remain when x-spring-paginated: false"); + } + + @Test + public void autoXSpringPaginatedCaseSensitiveMatching() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Operation with Page, Size, Sort (capitalized) should NOT match + int methodStart = content.indexOf("fun findPetsCaseSensitive("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertFalse(methodSignature.contains("pageable: Pageable"), + "findPetsCaseSensitive should NOT have pageable with capitalized param names (case-sensitive)"); + + // Should still have all three params with capital letters + Assert.assertTrue(methodSignature.contains("page:") || methodSignature.contains("Page:"), + "Page param should remain"); + } + + @Test + public void autoXSpringPaginatedOnlyForSpringBoot() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + // Test with spring-cloud library (should NOT auto-detect) + Map files = generateFromContract( + "src/test/resources/3_0/spring/petstore-auto-paginated.yaml", + additionalProperties, + new HashMap<>(), + configurator -> configurator.setLibrary("spring-cloud") + ); + + File petApi = files.get("PetApiClient.kt"); + if (petApi != null) { + String content = Files.readString(petApi.toPath()); + + // For spring-cloud, should NOT have Pageable even with auto-detect enabled + Assert.assertFalse(content.contains("pageable: Pageable"), + "spring-cloud library should NOT auto-detect pageable (needs actual query params for HTTP)"); + + // Should have all three query params + int methodStart = content.indexOf("fun findPetsWithAutoDetect("); + if (methodStart >= 0) { + int methodEnd = content.indexOf("): ", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertTrue(methodSignature.contains("page") || methodSignature.contains("@Query"), + "spring-cloud should keep query parameters"); + } + } + } + + @Test + public void autoXSpringPaginatedDisabledByDefault() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + // NOT setting AUTO_X_SPRING_PAGINATED (should default to false) + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Without AUTO_X_SPRING_PAGINATED, should NOT auto-detect + int methodStart = content.indexOf("fun findPetsWithAutoDetect("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertFalse(methodSignature.contains("pageable: Pageable"), + "Should NOT have pageable when autoXSpringPaginated is not enabled (default: false)"); + + // Should have all three query params + Assert.assertTrue(methodSignature.contains("page:"), + "page param should remain when auto-detect is disabled"); + Assert.assertTrue(methodSignature.contains("size:"), + "size param should remain when auto-detect is disabled"); + Assert.assertTrue(methodSignature.contains("sort:"), + "sort param should remain when auto-detect is disabled"); + } + + @Test + public void autoXSpringPaginatedWorksWithManualTrue() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Operation with manual x-spring-paginated: true should still work + int methodStart = content.indexOf("fun findPetsManualTrue("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertTrue(methodSignature.contains("pageable: Pageable"), + "findPetsManualTrue should have pageable (manual x-spring-paginated: true)"); + + // Query params should be removed + Assert.assertFalse(methodSignature.contains("page:"), + "page param should be removed"); + Assert.assertFalse(methodSignature.contains("size:"), + "size param should be removed"); + Assert.assertFalse(methodSignature.contains("sort:"), + "sort param should be removed"); + } + + @Test + public void autoXSpringPaginatedNoParamsDoesNotDetect() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(DOCUMENTATION_PROVIDER, "springdoc"); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, "true"); + additionalProperties.put(AUTO_X_SPRING_PAGINATED, "true"); + + Map files = generateFromContract("src/test/resources/3_0/spring/petstore-auto-paginated.yaml", additionalProperties); + + File petApi = files.get("PetApi.kt"); + String content = Files.readString(petApi.toPath()); + + // Operation with no params should NOT have Pageable + int methodStart = content.indexOf("fun findPetsNoParams("); + int methodEnd = content.indexOf("): ResponseEntity", methodStart); + String methodSignature = content.substring(methodStart, methodEnd); + + Assert.assertFalse(methodSignature.contains("pageable: Pageable"), + "findPetsNoParams should NOT have pageable when there are no pagination params"); + } } + + diff --git a/modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-with-x-kotlin-implements.yaml b/modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-with-x-kotlin-implements.yaml index 276e8595e33c..e3a8f414e755 100644 --- a/modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-with-x-kotlin-implements.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/kotlin/petstore-with-x-kotlin-implements.yaml @@ -486,6 +486,400 @@ paths: description: Invalid username supplied '404': description: User not found + # Comprehensive x-spring-paginated test endpoints + /pet/paginated/all: + get: + tags: + - pet + summary: List all pets with pagination (only pagination params) + description: Tests x-spring-paginated with ONLY page/size/sort params that will be removed + operationId: listAllPetsPaginated + parameters: + - name: page + in: query + description: Page number - will be removed when x-spring-paginated is true + schema: + type: integer + format: int32 + default: 0 + - name: size + in: query + description: Page size - will be removed when x-spring-paginated is true + schema: + type: integer + format: int32 + default: 20 + - name: sort + in: query + description: Sort order - will be removed when x-spring-paginated is true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + x-spring-paginated: true + /pet/paginated/withParams: + get: + tags: + - pet + summary: List pets with filtering and pagination + description: Tests x-spring-paginated with regular params + pagination params + operationId: listPetsWithFilterPaginated + parameters: + - name: status + in: query + description: Filter by status - will be kept + schema: + type: string + enum: + - available + - pending + - sold + - name: name + in: query + description: Filter by name - will be kept + schema: + type: string + - name: page + in: query + description: Page number - will be removed + schema: + type: integer + format: int32 + - name: size + in: query + description: Page size - will be removed + schema: + type: integer + format: int32 + - name: sort + in: query + description: Sort order - will be removed + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + x-spring-paginated: true + /pet/paginated/headerSize: + get: + tags: + - pet + summary: List pets with header param named 'size' + description: Tests that header param 'size' is preserved while query param 'size' is removed + operationId: listPetsWithHeaderSize + parameters: + - name: size + in: header + description: Size header - must NOT be removed (different from query param) + schema: + type: string + - name: category + in: query + description: Filter by category - will be kept + schema: + type: string + - name: page + in: query + description: Page number - will be removed + schema: + type: integer + - name: size + in: query + description: Page size query param - will be removed + schema: + type: integer + - name: sort + in: query + description: Sort order - will be removed + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + x-spring-paginated: true + /pet/paginated/noPagination: + get: + tags: + - pet + summary: List pets WITHOUT x-spring-paginated + description: Tests that operations without x-spring-paginated keep all params + operationId: listPetsNoPagination + parameters: + - name: status + in: query + description: Filter by status + schema: + type: string + - name: page + in: query + description: Page number - will be KEPT (no x-spring-paginated) + schema: + type: integer + - name: size + in: query + description: Page size - will be KEPT (no x-spring-paginated) + schema: + type: integer + - name: sort + in: query + description: Sort order - will be KEPT (no x-spring-paginated) + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + # NO x-spring-paginated - all params should remain + /pet/paginated/{ownerId}: + get: + tags: + - pet + summary: List pets by owner with pagination + description: Tests x-spring-paginated with path param + query params + operationId: listPetsByOwnerPaginated + parameters: + - name: ownerId + in: path + description: Owner ID - will be kept + required: true + schema: + type: integer + format: int64 + - name: includeAdopted + in: query + description: Include adopted pets - will be kept + schema: + type: boolean + default: false + - name: page + in: query + description: Page number - will be removed + schema: + type: integer + - name: size + in: query + description: Page size - will be removed + schema: + type: integer + - name: sort + in: query + description: Sort order - will be removed + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + x-spring-paginated: true + /pet/paginated/multiParam: + get: + tags: + - pet + summary: List pets with many query params + description: Tests x-spring-paginated removes only page/size/sort + operationId: listPetsMultipleParams + parameters: + - name: name + in: query + description: Filter by name - will be kept + schema: + type: string + - name: minAge + in: query + description: Minimum age - will be kept + schema: + type: integer + - name: maxAge + in: query + description: Maximum age - will be kept + schema: + type: integer + - name: tags + in: query + description: Filter by tags - will be kept + schema: + type: array + items: + type: string + - name: page + in: query + description: Page number - will be removed + schema: + type: integer + - name: size + in: query + description: Page size - will be removed + schema: + type: integer + - name: sort + in: query + description: Sort order - will be removed + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + x-spring-paginated: true + /pet/paginated/partialParams: + get: + tags: + - pet + summary: List pets with only some pagination params + description: Tests x-spring-paginated with only page and size (no sort) + operationId: listPetsPartialPagination + parameters: + - name: status + in: query + description: Filter by status - will be kept + schema: + type: string + - name: page + in: query + description: Page number - will be removed + schema: + type: integer + - name: size + in: query + description: Page size - will be removed + schema: + type: integer + # Intentionally NO 'sort' parameter + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + x-spring-paginated: true + /pet/paginated/pathOnly: + get: + tags: + - pet + summary: List pets with path and header params only + description: Tests x-spring-paginated with no query params except pagination + operationId: listPetsByIdPaginated + parameters: + - name: X-Request-ID + in: header + description: Request ID for tracing + schema: + type: string + - name: page + in: query + description: Page number - will be removed + schema: + type: integer + - name: size + in: query + description: Page size - will be removed + schema: + type: integer + - name: sort + in: query + description: Sort order - will be removed + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + x-spring-paginated: true + /pet/paginated/mixed: + get: + tags: + - pet + summary: Mixed params - path, header, query, and pagination + description: Comprehensive test with all parameter types + operationId: listPetsMixedParams + parameters: + - name: Authorization + in: header + description: Authorization header + schema: + type: string + - name: X-Tenant-ID + in: header + description: Tenant ID + schema: + type: string + - name: status + in: query + description: Status filter + schema: + type: string + - name: includeInactive + in: query + description: Include inactive pets + schema: + type: boolean + - name: page + in: query + description: Page number - will be removed + schema: + type: integer + - name: size + in: query + description: Page size - will be removed + schema: + type: integer + - name: sort + in: query + description: Sort order - will be removed + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + x-spring-paginated: true components: securitySchemes: diff --git a/modules/openapi-generator/src/test/resources/3_0/spring/petstore-auto-paginated.yaml b/modules/openapi-generator/src/test/resources/3_0/spring/petstore-auto-paginated.yaml new file mode 100644 index 000000000000..15228bab1ba0 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/spring/petstore-auto-paginated.yaml @@ -0,0 +1,267 @@ +openapi: 3.0.1 +info: + title: OpenAPI Petstore - Auto Pagination Test + description: Test spec for auto x-spring-paginated detection + version: 1.0.0 +servers: + - url: http://petstore.swagger.io/v2 +tags: + - name: pet + description: Everything about your Pets +paths: + /pet/autoDetect: + get: + tags: + - pet + summary: Test auto-detection with all three params + description: Should auto-detect pagination params (page, size, sort) + operationId: findPetsWithAutoDetect + parameters: + - name: status + in: query + description: Status filter + schema: + type: string + - name: page + in: query + description: Page number + schema: + type: integer + default: 0 + - name: size + in: query + description: Page size + schema: + type: integer + default: 20 + - name: sort + in: query + description: Sort order + schema: + type: string + default: "id,asc" + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + /pet/missingPage: + get: + tags: + - pet + summary: Test with missing page param + description: Should NOT auto-detect (missing page) + operationId: findPetsMissingPage + parameters: + - name: size + in: query + description: Page size + schema: + type: integer + - name: sort + in: query + description: Sort order + schema: + type: string + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + /pet/missingSize: + get: + tags: + - pet + summary: Test with missing size param + description: Should NOT auto-detect (missing size) + operationId: findPetsMissingSize + parameters: + - name: page + in: query + description: Page number + schema: + type: integer + - name: sort + in: query + description: Sort order + schema: + type: string + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + /pet/missingSort: + get: + tags: + - pet + summary: Test with missing sort param + description: Should NOT auto-detect (missing sort) + operationId: findPetsMissingSort + parameters: + - name: page + in: query + description: Page number + schema: + type: integer + - name: size + in: query + description: Page size + schema: + type: integer + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + /pet/manualFalse: + get: + tags: + - pet + summary: Test manual x-spring-paginated false + description: Should NOT auto-detect (manual override to false) + operationId: findPetsManualFalse + x-spring-paginated: false + parameters: + - name: page + in: query + description: Page number + schema: + type: integer + - name: size + in: query + description: Page size + schema: + type: integer + - name: sort + in: query + description: Sort order + schema: + type: string + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + /pet/caseSensitive: + get: + tags: + - pet + summary: Test case-sensitive matching + description: Should NOT auto-detect (Page, Size, Sort with capitals) + operationId: findPetsCaseSensitive + parameters: + - name: Page + in: query + description: Page number (capital P) + schema: + type: integer + - name: Size + in: query + description: Page size (capital S) + schema: + type: integer + - name: Sort + in: query + description: Sort order (capital S) + schema: + type: string + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + /pet/manualTrue: + get: + tags: + - pet + summary: Test manual x-spring-paginated true + description: Should have pageable (manual x-spring-paginated true) + operationId: findPetsManualTrue + x-spring-paginated: true + parameters: + - name: status + in: query + description: Status filter + schema: + type: string + - name: page + in: query + description: Page number + schema: + type: integer + - name: size + in: query + description: Page size + schema: + type: integer + - name: sort + in: query + description: Sort order + schema: + type: string + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + /pet/noParams: + get: + tags: + - pet + summary: Test with no pagination params + description: Should NOT auto-detect (no pagination params) + operationId: findPetsNoParams + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' +components: + schemas: + Pet: + type: object + required: + - name + properties: + id: + type: integer + format: int64 + name: + type: string + status: + type: string + description: pet status in the store + diff --git a/modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml b/modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml index dcf0075a21f0..5b37c7e65a73 100644 --- a/modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-spring-pageable.yaml @@ -117,6 +117,9 @@ paths: - write:pets - read:pets x-spring-paginated: true + x-operation-extra-annotation: + - "@org.springframework.validation.annotation.Validated" + - "@org.springframework.security.access.prepost.PreAuthorize(\"hasRole('ADMIN')\")" /pet/findByTags: get: tags: @@ -191,6 +194,59 @@ paths: - write:pets - read:pets x-spring-paginated: true + /pet/all: + get: + tags: + - pet + summary: List all pets + description: Returns all pets with pagination support + operationId: listAllPets + parameters: + - name: page + in: query + description: "The page number to return. Test QueryParam for issue #8315 - must be removed when x-spring-paginated:true is used." + required: false + schema: + type: integer + format: int32 + default: 0 + - name: size + in: query + description: "The number of items to return per page. Test QueryParam for issue #8315 - must be removed when x-spring-paginated:true is used." + required: false + schema: + type: integer + format: int32 + default: 20 + - name: sort + in: query + description: "The sort order. Test QueryParam for issue #8315 - must be removed when x-spring-paginated:true is used." + required: false + schema: + type: string + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + 400: + description: Invalid status value + content: {} + security: + - petstore_auth: + - write:pets + - read:pets + x-spring-paginated: true + x-operation-extra-annotation: "@org.springframework.validation.annotation.Validated" # test that the annotation is preserved when x-spring-paginated:true is used and is prepended with the @PageableAsQueryParam for springdoc /pet/{petId}: get: tags: diff --git a/samples/openapi3/client/petstore/spring-cloud-spring-pageable/src/main/java/org/openapitools/api/PetApi.java b/samples/openapi3/client/petstore/spring-cloud-spring-pageable/src/main/java/org/openapitools/api/PetApi.java index b549aef534ba..0b3df238b4e5 100644 --- a/samples/openapi3/client/petstore/spring-cloud-spring-pageable/src/main/java/org/openapitools/api/PetApi.java +++ b/samples/openapi3/client/petstore/spring-cloud-spring-pageable/src/main/java/org/openapitools/api/PetApi.java @@ -126,6 +126,8 @@ ResponseEntity deletePet( value = PetApi.PATH_FIND_PETS_BY_STATUS, produces = { "application/json", "application/xml" } ) + @org.springframework.validation.annotation.Validated + @org.springframework.security.access.prepost.PreAuthorize("hasRole('ADMIN')") ResponseEntity> findPetsByStatus( @NotNull @Parameter(name = "status", description = "Status values that need to be considered for filter", required = true, in = ParameterIn.QUERY) @Valid @RequestParam(value = "status", required = true) List status, @ParameterObject final Pageable pageable @@ -210,6 +212,41 @@ ResponseEntity getPetById( ); + String PATH_LIST_ALL_PETS = "/pet/all"; + /** + * GET /pet/all : List all pets + * Returns all pets with pagination support + * + * @return successful operation (status code 200) + * or Invalid status value (status code 400) + */ + @Operation( + operationId = "listAllPets", + summary = "List all pets", + description = "Returns all pets with pagination support", + tags = { "pet" }, + responses = { + @ApiResponse(responseCode = "200", description = "successful operation", content = { + @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = Pet.class))), + @Content(mediaType = "application/xml", array = @ArraySchema(schema = @Schema(implementation = Pet.class))) + }), + @ApiResponse(responseCode = "400", description = "Invalid status value") + }, + security = { + @SecurityRequirement(name = "petstore_auth", scopes={ "write:pets", "read:pets" }) + } + ) + @RequestMapping( + method = RequestMethod.GET, + value = PetApi.PATH_LIST_ALL_PETS, + produces = { "application/json", "application/xml" } + ) + @org.springframework.validation.annotation.Validated + ResponseEntity> listAllPets( + @ParameterObject final Pageable pageable + ); + + String PATH_UPDATE_PET = "/pet"; /** * PUT /pet : Update an existing pet diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/.openapi-generator/FILES b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/.openapi-generator/FILES index 18f6d4999914..6d78af78d508 100644 --- a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/.openapi-generator/FILES +++ b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/.openapi-generator/FILES @@ -6,9 +6,9 @@ gradlew gradlew.bat pom.xml settings.gradle -src/main/kotlin/org/openapitools/api/PetApiClient.kt -src/main/kotlin/org/openapitools/api/StoreApiClient.kt -src/main/kotlin/org/openapitools/api/UserApiClient.kt +src/main/kotlin/org/openapitools/api/PetApi.kt +src/main/kotlin/org/openapitools/api/StoreApi.kt +src/main/kotlin/org/openapitools/api/UserApi.kt src/main/kotlin/org/openapitools/model/Category.kt src/main/kotlin/org/openapitools/model/ModelApiResponse.kt src/main/kotlin/org/openapitools/model/Order.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/PetApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/PetApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/PetApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/PetApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/StoreApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/StoreApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/StoreApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/StoreApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/UserApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/UserApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/UserApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-reactive-coroutines/src/main/kotlin/org/openapitools/api/UserApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/.openapi-generator/FILES b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/.openapi-generator/FILES index 18f6d4999914..6d78af78d508 100644 --- a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/.openapi-generator/FILES +++ b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/.openapi-generator/FILES @@ -6,9 +6,9 @@ gradlew gradlew.bat pom.xml settings.gradle -src/main/kotlin/org/openapitools/api/PetApiClient.kt -src/main/kotlin/org/openapitools/api/StoreApiClient.kt -src/main/kotlin/org/openapitools/api/UserApiClient.kt +src/main/kotlin/org/openapitools/api/PetApi.kt +src/main/kotlin/org/openapitools/api/StoreApi.kt +src/main/kotlin/org/openapitools/api/UserApi.kt src/main/kotlin/org/openapitools/model/Category.kt src/main/kotlin/org/openapitools/model/ModelApiResponse.kt src/main/kotlin/org/openapitools/model/Order.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/PetApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/PetApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/PetApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/PetApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/StoreApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/StoreApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/StoreApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/StoreApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/UserApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/UserApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/UserApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-reactive-reactor-wrapped/src/main/kotlin/org/openapitools/api/UserApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/.openapi-generator/FILES b/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/.openapi-generator/FILES index 18f6d4999914..6d78af78d508 100644 --- a/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/.openapi-generator/FILES +++ b/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/.openapi-generator/FILES @@ -6,9 +6,9 @@ gradlew gradlew.bat pom.xml settings.gradle -src/main/kotlin/org/openapitools/api/PetApiClient.kt -src/main/kotlin/org/openapitools/api/StoreApiClient.kt -src/main/kotlin/org/openapitools/api/UserApiClient.kt +src/main/kotlin/org/openapitools/api/PetApi.kt +src/main/kotlin/org/openapitools/api/StoreApi.kt +src/main/kotlin/org/openapitools/api/UserApi.kt src/main/kotlin/org/openapitools/model/Category.kt src/main/kotlin/org/openapitools/model/ModelApiResponse.kt src/main/kotlin/org/openapitools/model/Order.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/PetApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/PetApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/PetApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/PetApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/StoreApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/StoreApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/StoreApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/StoreApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/UserApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/UserApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/UserApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface-wrapped/src/main/kotlin/org/openapitools/api/UserApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface/.openapi-generator/FILES b/samples/server/petstore/kotlin-spring-declarative-interface/.openapi-generator/FILES index 18f6d4999914..6d78af78d508 100644 --- a/samples/server/petstore/kotlin-spring-declarative-interface/.openapi-generator/FILES +++ b/samples/server/petstore/kotlin-spring-declarative-interface/.openapi-generator/FILES @@ -6,9 +6,9 @@ gradlew gradlew.bat pom.xml settings.gradle -src/main/kotlin/org/openapitools/api/PetApiClient.kt -src/main/kotlin/org/openapitools/api/StoreApiClient.kt -src/main/kotlin/org/openapitools/api/UserApiClient.kt +src/main/kotlin/org/openapitools/api/PetApi.kt +src/main/kotlin/org/openapitools/api/StoreApi.kt +src/main/kotlin/org/openapitools/api/UserApi.kt src/main/kotlin/org/openapitools/model/Category.kt src/main/kotlin/org/openapitools/model/ModelApiResponse.kt src/main/kotlin/org/openapitools/model/Order.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/PetApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/PetApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/PetApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/PetApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/StoreApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/StoreApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/StoreApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/StoreApi.kt diff --git a/samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/UserApiClient.kt b/samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/UserApi.kt similarity index 100% rename from samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/UserApiClient.kt rename to samples/server/petstore/kotlin-spring-declarative-interface/src/main/kotlin/org/openapitools/api/UserApi.kt diff --git a/samples/server/petstore/kotlin-spring-default/build.gradle.kts b/samples/server/petstore/kotlin-spring-default/build.gradle.kts index e956b616df71..319a704f5842 100644 --- a/samples/server/petstore/kotlin-spring-default/build.gradle.kts +++ b/samples/server/petstore/kotlin-spring-default/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-spring-default/pom.xml b/samples/server/petstore/kotlin-spring-default/pom.xml index ed6a78fd3ffe..76ef64f955da 100644 --- a/samples/server/petstore/kotlin-spring-default/pom.xml +++ b/samples/server/petstore/kotlin-spring-default/pom.xml @@ -86,6 +86,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-3-no-response-entity/build.gradle.kts b/samples/server/petstore/kotlin-springboot-3-no-response-entity/build.gradle.kts index 473c0162e0f4..db73c5e21693 100644 --- a/samples/server/petstore/kotlin-springboot-3-no-response-entity/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-3-no-response-entity/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("jakarta.validation:jakarta.validation-api") implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") diff --git a/samples/server/petstore/kotlin-springboot-3-no-response-entity/pom.xml b/samples/server/petstore/kotlin-springboot-3-no-response-entity/pom.xml index 3c52a401bf8a..3844d9e01f44 100644 --- a/samples/server/petstore/kotlin-springboot-3-no-response-entity/pom.xml +++ b/samples/server/petstore/kotlin-springboot-3-no-response-entity/pom.xml @@ -98,6 +98,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-3/build.gradle.kts b/samples/server/petstore/kotlin-springboot-3/build.gradle.kts index 473c0162e0f4..db73c5e21693 100644 --- a/samples/server/petstore/kotlin-springboot-3/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-3/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("jakarta.validation:jakarta.validation-api") implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") diff --git a/samples/server/petstore/kotlin-springboot-3/pom.xml b/samples/server/petstore/kotlin-springboot-3/pom.xml index 3c52a401bf8a..3844d9e01f44 100644 --- a/samples/server/petstore/kotlin-springboot-3/pom.xml +++ b/samples/server/petstore/kotlin-springboot-3/pom.xml @@ -98,6 +98,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-additionalproperties/build.gradle.kts b/samples/server/petstore/kotlin-springboot-additionalproperties/build.gradle.kts index 473c0162e0f4..db73c5e21693 100644 --- a/samples/server/petstore/kotlin-springboot-additionalproperties/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-additionalproperties/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("jakarta.validation:jakarta.validation-api") implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") diff --git a/samples/server/petstore/kotlin-springboot-additionalproperties/pom.xml b/samples/server/petstore/kotlin-springboot-additionalproperties/pom.xml index 3c52a401bf8a..3844d9e01f44 100644 --- a/samples/server/petstore/kotlin-springboot-additionalproperties/pom.xml +++ b/samples/server/petstore/kotlin-springboot-additionalproperties/pom.xml @@ -98,6 +98,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-bigdecimal-default/build.gradle.kts b/samples/server/petstore/kotlin-springboot-bigdecimal-default/build.gradle.kts index e956b616df71..319a704f5842 100644 --- a/samples/server/petstore/kotlin-springboot-bigdecimal-default/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-bigdecimal-default/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-bigdecimal-default/pom.xml b/samples/server/petstore/kotlin-springboot-bigdecimal-default/pom.xml index ed6a78fd3ffe..76ef64f955da 100644 --- a/samples/server/petstore/kotlin-springboot-bigdecimal-default/pom.xml +++ b/samples/server/petstore/kotlin-springboot-bigdecimal-default/pom.xml @@ -86,6 +86,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-delegate-nodefaults/build.gradle.kts b/samples/server/petstore/kotlin-springboot-delegate-nodefaults/build.gradle.kts index 8a3d16c5fad0..6122df330e69 100644 --- a/samples/server/petstore/kotlin-springboot-delegate-nodefaults/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-delegate-nodefaults/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("jakarta.validation:jakarta.validation-api") implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") diff --git a/samples/server/petstore/kotlin-springboot-delegate-nodefaults/pom.xml b/samples/server/petstore/kotlin-springboot-delegate-nodefaults/pom.xml index b041f6c83f6e..c7a557207ccc 100644 --- a/samples/server/petstore/kotlin-springboot-delegate-nodefaults/pom.xml +++ b/samples/server/petstore/kotlin-springboot-delegate-nodefaults/pom.xml @@ -99,6 +99,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-delegate/build.gradle.kts b/samples/server/petstore/kotlin-springboot-delegate/build.gradle.kts index e956b616df71..319a704f5842 100644 --- a/samples/server/petstore/kotlin-springboot-delegate/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-delegate/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-delegate/pom.xml b/samples/server/petstore/kotlin-springboot-delegate/pom.xml index ed6a78fd3ffe..76ef64f955da 100644 --- a/samples/server/petstore/kotlin-springboot-delegate/pom.xml +++ b/samples/server/petstore/kotlin-springboot-delegate/pom.xml @@ -86,6 +86,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/build.gradle.kts b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/build.gradle.kts index e78a961d4091..3fa8025c39dd 100644 --- a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/pom.xml b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/pom.xml index 1e85d34a2f9e..9e5b5f9d5877 100644 --- a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/pom.xml +++ b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/pom.xml @@ -97,6 +97,10 @@ kotlinx-coroutines-reactor ${kotlinx-coroutines.version} + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApi.kt b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApi.kt index fe980df7e0b8..9a0c20bb669f 100644 --- a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApi.kt +++ b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApi.kt @@ -6,6 +6,7 @@ package org.openapitools.api import org.openapitools.model.ModelApiResponse +import org.springframework.data.domain.Pageable import org.openapitools.model.Pet import io.swagger.annotations.Api import io.swagger.annotations.ApiOperation @@ -165,6 +166,239 @@ interface PetApi { } + @ApiOperation( + value = "List all pets with pagination (only pagination params)", + nickname = "listAllPetsPaginated", + notes = "Tests x-spring-paginated with ONLY page/size/sort params that will be removed", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/all" + value = [PATH_LIST_ALL_PETS_PAGINATED], + produces = ["application/json"] + ) + fun listAllPetsPaginated(@ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange, + @ApiParam(hidden = true) pageable: Pageable): ResponseEntity> { + return getDelegate().listAllPetsPaginated(exchange, pageable) + } + + + @ApiOperation( + value = "List pets with path and header params only", + nickname = "listPetsByIdPaginated", + notes = "Tests x-spring-paginated with no query params except pagination", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/pathOnly" + value = [PATH_LIST_PETS_BY_ID_PAGINATED], + produces = ["application/json"] + ) + fun listPetsByIdPaginated( + @ApiParam(value = "Request ID for tracing") @RequestHeader(value = "X-Request-ID", required = false) xRequestID: kotlin.String?, + @ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> { + return getDelegate().listPetsByIdPaginated(xRequestID, exchange, pageable) + } + + + @ApiOperation( + value = "List pets by owner with pagination", + nickname = "listPetsByOwnerPaginated", + notes = "Tests x-spring-paginated with path param + query params", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/{ownerId}" + value = [PATH_LIST_PETS_BY_OWNER_PAGINATED], + produces = ["application/json"] + ) + fun listPetsByOwnerPaginated( + @ApiParam(value = "Owner ID - will be kept", required = true) @PathVariable("ownerId") ownerId: kotlin.Long, + @ApiParam(value = "Include adopted pets - will be kept", defaultValue = "false") @Valid @RequestParam(value = "includeAdopted", required = false, defaultValue = "false") includeAdopted: kotlin.Boolean, + @ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> { + return getDelegate().listPetsByOwnerPaginated(ownerId, includeAdopted, exchange, pageable) + } + + + @ApiOperation( + value = "Mixed params - path, header, query, and pagination", + nickname = "listPetsMixedParams", + notes = "Comprehensive test with all parameter types", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/mixed" + value = [PATH_LIST_PETS_MIXED_PARAMS], + produces = ["application/json"] + ) + fun listPetsMixedParams( + @ApiParam(value = "Authorization header") @RequestHeader(value = "Authorization", required = false) authorization: kotlin.String?, + @ApiParam(value = "Tenant ID") @RequestHeader(value = "X-Tenant-ID", required = false) xTenantID: kotlin.String?, + @ApiParam(value = "Status filter") @Valid @RequestParam(value = "status", required = false) status: kotlin.String?, + @ApiParam(value = "Include inactive pets") @Valid @RequestParam(value = "includeInactive", required = false) includeInactive: kotlin.Boolean?, + @ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> { + return getDelegate().listPetsMixedParams(authorization, xTenantID, status, includeInactive, exchange, pageable) + } + + + @ApiOperation( + value = "List pets with many query params", + nickname = "listPetsMultipleParams", + notes = "Tests x-spring-paginated removes only page/size/sort", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/multiParam" + value = [PATH_LIST_PETS_MULTIPLE_PARAMS], + produces = ["application/json"] + ) + fun listPetsMultipleParams( + @ApiParam(value = "Filter by name - will be kept") @Valid @RequestParam(value = "name", required = false) name: kotlin.String?, + @ApiParam(value = "Minimum age - will be kept") @Valid @RequestParam(value = "minAge", required = false) minAge: kotlin.Int?, + @ApiParam(value = "Maximum age - will be kept") @Valid @RequestParam(value = "maxAge", required = false) maxAge: kotlin.Int?, + @ApiParam(value = "Filter by tags - will be kept") @Valid @RequestParam(value = "tags", required = false) tags: kotlin.collections.List?, + @ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> { + return getDelegate().listPetsMultipleParams(name, minAge, maxAge, tags, exchange, pageable) + } + + + @ApiOperation( + value = "List pets WITHOUT x-spring-paginated", + nickname = "listPetsNoPagination", + notes = "Tests that operations without x-spring-paginated keep all params", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/noPagination" + value = [PATH_LIST_PETS_NO_PAGINATION], + produces = ["application/json"] + ) + fun listPetsNoPagination( + @ApiParam(value = "Filter by status") @Valid @RequestParam(value = "status", required = false) status: kotlin.String?, + @ApiParam(value = "Page number - will be KEPT (no x-spring-paginated)") @Valid @RequestParam(value = "page", required = false) page: kotlin.Int?, + @ApiParam(value = "Page size - will be KEPT (no x-spring-paginated)") @Valid @RequestParam(value = "size", required = false) size: kotlin.Int?, + @ApiParam(value = "Sort order - will be KEPT (no x-spring-paginated)") @Valid @RequestParam(value = "sort", required = false) sort: kotlin.String?, + @ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange + ): ResponseEntity> { + return getDelegate().listPetsNoPagination(status, page, size, sort, exchange) + } + + + @ApiOperation( + value = "List pets with only some pagination params", + nickname = "listPetsPartialPagination", + notes = "Tests x-spring-paginated with only page and size (no sort)", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/partialParams" + value = [PATH_LIST_PETS_PARTIAL_PAGINATION], + produces = ["application/json"] + ) + fun listPetsPartialPagination( + @ApiParam(value = "Filter by status - will be kept") @Valid @RequestParam(value = "status", required = false) status: kotlin.String?, + @ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> { + return getDelegate().listPetsPartialPagination(status, exchange, pageable) + } + + + @ApiOperation( + value = "List pets with filtering and pagination", + nickname = "listPetsWithFilterPaginated", + notes = "Tests x-spring-paginated with regular params + pagination params", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/withParams" + value = [PATH_LIST_PETS_WITH_FILTER_PAGINATED], + produces = ["application/json"] + ) + fun listPetsWithFilterPaginated( + @ApiParam(value = "Filter by status - will be kept", allowableValues = "available, pending, sold") @Valid @RequestParam(value = "status", required = false) status: kotlin.String?, + @ApiParam(value = "Filter by name - will be kept") @Valid @RequestParam(value = "name", required = false) name: kotlin.String?, + @ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> { + return getDelegate().listPetsWithFilterPaginated(status, name, exchange, pageable) + } + + + @ApiOperation( + value = "List pets with header param named 'size'", + nickname = "listPetsWithHeaderSize", + notes = "Tests that header param 'size' is preserved while query param 'size' is removed", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/headerSize" + value = [PATH_LIST_PETS_WITH_HEADER_SIZE], + produces = ["application/json"] + ) + fun listPetsWithHeaderSize( + @ApiParam(value = "Size header - must NOT be removed (different from query param)") @RequestHeader(value = "size", required = false) size: kotlin.String?, + @ApiParam(value = "Filter by category - will be kept") @Valid @RequestParam(value = "category", required = false) category: kotlin.String?, + @ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> { + return getDelegate().listPetsWithHeaderSize(size, category, exchange, pageable) + } + + @ApiOperation( value = "Update an existing pet", nickname = "updatePet", @@ -246,6 +480,15 @@ interface PetApi { const val PATH_FIND_PETS_BY_STATUS: String = "/pet/findByStatus" const val PATH_FIND_PETS_BY_TAGS: String = "/pet/findByTags" const val PATH_GET_PET_BY_ID: String = "/pet/{petId}" + const val PATH_LIST_ALL_PETS_PAGINATED: String = "/pet/paginated/all" + const val PATH_LIST_PETS_BY_ID_PAGINATED: String = "/pet/paginated/pathOnly" + const val PATH_LIST_PETS_BY_OWNER_PAGINATED: String = "/pet/paginated/{ownerId}" + const val PATH_LIST_PETS_MIXED_PARAMS: String = "/pet/paginated/mixed" + const val PATH_LIST_PETS_MULTIPLE_PARAMS: String = "/pet/paginated/multiParam" + const val PATH_LIST_PETS_NO_PAGINATION: String = "/pet/paginated/noPagination" + const val PATH_LIST_PETS_PARTIAL_PAGINATION: String = "/pet/paginated/partialParams" + const val PATH_LIST_PETS_WITH_FILTER_PAGINATED: String = "/pet/paginated/withParams" + const val PATH_LIST_PETS_WITH_HEADER_SIZE: String = "/pet/paginated/headerSize" const val PATH_UPDATE_PET: String = "/pet" const val PATH_UPDATE_PET_WITH_FORM: String = "/pet/{petId}" const val PATH_UPLOAD_FILE: String = "/pet/{petId}/uploadImage" diff --git a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApiDelegate.kt b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApiDelegate.kt index 66314ad03c2b..ff85edeb0007 100644 --- a/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApiDelegate.kt +++ b/samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApiDelegate.kt @@ -1,6 +1,7 @@ package org.openapitools.api import org.openapitools.model.ModelApiResponse +import org.springframework.data.domain.Pageable import org.openapitools.model.Pet import org.springframework.http.HttpStatus import org.springframework.http.MediaType @@ -55,6 +56,88 @@ interface PetApiDelegate { exchange: org.springframework.web.server.ServerWebExchange): ResponseEntity + /** + * @see PetApi#listAllPetsPaginated + */ + fun listAllPetsPaginated(exchange: org.springframework.web.server.ServerWebExchange, + pageable: Pageable): ResponseEntity> + + + /** + * @see PetApi#listPetsByIdPaginated + */ + fun listPetsByIdPaginated(xRequestID: kotlin.String?, + exchange: org.springframework.web.server.ServerWebExchange, + pageable: Pageable): ResponseEntity> + + + /** + * @see PetApi#listPetsByOwnerPaginated + */ + fun listPetsByOwnerPaginated(ownerId: kotlin.Long, + includeAdopted: kotlin.Boolean, + exchange: org.springframework.web.server.ServerWebExchange, + pageable: Pageable): ResponseEntity> + + + /** + * @see PetApi#listPetsMixedParams + */ + fun listPetsMixedParams(authorization: kotlin.String?, + xTenantID: kotlin.String?, + status: kotlin.String?, + includeInactive: kotlin.Boolean?, + exchange: org.springframework.web.server.ServerWebExchange, + pageable: Pageable): ResponseEntity> + + + /** + * @see PetApi#listPetsMultipleParams + */ + fun listPetsMultipleParams(name: kotlin.String?, + minAge: kotlin.Int?, + maxAge: kotlin.Int?, + tags: kotlin.collections.List?, + exchange: org.springframework.web.server.ServerWebExchange, + pageable: Pageable): ResponseEntity> + + + /** + * @see PetApi#listPetsNoPagination + */ + fun listPetsNoPagination(status: kotlin.String?, + page: kotlin.Int?, + size: kotlin.Int?, + sort: kotlin.String?, + exchange: org.springframework.web.server.ServerWebExchange): ResponseEntity> + + + /** + * @see PetApi#listPetsPartialPagination + */ + fun listPetsPartialPagination(status: kotlin.String?, + exchange: org.springframework.web.server.ServerWebExchange, + pageable: Pageable): ResponseEntity> + + + /** + * @see PetApi#listPetsWithFilterPaginated + */ + fun listPetsWithFilterPaginated(status: kotlin.String?, + name: kotlin.String?, + exchange: org.springframework.web.server.ServerWebExchange, + pageable: Pageable): ResponseEntity> + + + /** + * @see PetApi#listPetsWithHeaderSize + */ + fun listPetsWithHeaderSize(size: kotlin.String?, + category: kotlin.String?, + exchange: org.springframework.web.server.ServerWebExchange, + pageable: Pageable): ResponseEntity> + + /** * @see PetApi#updatePet */ diff --git a/samples/server/petstore/kotlin-springboot-integer-enum/build.gradle.kts b/samples/server/petstore/kotlin-springboot-integer-enum/build.gradle.kts index 0caea597c84f..559b1f327bec 100644 --- a/samples/server/petstore/kotlin-springboot-integer-enum/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-integer-enum/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("jakarta.validation:jakarta.validation-api") implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") diff --git a/samples/server/petstore/kotlin-springboot-integer-enum/pom.xml b/samples/server/petstore/kotlin-springboot-integer-enum/pom.xml index 77a9c03660db..67af2523dcb8 100644 --- a/samples/server/petstore/kotlin-springboot-integer-enum/pom.xml +++ b/samples/server/petstore/kotlin-springboot-integer-enum/pom.xml @@ -98,6 +98,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-modelMutable/build.gradle.kts b/samples/server/petstore/kotlin-springboot-modelMutable/build.gradle.kts index e956b616df71..319a704f5842 100644 --- a/samples/server/petstore/kotlin-springboot-modelMutable/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-modelMutable/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-modelMutable/pom.xml b/samples/server/petstore/kotlin-springboot-modelMutable/pom.xml index ed6a78fd3ffe..76ef64f955da 100644 --- a/samples/server/petstore/kotlin-springboot-modelMutable/pom.xml +++ b/samples/server/petstore/kotlin-springboot-modelMutable/pom.xml @@ -86,6 +86,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-multipart-request-model/build.gradle.kts b/samples/server/petstore/kotlin-springboot-multipart-request-model/build.gradle.kts index e956b616df71..319a704f5842 100644 --- a/samples/server/petstore/kotlin-springboot-multipart-request-model/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-multipart-request-model/build.gradle.kts @@ -40,6 +40,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-multipart-request-model/pom.xml b/samples/server/petstore/kotlin-springboot-multipart-request-model/pom.xml index ed6a78fd3ffe..76ef64f955da 100644 --- a/samples/server/petstore/kotlin-springboot-multipart-request-model/pom.xml +++ b/samples/server/petstore/kotlin-springboot-multipart-request-model/pom.xml @@ -86,6 +86,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-no-response-entity-delegate/build.gradle.kts b/samples/server/petstore/kotlin-springboot-no-response-entity-delegate/build.gradle.kts index 3507e44d515a..b3086b53f094 100644 --- a/samples/server/petstore/kotlin-springboot-no-response-entity-delegate/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-no-response-entity-delegate/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-no-response-entity-delegate/pom.xml b/samples/server/petstore/kotlin-springboot-no-response-entity-delegate/pom.xml index cab717f26a33..7a6726d7bbfd 100644 --- a/samples/server/petstore/kotlin-springboot-no-response-entity-delegate/pom.xml +++ b/samples/server/petstore/kotlin-springboot-no-response-entity-delegate/pom.xml @@ -85,6 +85,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-no-response-entity/build.gradle.kts b/samples/server/petstore/kotlin-springboot-no-response-entity/build.gradle.kts index 3507e44d515a..b3086b53f094 100644 --- a/samples/server/petstore/kotlin-springboot-no-response-entity/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-no-response-entity/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-no-response-entity/pom.xml b/samples/server/petstore/kotlin-springboot-no-response-entity/pom.xml index cab717f26a33..7a6726d7bbfd 100644 --- a/samples/server/petstore/kotlin-springboot-no-response-entity/pom.xml +++ b/samples/server/petstore/kotlin-springboot-no-response-entity/pom.xml @@ -85,6 +85,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-reactive-without-flow/build.gradle.kts b/samples/server/petstore/kotlin-springboot-reactive-without-flow/build.gradle.kts index e2f66299c1f1..f8d51547a12e 100644 --- a/samples/server/petstore/kotlin-springboot-reactive-without-flow/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-reactive-without-flow/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-reactive-without-flow/pom.xml b/samples/server/petstore/kotlin-springboot-reactive-without-flow/pom.xml index 80ae9216e1e3..39afc668db31 100644 --- a/samples/server/petstore/kotlin-springboot-reactive-without-flow/pom.xml +++ b/samples/server/petstore/kotlin-springboot-reactive-without-flow/pom.xml @@ -97,6 +97,10 @@ kotlinx-coroutines-reactor ${kotlinx-coroutines.version} + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-reactive/build.gradle.kts b/samples/server/petstore/kotlin-springboot-reactive/build.gradle.kts index e2f66299c1f1..f8d51547a12e 100644 --- a/samples/server/petstore/kotlin-springboot-reactive/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-reactive/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-reactive/pom.xml b/samples/server/petstore/kotlin-springboot-reactive/pom.xml index 80ae9216e1e3..39afc668db31 100644 --- a/samples/server/petstore/kotlin-springboot-reactive/pom.xml +++ b/samples/server/petstore/kotlin-springboot-reactive/pom.xml @@ -97,6 +97,10 @@ kotlinx-coroutines-reactor ${kotlinx-coroutines.version} + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-request-cookie/build.gradle.kts b/samples/server/petstore/kotlin-springboot-request-cookie/build.gradle.kts index ebeee8407bcc..fa644c9b144c 100644 --- a/samples/server/petstore/kotlin-springboot-request-cookie/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-request-cookie/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("jakarta.validation:jakarta.validation-api") implementation("jakarta.annotation:jakarta.annotation-api:2.1.0") diff --git a/samples/server/petstore/kotlin-springboot-request-cookie/pom.xml b/samples/server/petstore/kotlin-springboot-request-cookie/pom.xml index fb11e4866dec..797865c697a9 100644 --- a/samples/server/petstore/kotlin-springboot-request-cookie/pom.xml +++ b/samples/server/petstore/kotlin-springboot-request-cookie/pom.xml @@ -99,6 +99,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-source-swagger1/build.gradle.kts b/samples/server/petstore/kotlin-springboot-source-swagger1/build.gradle.kts index 0e1a003d2859..cd5e76efe1bd 100644 --- a/samples/server/petstore/kotlin-springboot-source-swagger1/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-source-swagger1/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-source-swagger1/pom.xml b/samples/server/petstore/kotlin-springboot-source-swagger1/pom.xml index 31f4899ccbe3..07c34bb1288e 100644 --- a/samples/server/petstore/kotlin-springboot-source-swagger1/pom.xml +++ b/samples/server/petstore/kotlin-springboot-source-swagger1/pom.xml @@ -87,6 +87,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-source-swagger2/build.gradle.kts b/samples/server/petstore/kotlin-springboot-source-swagger2/build.gradle.kts index a024c20a2a9c..bab28e4573e9 100644 --- a/samples/server/petstore/kotlin-springboot-source-swagger2/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-source-swagger2/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-source-swagger2/pom.xml b/samples/server/petstore/kotlin-springboot-source-swagger2/pom.xml index 2c1eb5b4ae8a..b8f74802da03 100644 --- a/samples/server/petstore/kotlin-springboot-source-swagger2/pom.xml +++ b/samples/server/petstore/kotlin-springboot-source-swagger2/pom.xml @@ -87,6 +87,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-springfox/build.gradle.kts b/samples/server/petstore/kotlin-springboot-springfox/build.gradle.kts index a5e654a22c9f..cad1242edc51 100644 --- a/samples/server/petstore/kotlin-springboot-springfox/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-springfox/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-springfox/pom.xml b/samples/server/petstore/kotlin-springboot-springfox/pom.xml index c733bf0da18b..789f094be1c1 100644 --- a/samples/server/petstore/kotlin-springboot-springfox/pom.xml +++ b/samples/server/petstore/kotlin-springboot-springfox/pom.xml @@ -87,6 +87,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/build.gradle.kts b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/build.gradle.kts index 10a80f14f0ff..39df365c5d0b 100644 --- a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/build.gradle.kts @@ -44,6 +44,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/pom.xml b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/pom.xml index 067cb2bbd025..abd0f31c98ca 100644 --- a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/pom.xml +++ b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/pom.xml @@ -86,6 +86,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons + diff --git a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/main/kotlin/org/openapitools/api/PetApi.kt b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/main/kotlin/org/openapitools/api/PetApi.kt index d30df48a2e47..828ae7b810f9 100644 --- a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/main/kotlin/org/openapitools/api/PetApi.kt +++ b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/main/kotlin/org/openapitools/api/PetApi.kt @@ -6,6 +6,7 @@ package org.openapitools.api import org.openapitools.model.ModelApiResponse +import org.springframework.data.domain.Pageable import org.openapitools.model.Pet import io.swagger.annotations.Api import io.swagger.annotations.ApiOperation @@ -152,6 +153,221 @@ interface PetApi { ): ResponseEntity + @ApiOperation( + value = "List all pets with pagination (only pagination params)", + nickname = "listAllPetsPaginated", + notes = "Tests x-spring-paginated with ONLY page/size/sort params that will be removed", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/all" + value = [PATH_LIST_ALL_PETS_PAGINATED], + produces = ["application/json"] + ) + fun listAllPetsPaginated(@ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest, + @ApiParam(hidden = true) pageable: Pageable): ResponseEntity> + + + @ApiOperation( + value = "List pets with path and header params only", + nickname = "listPetsByIdPaginated", + notes = "Tests x-spring-paginated with no query params except pagination", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/pathOnly" + value = [PATH_LIST_PETS_BY_ID_PAGINATED], + produces = ["application/json"] + ) + fun listPetsByIdPaginated( + @ApiParam(value = "Request ID for tracing") @RequestHeader(value = "X-Request-ID", required = false) xRequestID: kotlin.String?, + @ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> + + + @ApiOperation( + value = "List pets by owner with pagination", + nickname = "listPetsByOwnerPaginated", + notes = "Tests x-spring-paginated with path param + query params", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/{ownerId}" + value = [PATH_LIST_PETS_BY_OWNER_PAGINATED], + produces = ["application/json"] + ) + fun listPetsByOwnerPaginated( + @ApiParam(value = "Owner ID - will be kept", required = true) @PathVariable("ownerId") ownerId: kotlin.Long, + @ApiParam(value = "Include adopted pets - will be kept", defaultValue = "false") @Valid @RequestParam(value = "includeAdopted", required = false, defaultValue = "false") includeAdopted: kotlin.Boolean, + @ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> + + + @ApiOperation( + value = "Mixed params - path, header, query, and pagination", + nickname = "listPetsMixedParams", + notes = "Comprehensive test with all parameter types", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/mixed" + value = [PATH_LIST_PETS_MIXED_PARAMS], + produces = ["application/json"] + ) + fun listPetsMixedParams( + @ApiParam(value = "Authorization header") @RequestHeader(value = "Authorization", required = false) authorization: kotlin.String?, + @ApiParam(value = "Tenant ID") @RequestHeader(value = "X-Tenant-ID", required = false) xTenantID: kotlin.String?, + @ApiParam(value = "Status filter") @Valid @RequestParam(value = "status", required = false) status: kotlin.String?, + @ApiParam(value = "Include inactive pets") @Valid @RequestParam(value = "includeInactive", required = false) includeInactive: kotlin.Boolean?, + @ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> + + + @ApiOperation( + value = "List pets with many query params", + nickname = "listPetsMultipleParams", + notes = "Tests x-spring-paginated removes only page/size/sort", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/multiParam" + value = [PATH_LIST_PETS_MULTIPLE_PARAMS], + produces = ["application/json"] + ) + fun listPetsMultipleParams( + @ApiParam(value = "Filter by name - will be kept") @Valid @RequestParam(value = "name", required = false) name: kotlin.String?, + @ApiParam(value = "Minimum age - will be kept") @Valid @RequestParam(value = "minAge", required = false) minAge: kotlin.Int?, + @ApiParam(value = "Maximum age - will be kept") @Valid @RequestParam(value = "maxAge", required = false) maxAge: kotlin.Int?, + @ApiParam(value = "Filter by tags - will be kept") @Valid @RequestParam(value = "tags", required = false) tags: kotlin.collections.List?, + @ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> + + + @ApiOperation( + value = "List pets WITHOUT x-spring-paginated", + nickname = "listPetsNoPagination", + notes = "Tests that operations without x-spring-paginated keep all params", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/noPagination" + value = [PATH_LIST_PETS_NO_PAGINATION], + produces = ["application/json"] + ) + fun listPetsNoPagination( + @ApiParam(value = "Filter by status") @Valid @RequestParam(value = "status", required = false) status: kotlin.String?, + @ApiParam(value = "Page number - will be KEPT (no x-spring-paginated)") @Valid @RequestParam(value = "page", required = false) page: kotlin.Int?, + @ApiParam(value = "Page size - will be KEPT (no x-spring-paginated)") @Valid @RequestParam(value = "size", required = false) size: kotlin.Int?, + @ApiParam(value = "Sort order - will be KEPT (no x-spring-paginated)") @Valid @RequestParam(value = "sort", required = false) sort: kotlin.String?, + @ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest + ): ResponseEntity> + + + @ApiOperation( + value = "List pets with only some pagination params", + nickname = "listPetsPartialPagination", + notes = "Tests x-spring-paginated with only page and size (no sort)", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/partialParams" + value = [PATH_LIST_PETS_PARTIAL_PAGINATION], + produces = ["application/json"] + ) + fun listPetsPartialPagination( + @ApiParam(value = "Filter by status - will be kept") @Valid @RequestParam(value = "status", required = false) status: kotlin.String?, + @ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> + + + @ApiOperation( + value = "List pets with filtering and pagination", + nickname = "listPetsWithFilterPaginated", + notes = "Tests x-spring-paginated with regular params + pagination params", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/withParams" + value = [PATH_LIST_PETS_WITH_FILTER_PAGINATED], + produces = ["application/json"] + ) + fun listPetsWithFilterPaginated( + @ApiParam(value = "Filter by status - will be kept", allowableValues = "available, pending, sold") @Valid @RequestParam(value = "status", required = false) status: kotlin.String?, + @ApiParam(value = "Filter by name - will be kept") @Valid @RequestParam(value = "name", required = false) name: kotlin.String?, + @ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> + + + @ApiOperation( + value = "List pets with header param named 'size'", + nickname = "listPetsWithHeaderSize", + notes = "Tests that header param 'size' is preserved while query param 'size' is removed", + response = Pet::class, + responseContainer = "List" + ) + @ApiResponses( + value = [ApiResponse(code = 200, message = "successful operation", response = Pet::class, responseContainer = "List")] + ) + @RequestMapping( + method = [RequestMethod.GET], + // "/pet/paginated/headerSize" + value = [PATH_LIST_PETS_WITH_HEADER_SIZE], + produces = ["application/json"] + ) + fun listPetsWithHeaderSize( + @ApiParam(value = "Size header - must NOT be removed (different from query param)") @RequestHeader(value = "size", required = false) size: kotlin.String?, + @ApiParam(value = "Filter by category - will be kept") @Valid @RequestParam(value = "category", required = false) category: kotlin.String?, + @ApiParam(hidden = true) request: javax.servlet.http.HttpServletRequest, + @ApiParam(hidden = true) pageable: Pageable + ): ResponseEntity> + + @ApiOperation( value = "Update an existing pet", nickname = "updatePet", @@ -227,6 +443,15 @@ interface PetApi { const val PATH_FIND_PETS_BY_STATUS: String = "/pet/findByStatus" const val PATH_FIND_PETS_BY_TAGS: String = "/pet/findByTags" const val PATH_GET_PET_BY_ID: String = "/pet/{petId}" + const val PATH_LIST_ALL_PETS_PAGINATED: String = "/pet/paginated/all" + const val PATH_LIST_PETS_BY_ID_PAGINATED: String = "/pet/paginated/pathOnly" + const val PATH_LIST_PETS_BY_OWNER_PAGINATED: String = "/pet/paginated/{ownerId}" + const val PATH_LIST_PETS_MIXED_PARAMS: String = "/pet/paginated/mixed" + const val PATH_LIST_PETS_MULTIPLE_PARAMS: String = "/pet/paginated/multiParam" + const val PATH_LIST_PETS_NO_PAGINATION: String = "/pet/paginated/noPagination" + const val PATH_LIST_PETS_PARTIAL_PAGINATION: String = "/pet/paginated/partialParams" + const val PATH_LIST_PETS_WITH_FILTER_PAGINATED: String = "/pet/paginated/withParams" + const val PATH_LIST_PETS_WITH_HEADER_SIZE: String = "/pet/paginated/headerSize" const val PATH_UPDATE_PET: String = "/pet" const val PATH_UPDATE_PET_WITH_FORM: String = "/pet/{petId}" const val PATH_UPLOAD_FILE: String = "/pet/{petId}/uploadImage" diff --git a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/PetApiTest.kt b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/PetApiTest.kt deleted file mode 100644 index 4ee3b320cc3e..000000000000 --- a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/PetApiTest.kt +++ /dev/null @@ -1,129 +0,0 @@ -package org.openapitools.api - -import org.openapitools.model.ModelApiResponse -import org.openapitools.model.Pet -import org.junit.jupiter.api.Test -import org.springframework.http.ResponseEntity - -class PetApiTest { - - private val service: PetApiService = PetApiServiceImpl() - private val api: PetApiController = PetApiController(service) - - /** - * To test PetApiController.addPet - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun addPetTest() { - val body: Pet = TODO() - val response: ResponseEntity = api.addPet(body) - - // TODO: test validations - } - - /** - * To test PetApiController.deletePet - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun deletePetTest() { - val petId: kotlin.Long = TODO() - val apiKey: kotlin.String? = TODO() - val response: ResponseEntity = api.deletePet(petId, apiKey) - - // TODO: test validations - } - - /** - * To test PetApiController.findPetsByStatus - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun findPetsByStatusTest() { - val status: kotlin.collections.List = TODO() - val response: ResponseEntity> = api.findPetsByStatus(status) - - // TODO: test validations - } - - /** - * To test PetApiController.findPetsByTags - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun findPetsByTagsTest() { - val tags: kotlin.collections.List = TODO() - val response: ResponseEntity> = api.findPetsByTags(tags) - - // TODO: test validations - } - - /** - * To test PetApiController.getPetById - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun getPetByIdTest() { - val petId: kotlin.Long = TODO() - val response: ResponseEntity = api.getPetById(petId) - - // TODO: test validations - } - - /** - * To test PetApiController.updatePet - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun updatePetTest() { - val body: Pet = TODO() - val response: ResponseEntity = api.updatePet(body) - - // TODO: test validations - } - - /** - * To test PetApiController.updatePetWithForm - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun updatePetWithFormTest() { - val petId: kotlin.Long = TODO() - val name: kotlin.String? = TODO() - val status: kotlin.String? = TODO() - val response: ResponseEntity = api.updatePetWithForm(petId, name, status) - - // TODO: test validations - } - - /** - * To test PetApiController.uploadFile - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun uploadFileTest() { - val petId: kotlin.Long = TODO() - val additionalMetadata: kotlin.String? = TODO() - val file: org.springframework.web.multipart.MultipartFile? = TODO() - val response: ResponseEntity = api.uploadFile(petId, additionalMetadata, file) - - // TODO: test validations - } -} diff --git a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/StoreApiTest.kt b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/StoreApiTest.kt deleted file mode 100644 index 69e269b03ef1..000000000000 --- a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/StoreApiTest.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.openapitools.api - -import org.openapitools.model.Order -import org.junit.jupiter.api.Test -import org.springframework.http.ResponseEntity - -class StoreApiTest { - - private val service: StoreApiService = StoreApiServiceImpl() - private val api: StoreApiController = StoreApiController(service) - - /** - * To test StoreApiController.deleteOrder - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun deleteOrderTest() { - val orderId: kotlin.String = TODO() - val response: ResponseEntity = api.deleteOrder(orderId) - - // TODO: test validations - } - - /** - * To test StoreApiController.getInventory - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun getInventoryTest() { - val response: ResponseEntity> = api.getInventory() - - // TODO: test validations - } - - /** - * To test StoreApiController.getOrderById - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun getOrderByIdTest() { - val orderId: kotlin.Long = TODO() - val response: ResponseEntity = api.getOrderById(orderId) - - // TODO: test validations - } - - /** - * To test StoreApiController.placeOrder - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun placeOrderTest() { - val body: Order = TODO() - val response: ResponseEntity = api.placeOrder(body) - - // TODO: test validations - } -} diff --git a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/UserApiTest.kt b/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/UserApiTest.kt deleted file mode 100644 index eb20423da71c..000000000000 --- a/samples/server/petstore/kotlin-springboot-x-kotlin-implements/src/test/kotlin/org/openapitools/api/UserApiTest.kt +++ /dev/null @@ -1,124 +0,0 @@ -package org.openapitools.api - -import org.openapitools.model.User -import org.junit.jupiter.api.Test -import org.springframework.http.ResponseEntity - -class UserApiTest { - - private val service: UserApiService = UserApiServiceImpl() - private val api: UserApiController = UserApiController(service) - - /** - * To test UserApiController.createUser - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun createUserTest() { - val body: User = TODO() - val response: ResponseEntity = api.createUser(body) - - // TODO: test validations - } - - /** - * To test UserApiController.createUsersWithArrayInput - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun createUsersWithArrayInputTest() { - val body: kotlin.collections.List = TODO() - val response: ResponseEntity = api.createUsersWithArrayInput(body) - - // TODO: test validations - } - - /** - * To test UserApiController.createUsersWithListInput - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun createUsersWithListInputTest() { - val body: kotlin.collections.List = TODO() - val response: ResponseEntity = api.createUsersWithListInput(body) - - // TODO: test validations - } - - /** - * To test UserApiController.deleteUser - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun deleteUserTest() { - val username: kotlin.String = TODO() - val response: ResponseEntity = api.deleteUser(username) - - // TODO: test validations - } - - /** - * To test UserApiController.getUserByName - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun getUserByNameTest() { - val username: kotlin.String = TODO() - val response: ResponseEntity = api.getUserByName(username) - - // TODO: test validations - } - - /** - * To test UserApiController.loginUser - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun loginUserTest() { - val username: kotlin.String = TODO() - val password: kotlin.String = TODO() - val response: ResponseEntity = api.loginUser(username, password) - - // TODO: test validations - } - - /** - * To test UserApiController.logoutUser - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun logoutUserTest() { - val response: ResponseEntity = api.logoutUser() - - // TODO: test validations - } - - /** - * To test UserApiController.updateUser - * - * @throws ApiException - * if the Api call fails - */ - @Test - fun updateUserTest() { - val username: kotlin.String = TODO() - val body: User = TODO() - val response: ResponseEntity = api.updateUser(username, body) - - // TODO: test validations - } -} diff --git a/samples/server/petstore/kotlin-springboot/build.gradle.kts b/samples/server/petstore/kotlin-springboot/build.gradle.kts index 3507e44d515a..b3086b53f094 100644 --- a/samples/server/petstore/kotlin-springboot/build.gradle.kts +++ b/samples/server/petstore/kotlin-springboot/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("org.springframework.data:spring-data-commons") implementation("javax.validation:validation-api") implementation("javax.annotation:javax.annotation-api:1.3.2") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") diff --git a/samples/server/petstore/kotlin-springboot/pom.xml b/samples/server/petstore/kotlin-springboot/pom.xml index cab717f26a33..7a6726d7bbfd 100644 --- a/samples/server/petstore/kotlin-springboot/pom.xml +++ b/samples/server/petstore/kotlin-springboot/pom.xml @@ -85,6 +85,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.data + spring-data-commons +