Skip to content

Commit f1935b4

Browse files
committed
improve sample
1 parent 9728c9a commit f1935b4

6 files changed

Lines changed: 87 additions & 2 deletions

File tree

bin/configs/kotlin-spring-boot-sort-validation.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ additionalProperties:
1212
beanValidations: "true"
1313
useSpringBoot3: "true"
1414
generateSortValidation: "true"
15+
generatePageableConstraintValidation: "true"
1516
useTags: "true"
1617
requestMappingMode: api_interface

samples/server/petstore/kotlin-springboot-sort-validation/.openapi-generator/FILES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ src/main/kotlin/org/openapitools/api/PetApiController.kt
1313
src/main/kotlin/org/openapitools/api/PetApiService.kt
1414
src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt
1515
src/main/kotlin/org/openapitools/configuration/EnumConverterConfiguration.kt
16+
src/main/kotlin/org/openapitools/configuration/ValidPageable.kt
1617
src/main/kotlin/org/openapitools/configuration/ValidSort.kt
1718
src/main/kotlin/org/openapitools/model/Pet.kt
1819
src/main/kotlin/org/openapitools/model/PetSort.kt

samples/server/petstore/kotlin-springboot-sort-validation/src/main/kotlin/org/openapitools/api/PetApiController.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.openapitools.model.Pet
66
import org.openapitools.model.PetSort
77
import org.springframework.data.domain.Sort
88
import org.springframework.data.web.SortDefault
9+
import org.openapitools.configuration.ValidPageable
910
import org.openapitools.configuration.ValidSort
1011
import org.springframework.http.HttpStatus
1112
import org.springframework.http.MediaType
@@ -91,7 +92,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
9192
value = [PATH_FIND_PETS_WITH_PAGE_AND_SIZE_CONSTRAINT],
9293
produces = ["application/json"]
9394
)
94-
fun findPetsWithPageAndSizeConstraint(pageable: Pageable): ResponseEntity<List<Pet>> {
95+
fun findPetsWithPageAndSizeConstraint(@ValidPageable(maxSize = 50, maxPage = 999) pageable: Pageable): ResponseEntity<List<Pet>> {
9596
return ResponseEntity(service.findPetsWithPageAndSizeConstraint(), HttpStatus.valueOf(200))
9697
}
9798

@@ -124,7 +125,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
124125
value = [PATH_FIND_PETS_WITH_SIZE_CONSTRAINT],
125126
produces = ["application/json"]
126127
)
127-
fun findPetsWithSizeConstraint(pageable: Pageable): ResponseEntity<List<Pet>> {
128+
fun findPetsWithSizeConstraint(@ValidPageable(maxSize = 100) pageable: Pageable): ResponseEntity<List<Pet>> {
128129
return ResponseEntity(service.findPetsWithSizeConstraint(), HttpStatus.valueOf(200))
129130
}
130131

samples/server/petstore/kotlin-springboot-sort-validation/src/main/kotlin/org/openapitools/api/PetApiService.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.openapitools.model.Pet
66
import org.openapitools.model.PetSort
77
import org.springframework.data.domain.Sort
88
import org.springframework.data.web.SortDefault
9+
import org.openapitools.configuration.ValidPageable
910
import org.openapitools.configuration.ValidSort
1011

1112
interface PetApiService {

samples/server/petstore/kotlin-springboot-sort-validation/src/main/kotlin/org/openapitools/api/PetApiServiceImpl.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.openapitools.model.Pet
66
import org.openapitools.model.PetSort
77
import org.springframework.data.domain.Sort
88
import org.springframework.data.web.SortDefault
9+
import org.openapitools.configuration.ValidPageable
910
import org.openapitools.configuration.ValidSort
1011
import org.springframework.stereotype.Service
1112
@Service
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.openapitools.configuration
2+
3+
import jakarta.validation.Constraint
4+
import jakarta.validation.ConstraintValidator
5+
import jakarta.validation.ConstraintValidatorContext
6+
import jakarta.validation.Payload
7+
import org.springframework.data.domain.Pageable
8+
9+
/**
10+
* Validates that the page number and page size in the annotated [Pageable] parameter do not
11+
* exceed their configured maximums.
12+
*
13+
* Apply directly on a `pageable: Pageable` parameter. Each attribute is independently optional:
14+
* - [maxSize] — when set (>= 0), validates `pageable.pageSize <= maxSize`
15+
* - [maxPage] — when set (>= 0), validates `pageable.pageNumber <= maxPage`
16+
*
17+
* Use [NO_LIMIT] (= -1, the default) to leave an attribute unconstrained.
18+
*
19+
* Constraining [maxPage] is useful to prevent deep-pagination attacks, where a large page
20+
* offset (e.g. `?page=100000&size=20`) causes an expensive `OFFSET` query on the database.
21+
*
22+
* @property maxSize Maximum allowed page size, or [NO_LIMIT] if unconstrained
23+
* @property maxPage Maximum allowed page number (0-based), or [NO_LIMIT] if unconstrained
24+
* @property groups Validation groups (optional)
25+
* @property payload Additional payload (optional)
26+
* @property message Validation error message (default: "Invalid page request")
27+
*/
28+
@MustBeDocumented
29+
@Retention(AnnotationRetention.RUNTIME)
30+
@Constraint(validatedBy = [PageableConstraintValidator::class])
31+
@Target(AnnotationTarget.VALUE_PARAMETER)
32+
annotation class ValidPageable(
33+
val maxSize: Int = ValidPageable.NO_LIMIT,
34+
val maxPage: Int = ValidPageable.NO_LIMIT,
35+
val groups: Array<kotlin.reflect.KClass<*>> = [],
36+
val payload: Array<kotlin.reflect.KClass<out Payload>> = [],
37+
val message: String = "Invalid page request"
38+
) {
39+
companion object {
40+
const val NO_LIMIT = -1
41+
}
42+
}
43+
44+
class PageableConstraintValidator : ConstraintValidator<ValidPageable, Pageable> {
45+
46+
private var maxSize = ValidPageable.NO_LIMIT
47+
private var maxPage = ValidPageable.NO_LIMIT
48+
49+
override fun initialize(constraintAnnotation: ValidPageable) {
50+
maxSize = constraintAnnotation.maxSize
51+
maxPage = constraintAnnotation.maxPage
52+
}
53+
54+
override fun isValid(pageable: Pageable?, context: ConstraintValidatorContext): Boolean {
55+
if (pageable == null) return true
56+
57+
var valid = true
58+
context.disableDefaultConstraintViolation()
59+
60+
if (maxSize >= 0 && pageable.pageSize > maxSize) {
61+
context.buildConstraintViolationWithTemplate(
62+
"${context.defaultConstraintMessageTemplate}: page size ${pageable.pageSize} exceeds maximum $maxSize"
63+
)
64+
.addPropertyNode("size")
65+
.addConstraintViolation()
66+
valid = false
67+
}
68+
69+
if (maxPage >= 0 && pageable.pageNumber > maxPage) {
70+
context.buildConstraintViolationWithTemplate(
71+
"${context.defaultConstraintMessageTemplate}: page number ${pageable.pageNumber} exceeds maximum $maxPage"
72+
)
73+
.addPropertyNode("page")
74+
.addConstraintViolation()
75+
valid = false
76+
}
77+
78+
return valid
79+
}
80+
}

0 commit comments

Comments
 (0)