Skip to content

Commit 88fbea3

Browse files
committed
update samples
1 parent 9af137f commit 88fbea3

7 files changed

Lines changed: 214 additions & 25 deletions

File tree

samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApi.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package org.openapitools.api
77

88
import org.openapitools.model.ModelApiResponse
99
import org.springframework.data.domain.Pageable
10+
import org.springframework.data.web.PageableDefault
1011
import org.openapitools.model.Pet
1112
import io.swagger.annotations.Api
1213
import io.swagger.annotations.ApiOperation
@@ -183,7 +184,7 @@ interface PetApi {
183184
produces = ["application/json"]
184185
)
185186
fun listAllPetsPaginated(@ApiParam(hidden = true) exchange: org.springframework.web.server.ServerWebExchange,
186-
@ApiParam(hidden = true) pageable: Pageable): ResponseEntity<Flow<Pet>> {
187+
@PageableDefault(page = 0, size = 20) @ApiParam(hidden = true) pageable: Pageable): ResponseEntity<Flow<Pet>> {
187188
return getDelegate().listAllPetsPaginated(exchange, pageable)
188189
}
189190

samples/server/petstore/kotlin-springboot-include-http-request-context-delegate/src/main/kotlin/org/openapitools/api/PetApiDelegate.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.openapitools.api
22

33
import org.openapitools.model.ModelApiResponse
44
import org.springframework.data.domain.Pageable
5+
import org.springframework.data.web.PageableDefault
56
import org.openapitools.model.Pet
67
import org.springframework.http.HttpStatus
78
import org.springframework.http.MediaType

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

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package org.openapitools.api
22

33
import org.springframework.data.domain.Pageable
4+
import org.springframework.data.web.PageableDefault
45
import org.openapitools.model.Pet
56
import org.openapitools.model.PetSort
7+
import org.springframework.data.domain.Sort
8+
import org.springframework.data.web.SortDefault
69
import org.openapitools.configuration.ValidSort
710
import org.springframework.http.HttpStatus
811
import org.springframework.http.MediaType
@@ -60,19 +63,94 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
6063
}
6164

6265

63-
@ValidSort(allowedValues = ["id,asc", "id,desc", "createdAt,asc", "createdAt,desc"])
66+
@RequestMapping(
67+
method = [RequestMethod.GET],
68+
// "/pet/findWithAllDefaults"
69+
value = [PATH_FIND_PETS_WITH_ALL_DEFAULTS],
70+
produces = ["application/json"]
71+
)
72+
fun findPetsWithAllDefaults(@PageableDefault(page = 0, size = 10) @SortDefault.SortDefaults(SortDefault(sort = ["name"], direction = Sort.Direction.DESC), SortDefault(sort = ["id"], direction = Sort.Direction.ASC)) pageable: Pageable): ResponseEntity<List<Pet>> {
73+
return ResponseEntity(service.findPetsWithAllDefaults(), HttpStatus.valueOf(200))
74+
}
75+
76+
77+
@RequestMapping(
78+
method = [RequestMethod.GET],
79+
// "/pet/findWithMixedSortDefaults"
80+
value = [PATH_FIND_PETS_WITH_MIXED_SORT_DEFAULTS],
81+
produces = ["application/json"]
82+
)
83+
fun findPetsWithMixedSortDefaults(@SortDefault.SortDefaults(SortDefault(sort = ["name"], direction = Sort.Direction.DESC), SortDefault(sort = ["id"], direction = Sort.Direction.ASC)) pageable: Pageable): ResponseEntity<List<Pet>> {
84+
return ResponseEntity(service.findPetsWithMixedSortDefaults(), HttpStatus.valueOf(200))
85+
}
86+
87+
88+
@RequestMapping(
89+
method = [RequestMethod.GET],
90+
// "/pet/findWithPageAndSizeConstraint"
91+
value = [PATH_FIND_PETS_WITH_PAGE_AND_SIZE_CONSTRAINT],
92+
produces = ["application/json"]
93+
)
94+
fun findPetsWithPageAndSizeConstraint(pageable: Pageable): ResponseEntity<List<Pet>> {
95+
return ResponseEntity(service.findPetsWithPageAndSizeConstraint(), HttpStatus.valueOf(200))
96+
}
97+
98+
99+
@RequestMapping(
100+
method = [RequestMethod.GET],
101+
// "/pet/findWithPageSizeDefaultsOnly"
102+
value = [PATH_FIND_PETS_WITH_PAGE_SIZE_DEFAULTS_ONLY],
103+
produces = ["application/json"]
104+
)
105+
fun findPetsWithPageSizeDefaultsOnly(@PageableDefault(page = 0, size = 25) pageable: Pageable): ResponseEntity<List<Pet>> {
106+
return ResponseEntity(service.findPetsWithPageSizeDefaultsOnly(), HttpStatus.valueOf(200))
107+
}
108+
109+
64110
@RequestMapping(
65111
method = [RequestMethod.GET],
66112
// "/pet/findWithRefSort"
67113
value = [PATH_FIND_PETS_WITH_REF_SORT],
68114
produces = ["application/json"]
69115
)
70-
fun findPetsWithRefSort(pageable: Pageable): ResponseEntity<List<Pet>> {
116+
fun findPetsWithRefSort(@ValidSort(allowedValues = ["id,asc", "id,desc", "createdAt,asc", "createdAt,desc"]) @PageableDefault(page = 0, size = 20) pageable: Pageable): ResponseEntity<List<Pet>> {
71117
return ResponseEntity(service.findPetsWithRefSort(), HttpStatus.valueOf(200))
72118
}
73119

74120

75-
@ValidSort(allowedValues = ["id,asc", "id,desc", "name,asc", "name,desc"])
121+
@RequestMapping(
122+
method = [RequestMethod.GET],
123+
// "/pet/findWithSizeConstraint"
124+
value = [PATH_FIND_PETS_WITH_SIZE_CONSTRAINT],
125+
produces = ["application/json"]
126+
)
127+
fun findPetsWithSizeConstraint(pageable: Pageable): ResponseEntity<List<Pet>> {
128+
return ResponseEntity(service.findPetsWithSizeConstraint(), HttpStatus.valueOf(200))
129+
}
130+
131+
132+
@RequestMapping(
133+
method = [RequestMethod.GET],
134+
// "/pet/findWithSortDefaultAsc"
135+
value = [PATH_FIND_PETS_WITH_SORT_DEFAULT_ASC],
136+
produces = ["application/json"]
137+
)
138+
fun findPetsWithSortDefaultAsc(@SortDefault.SortDefaults(SortDefault(sort = ["id"], direction = Sort.Direction.ASC)) pageable: Pageable): ResponseEntity<List<Pet>> {
139+
return ResponseEntity(service.findPetsWithSortDefaultAsc(), HttpStatus.valueOf(200))
140+
}
141+
142+
143+
@RequestMapping(
144+
method = [RequestMethod.GET],
145+
// "/pet/findWithSortDefaultOnly"
146+
value = [PATH_FIND_PETS_WITH_SORT_DEFAULT_ONLY],
147+
produces = ["application/json"]
148+
)
149+
fun findPetsWithSortDefaultOnly(@SortDefault.SortDefaults(SortDefault(sort = ["name"], direction = Sort.Direction.DESC)) pageable: Pageable): ResponseEntity<List<Pet>> {
150+
return ResponseEntity(service.findPetsWithSortDefaultOnly(), HttpStatus.valueOf(200))
151+
}
152+
153+
76154
@RequestMapping(
77155
method = [RequestMethod.GET],
78156
// "/pet/findByStatusWithSort"
@@ -81,7 +159,7 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
81159
)
82160
fun findPetsWithSortEnum(
83161
@Valid @RequestParam(value = "status", required = false) status: kotlin.String?,
84-
pageable: Pageable
162+
@ValidSort(allowedValues = ["id,asc", "id,desc", "name,asc", "name,desc"]) @PageableDefault(page = 0, size = 20) pageable: Pageable
85163
): ResponseEntity<List<Pet>> {
86164
return ResponseEntity(service.findPetsWithSortEnum(status), HttpStatus.valueOf(200))
87165
}
@@ -93,15 +171,22 @@ class PetApiController(@Autowired(required = true) val service: PetApiService) {
93171
value = [PATH_FIND_PETS_WITHOUT_SORT_ENUM],
94172
produces = ["application/json"]
95173
)
96-
fun findPetsWithoutSortEnum(pageable: Pageable): ResponseEntity<List<Pet>> {
174+
fun findPetsWithoutSortEnum(@PageableDefault(page = 0, size = 20) pageable: Pageable): ResponseEntity<List<Pet>> {
97175
return ResponseEntity(service.findPetsWithoutSortEnum(), HttpStatus.valueOf(200))
98176
}
99177

100178
companion object {
101179
//for your own safety never directly reuse these path definitions in tests
102180
const val PATH_FIND_PETS_AUTO_DETECTED_WITH_SORT: String = "/pet/findAutoDetectedWithSort"
103181
const val PATH_FIND_PETS_NON_PAGINATED_WITH_SORT_ENUM: String = "/pet/findNonPaginatedWithSortEnum"
182+
const val PATH_FIND_PETS_WITH_ALL_DEFAULTS: String = "/pet/findWithAllDefaults"
183+
const val PATH_FIND_PETS_WITH_MIXED_SORT_DEFAULTS: String = "/pet/findWithMixedSortDefaults"
184+
const val PATH_FIND_PETS_WITH_PAGE_AND_SIZE_CONSTRAINT: String = "/pet/findWithPageAndSizeConstraint"
185+
const val PATH_FIND_PETS_WITH_PAGE_SIZE_DEFAULTS_ONLY: String = "/pet/findWithPageSizeDefaultsOnly"
104186
const val PATH_FIND_PETS_WITH_REF_SORT: String = "/pet/findWithRefSort"
187+
const val PATH_FIND_PETS_WITH_SIZE_CONSTRAINT: String = "/pet/findWithSizeConstraint"
188+
const val PATH_FIND_PETS_WITH_SORT_DEFAULT_ASC: String = "/pet/findWithSortDefaultAsc"
189+
const val PATH_FIND_PETS_WITH_SORT_DEFAULT_ONLY: String = "/pet/findWithSortDefaultOnly"
105190
const val PATH_FIND_PETS_WITH_SORT_ENUM: String = "/pet/findByStatusWithSort"
106191
const val PATH_FIND_PETS_WITHOUT_SORT_ENUM: String = "/pet/findWithoutSortEnum"
107192
}

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package org.openapitools.api
22

33
import org.springframework.data.domain.Pageable
4+
import org.springframework.data.web.PageableDefault
45
import org.openapitools.model.Pet
56
import org.openapitools.model.PetSort
7+
import org.springframework.data.domain.Sort
8+
import org.springframework.data.web.SortDefault
69
import org.openapitools.configuration.ValidSort
710

811
interface PetApiService {
@@ -28,6 +31,38 @@ interface PetApiService {
2831
*/
2932
fun findPetsNonPaginatedWithSortEnum(sort: kotlin.String?): List<Pet>
3033

34+
/**
35+
* GET /pet/findWithAllDefaults : Find pets — page, size, and mixed sort defaults all present
36+
*
37+
* @return successful operation (status code 200)
38+
* @see PetApi#findPetsWithAllDefaults
39+
*/
40+
fun findPetsWithAllDefaults(): List<Pet>
41+
42+
/**
43+
* GET /pet/findWithMixedSortDefaults : Find pets — multiple sort defaults with mixed directions (array sort param)
44+
*
45+
* @return successful operation (status code 200)
46+
* @see PetApi#findPetsWithMixedSortDefaults
47+
*/
48+
fun findPetsWithMixedSortDefaults(): List<Pet>
49+
50+
/**
51+
* GET /pet/findWithPageAndSizeConstraint : Find pets — both page and size have maximum constraints
52+
*
53+
* @return successful operation (status code 200)
54+
* @see PetApi#findPetsWithPageAndSizeConstraint
55+
*/
56+
fun findPetsWithPageAndSizeConstraint(): List<Pet>
57+
58+
/**
59+
* GET /pet/findWithPageSizeDefaultsOnly : Find pets — page and size defaults only, no sort default
60+
*
61+
* @return successful operation (status code 200)
62+
* @see PetApi#findPetsWithPageSizeDefaultsOnly
63+
*/
64+
fun findPetsWithPageSizeDefaultsOnly(): List<Pet>
65+
3166
/**
3267
* GET /pet/findWithRefSort : Find pets with x-spring-paginated and $ref sort enum
3368
*
@@ -36,6 +71,30 @@ interface PetApiService {
3671
*/
3772
fun findPetsWithRefSort(): List<Pet>
3873

74+
/**
75+
* GET /pet/findWithSizeConstraint : Find pets — size has maximum constraint only
76+
*
77+
* @return successful operation (status code 200)
78+
* @see PetApi#findPetsWithSizeConstraint
79+
*/
80+
fun findPetsWithSizeConstraint(): List<Pet>
81+
82+
/**
83+
* GET /pet/findWithSortDefaultAsc : Find pets — sort default only (single field, no explicit direction defaults to ASC)
84+
*
85+
* @return successful operation (status code 200)
86+
* @see PetApi#findPetsWithSortDefaultAsc
87+
*/
88+
fun findPetsWithSortDefaultAsc(): List<Pet>
89+
90+
/**
91+
* GET /pet/findWithSortDefaultOnly : Find pets — sort default only (single field DESC, no page/size defaults)
92+
*
93+
* @return successful operation (status code 200)
94+
* @see PetApi#findPetsWithSortDefaultOnly
95+
*/
96+
fun findPetsWithSortDefaultOnly(): List<Pet>
97+
3998
/**
4099
* GET /pet/findByStatusWithSort : Find pets with explicit x-spring-paginated and inline sort enum
41100
*

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package org.openapitools.api
22

33
import org.springframework.data.domain.Pageable
4+
import org.springframework.data.web.PageableDefault
45
import org.openapitools.model.Pet
56
import org.openapitools.model.PetSort
7+
import org.springframework.data.domain.Sort
8+
import org.springframework.data.web.SortDefault
69
import org.openapitools.configuration.ValidSort
710
import org.springframework.stereotype.Service
811
@Service
@@ -16,10 +19,38 @@ class PetApiServiceImpl : PetApiService {
1619
TODO("Implement me")
1720
}
1821

22+
override fun findPetsWithAllDefaults(): List<Pet> {
23+
TODO("Implement me")
24+
}
25+
26+
override fun findPetsWithMixedSortDefaults(): List<Pet> {
27+
TODO("Implement me")
28+
}
29+
30+
override fun findPetsWithPageAndSizeConstraint(): List<Pet> {
31+
TODO("Implement me")
32+
}
33+
34+
override fun findPetsWithPageSizeDefaultsOnly(): List<Pet> {
35+
TODO("Implement me")
36+
}
37+
1938
override fun findPetsWithRefSort(): List<Pet> {
2039
TODO("Implement me")
2140
}
2241

42+
override fun findPetsWithSizeConstraint(): List<Pet> {
43+
TODO("Implement me")
44+
}
45+
46+
override fun findPetsWithSortDefaultAsc(): List<Pet> {
47+
TODO("Implement me")
48+
}
49+
50+
override fun findPetsWithSortDefaultOnly(): List<Pet> {
51+
TODO("Implement me")
52+
}
53+
2354
override fun findPetsWithSortEnum(status: kotlin.String?): List<Pet> {
2455
TODO("Implement me")
2556
}

samples/server/petstore/kotlin-springboot-sort-validation/src/main/kotlin/org/openapitools/configuration/ValidSort.kt

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,25 @@ import jakarta.validation.Constraint
44
import jakarta.validation.ConstraintValidator
55
import jakarta.validation.ConstraintValidatorContext
66
import jakarta.validation.Payload
7-
import jakarta.validation.constraintvalidation.SupportedValidationTarget
8-
import jakarta.validation.constraintvalidation.ValidationTarget
97
import org.springframework.data.domain.Pageable
108

119
/**
12-
* Validates that sort properties in a [Pageable] parameter match the allowed values.
10+
* Validates that sort properties in the annotated [Pageable] parameter match the allowed values.
1311
*
14-
* This annotation can only be applied to methods that have a [Pageable] parameter.
15-
* The validator checks that each sort property and direction combination in the [Pageable]
16-
* matches one of the strings specified in [allowedValues].
12+
* Apply directly on a `pageable: Pageable` parameter. The validator checks that each sort
13+
* property and direction combination in the [Pageable] matches one of the strings specified
14+
* in [allowedValues].
1715
*
18-
* Expected value format: `"property,direction"` (e.g. `"id,asc"`, `"name,desc"`).
16+
* Two formats are accepted in [allowedValues]:
17+
* - `"property,direction"` — permits only the specific direction (e.g. `"id,asc"`, `"name,desc"`).
18+
* Direction matching is case-insensitive: `"id,ASC"` and `"id,asc"` are treated identically.
19+
* - `"property"` — permits any direction for that property (e.g. `"id"` matches `sort=id,asc`
20+
* and `sort=id,desc`). Note: because Spring always normalises a bare `sort=id` to ascending
21+
* before the validator runs, bare property names in [allowedValues] effectively allow all
22+
* directions — the original omission of a direction cannot be detected.
23+
*
24+
* Both formats may be mixed freely. For example `["id", "name,desc"]` allows `id` in any
25+
* direction but restricts `name` to descending only.
1926
*
2027
* @property allowedValues The allowed sort strings (e.g. `["id,asc", "id,desc"]`)
2128
* @property groups Validation groups (optional)
@@ -25,34 +32,38 @@ import org.springframework.data.domain.Pageable
2532
@MustBeDocumented
2633
@Retention(AnnotationRetention.RUNTIME)
2734
@Constraint(validatedBy = [SortValidator::class])
28-
@Target(AnnotationTarget.FUNCTION)
35+
@Target(AnnotationTarget.VALUE_PARAMETER)
2936
annotation class ValidSort(
3037
val allowedValues: Array<String>,
3138
val groups: Array<kotlin.reflect.KClass<*>> = [],
3239
val payload: Array<kotlin.reflect.KClass<out Payload>> = [],
3340
val message: String = "Invalid sort column"
3441
)
3542

36-
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
37-
class SortValidator : ConstraintValidator<ValidSort, Array<Any?>> {
43+
class SortValidator : ConstraintValidator<ValidSort, Pageable> {
3844

3945
private lateinit var allowedValues: Set<String>
4046

4147
override fun initialize(constraintAnnotation: ValidSort) {
42-
allowedValues = constraintAnnotation.allowedValues.toSet()
48+
allowedValues = constraintAnnotation.allowedValues.map { entry ->
49+
DIRECTION_ASC_SUFFIX.replace(entry, ",asc")
50+
.let { DIRECTION_DESC_SUFFIX.replace(it, ",desc") }
51+
}.toSet()
52+
}
53+
54+
private companion object {
55+
val DIRECTION_ASC_SUFFIX = Regex(",ASC$", RegexOption.IGNORE_CASE)
56+
val DIRECTION_DESC_SUFFIX = Regex(",DESC$", RegexOption.IGNORE_CASE)
4357
}
4458

45-
override fun isValid(parameters: Array<Any?>?, context: ConstraintValidatorContext): Boolean {
46-
val pageable = parameters?.filterIsInstance<Pageable>()?.firstOrNull()
47-
?: throw IllegalStateException(
48-
"@ValidSort can only be used on methods with a Pageable parameter. " +
49-
"Ensure the annotated method has a parameter of type org.springframework.data.domain.Pageable."
50-
)
59+
override fun isValid(pageable: Pageable?, context: ConstraintValidatorContext): Boolean {
60+
if (pageable == null || pageable.sort.isUnsorted) return true
5161

5262
val invalid = pageable.sort
5363
.foldIndexed(emptyMap<Int, String>()) { index, acc, order ->
5464
val sortValue = "${order.property},${order.direction.name.lowercase()}"
55-
if (sortValue !in allowedValues) acc + (index to order.property)
65+
// Accept "property,direction" (exact match) OR "property" alone (any direction allowed)
66+
if (sortValue !in allowedValues && order.property !in allowedValues) acc + (index to order.property)
5667
else acc
5768
}
5869
.toSortedMap()

0 commit comments

Comments
 (0)