Skip to content

Commit d527139

Browse files
committed
Use common base class for validation exceptions
1 parent 9048497 commit d527139

File tree

10 files changed

+57
-45
lines changed

10 files changed

+57
-45
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.papsign.ktor.openapigen.annotations.type.common
2+
3+
import java.lang.Exception
4+
5+
abstract class ConstraintViolation(defaultMessage: String, message: String = "", cause: Throwable? = null)
6+
: Exception(if (message.isEmpty()) defaultMessage else message, cause)

src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package com.papsign.ktor.openapigen.annotations.type.number
22

3+
import com.papsign.ktor.openapigen.annotations.type.common.ConstraintViolation
34
import com.papsign.ktor.openapigen.classLogger
45
import com.papsign.ktor.openapigen.model.schema.SchemaModel
56
import com.papsign.ktor.openapigen.schema.processor.SchemaProcessor
67
import com.papsign.ktor.openapigen.validation.Validator
78
import com.papsign.ktor.openapigen.validation.ValidatorBuilder
8-
import java.lang.Exception
99
import java.math.BigDecimal
1010
import kotlin.reflect.KType
1111
import kotlin.reflect.full.withNullability
@@ -61,9 +61,7 @@ abstract class NumberConstraintProcessor<A: Annotation>(allowedTypes: Iterable<K
6161

6262
data class NumberConstraint(val min: BigDecimal? = null, val max: BigDecimal? = null, val minInclusive: Boolean = true, val maxInclusive: Boolean = true)
6363

64-
open class ConstraintVialoation(message: String, cause: Throwable? = null): Exception(message, cause)
65-
66-
class NumberConstraintViolation(val actual: Number?, val constraint: NumberConstraint): ConstraintVialoation("Constraint violation: $actual should be ${
64+
class NumberConstraintViolation(val actual: Number?, val constraint: NumberConstraint): ConstraintViolation("Constraint violation: $actual should be ${
6765
{
6866
val min = "${constraint.min} ${if (constraint.minInclusive) "inclusive" else "exclusive"}"
6967
val max = "${constraint.max} ${if (constraint.maxInclusive) "inclusive" else "exclusive"}"
@@ -76,4 +74,4 @@ class NumberConstraintViolation(val actual: Number?, val constraint: NumberConst
7674
}()
7775
}")
7876

79-
class NotANumberViolationViolation(val value: Any?): ConstraintVialoation("Constraint violation: $value is not a number")
77+
class NotANumberViolationViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a number")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.papsign.ktor.openapigen.annotations.type.string
2+
3+
import com.papsign.ktor.openapigen.annotations.type.common.ConstraintViolation
4+
5+
class NotAStringViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a string")

src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package com.papsign.ktor.openapigen.annotations.type.string.length
22

3+
import com.papsign.ktor.openapigen.annotations.type.common.ConstraintViolation
4+
import com.papsign.ktor.openapigen.annotations.type.string.NotAStringViolation
35
import com.papsign.ktor.openapigen.classLogger
46
import com.papsign.ktor.openapigen.getKType
57
import com.papsign.ktor.openapigen.model.schema.SchemaModel
68
import com.papsign.ktor.openapigen.schema.processor.SchemaProcessor
79
import com.papsign.ktor.openapigen.validation.Validator
810
import com.papsign.ktor.openapigen.validation.ValidatorBuilder
9-
import java.lang.Exception
1011
import kotlin.reflect.KType
1112
import kotlin.reflect.full.withNullability
1213

@@ -55,11 +56,9 @@ abstract class LengthConstraintProcessor<A: Annotation>(): SchemaProcessor<A>, V
5556
}
5657
}
5758

58-
data class LengthConstraint(val min: Int? = null, val max: Int? = null, val errorMessage: String? = null)
59+
data class LengthConstraint(val min: Int? = null, val max: Int? = null, val errorMessage: String)
5960

60-
open class ConstraintViolation(message: String, cause: Throwable? = null): Exception(message, cause)
61-
62-
class LengthConstraintViolation(val actual: Number?, val constraint: LengthConstraint): ConstraintViolation(constraint.errorMessage ?: "Constraint violation: the length of the string should be ${
61+
class LengthConstraintViolation(val actual: Number?, val constraint: LengthConstraint): ConstraintViolation("Constraint violation: the length of the string should be ${
6362
{
6463
val min = "${constraint.min}"
6564
val max = "${constraint.max}"
@@ -70,6 +69,4 @@ class LengthConstraintViolation(val actual: Number?, val constraint: LengthConst
7069
else -> "anything"
7170
}
7271
}()
73-
}, but it is $actual")
74-
75-
class NotAStringViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a string")
72+
}, but it is $actual", constraint.errorMessage)

src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ object LengthProcessor : LengthConstraintProcessor<Length>() {
1212
}
1313

1414
override fun getConstraint(annotation: Length): LengthConstraint {
15-
val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null
16-
return LengthConstraint(min = annotation.min, max = annotation.max, errorMessage = errorMessage)
15+
return LengthConstraint(min = annotation.min, max = annotation.max, errorMessage = annotation.message)
1716
}
1817
}

src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ object MaxLengthProcessor : LengthConstraintProcessor<MaxLength>() {
1111
}
1212

1313
override fun getConstraint(annotation: MaxLength): LengthConstraint {
14-
val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null
15-
return LengthConstraint(max = annotation.value, errorMessage = errorMessage)
14+
return LengthConstraint(max = annotation.value, errorMessage = annotation.message)
1615
}
1716
}

src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ object MinLengthProcessor : LengthConstraintProcessor<MinLength>() {
1111
}
1212

1313
override fun getConstraint(annotation: MinLength): LengthConstraint {
14-
val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null
15-
return LengthConstraint(min = annotation.value, errorMessage = errorMessage)
14+
return LengthConstraint(min = annotation.value, errorMessage = annotation.message)
1615
}
1716
}

src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package com.papsign.ktor.openapigen.annotations.type.string.pattern
22

3+
import com.papsign.ktor.openapigen.annotations.type.common.ConstraintViolation
4+
import com.papsign.ktor.openapigen.annotations.type.string.NotAStringViolation
35
import com.papsign.ktor.openapigen.classLogger
46
import com.papsign.ktor.openapigen.getKType
57
import com.papsign.ktor.openapigen.model.schema.SchemaModel
68
import com.papsign.ktor.openapigen.schema.processor.SchemaProcessor
79
import com.papsign.ktor.openapigen.validation.Validator
810
import com.papsign.ktor.openapigen.validation.ValidatorBuilder
9-
import java.lang.Exception
1011
import kotlin.reflect.KType
1112
import kotlin.reflect.full.withNullability
1213

@@ -51,11 +52,7 @@ abstract class RegularExpressionConstraintProcessor<A: Annotation>(): SchemaProc
5152
}
5253
}
5354

54-
data class RegularExpressionConstraint(val pattern: String, val errorMessage: String? = null)
55+
data class RegularExpressionConstraint(val pattern: String, val errorMessage: String)
5556

56-
open class ConstraintViolation(message: String, cause: Throwable? = null): Exception(message, cause)
57-
58-
class RegularExpressionConstraintViolation(val actual: String?, val constraint: RegularExpressionConstraint): ConstraintViolation(constraint.errorMessage ?: "Constraint violation: the string " +
59-
"\'$actual\' does not match the regular expression ${constraint.pattern}")
60-
61-
class NotAStringViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a string")
57+
class RegularExpressionConstraintViolation(val actual: String?, val constraint: RegularExpressionConstraint): ConstraintViolation("Constraint violation: the string " +
58+
"'$actual' does not match the regular expression ${constraint.pattern}", constraint.errorMessage)

src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ object RegularExpressionProcessor : RegularExpressionConstraintProcessor<Regular
1111
}
1212

1313
override fun getConstraint(annotation: RegularExpression): RegularExpressionConstraint {
14-
val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null
15-
return RegularExpressionConstraint(annotation.pattern, errorMessage)
14+
return RegularExpressionConstraint(annotation.pattern, annotation.message)
1615
}
1716
}

src/test/kotlin/TestServer.kt

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ import com.papsign.ktor.openapigen.annotations.parameters.PathParam
1818
import com.papsign.ktor.openapigen.annotations.properties.description.Description
1919
import com.papsign.ktor.openapigen.annotations.type.`object`.example.ExampleProvider
2020
import com.papsign.ktor.openapigen.annotations.type.`object`.example.WithExample
21+
import com.papsign.ktor.openapigen.annotations.type.common.ConstraintViolation
22+
import com.papsign.ktor.openapigen.annotations.type.number.integer.clamp.Clamp
23+
import com.papsign.ktor.openapigen.annotations.type.number.integer.max.Max
24+
import com.papsign.ktor.openapigen.annotations.type.number.integer.min.Min
25+
import com.papsign.ktor.openapigen.annotations.type.string.example.StringExample
26+
import com.papsign.ktor.openapigen.annotations.type.string.length.Length
27+
import com.papsign.ktor.openapigen.annotations.type.string.length.MaxLength
28+
import com.papsign.ktor.openapigen.annotations.type.string.length.MinLength
29+
import com.papsign.ktor.openapigen.annotations.type.string.pattern.RegularExpression
2130
import com.papsign.ktor.openapigen.interop.withAPI
2231
import com.papsign.ktor.openapigen.model.Described
2332
import com.papsign.ktor.openapigen.model.server.ServerModel
@@ -28,16 +37,6 @@ import com.papsign.ktor.openapigen.route.path.normal.post
2837
import com.papsign.ktor.openapigen.route.response.respond
2938
import com.papsign.ktor.openapigen.schema.namer.DefaultSchemaNamer
3039
import com.papsign.ktor.openapigen.schema.namer.SchemaNamer
31-
import com.papsign.ktor.openapigen.annotations.type.number.ConstraintVialoation
32-
import com.papsign.ktor.openapigen.annotations.type.number.integer.clamp.Clamp
33-
import com.papsign.ktor.openapigen.annotations.type.number.integer.max.Max
34-
import com.papsign.ktor.openapigen.annotations.type.number.integer.min.Min
35-
import com.papsign.ktor.openapigen.annotations.type.string.example.StringExample
36-
import com.papsign.ktor.openapigen.annotations.type.string.length.ConstraintViolation
37-
import com.papsign.ktor.openapigen.annotations.type.string.length.Length
38-
import com.papsign.ktor.openapigen.annotations.type.string.length.MaxLength
39-
import com.papsign.ktor.openapigen.annotations.type.string.length.MinLength
40-
import com.papsign.ktor.openapigen.annotations.type.string.pattern.RegularExpression
4140
import io.ktor.application.application
4241
import io.ktor.application.call
4342
import io.ktor.application.install
@@ -115,15 +114,9 @@ object TestServer {
115114
it.printStackTrace()
116115
Error("mapping.json", it.localizedMessage)
117116
}
118-
exception<ConstraintVialoation, Error>(HttpStatusCode.BadRequest) {
119-
Error("violation.constraint", it.localizedMessage)
120-
}
121117
exception<ConstraintViolation, Error>(HttpStatusCode.BadRequest) {
122118
Error("violation.constraint", it.localizedMessage)
123119
}
124-
exception<com.papsign.ktor.openapigen.annotations.type.string.pattern.ConstraintViolation, Error>(HttpStatusCode.BadRequest) {
125-
Error("violation.constraint", it.localizedMessage)
126-
}
127120
exception<ProperException, Error>(HttpStatusCode.BadRequest) {
128121
it.printStackTrace()
129122
Error(it.id, it.localizedMessage)
@@ -210,6 +203,18 @@ object TestServer {
210203
respond(StringResponse("All of the fields were valid"))
211204
}
212205

206+
route("validate-number").post<Unit, StringResponse, NumberValidatorsExample>(
207+
info("This endpoint demonstrates the usage of number validators", "This endpoint demonstrates the usage of number validators"),
208+
exampleRequest = NumberValidatorsExample(
209+
1,
210+
56,
211+
15.02f,
212+
0.023f),
213+
exampleResponse = StringResponse("All of the fields were valid")
214+
) { params, body ->
215+
respond(StringResponse("All of the fields were valid"))
216+
}
217+
213218
route("again") {
214219
tag(TestServer.Tags.EXAMPLE) {
215220

@@ -262,6 +267,14 @@ object TestServer {
262267
@RegularExpression("^[0-9a-fA-F]*$", "The field strHexaDec should only contain hexadecimal digits") val strHexaDec: String
263268
)
264269

270+
@Request("A Request with validated number fields")
271+
data class NumberValidatorsExample(
272+
@Min(0) val intWithMin: Int,
273+
@Clamp( 1, 90 ) val intBetween: Int,
274+
@Max(100 ) val floatMax: Float,
275+
@Clamp(0, 1) val floatBetween: Float
276+
)
277+
265278
@Response("A String Response")
266279
@Request("A String Request")
267280
data class StringUsable(val str: String)

0 commit comments

Comments
 (0)