From fcbbb73e7d827d5a3224bd4b1b8e35cf3be6ba4e Mon Sep 17 00:00:00 2001 From: bargergo Date: Sun, 24 May 2020 09:38:25 +0200 Subject: [PATCH 01/12] Added string length validators, string schemamodel --- .../annotations/type/string/length/Length.kt | 9 +++ .../length/LengthConstraintProcessor.kt | 75 +++++++++++++++++++ .../type/string/length/LengthProcessor.kt | 17 +++++ .../type/string/length/MaxLength.kt | 9 +++ .../type/string/length/MaxLengthProcessor.kt | 16 ++++ .../type/string/length/MinLength.kt | 9 +++ .../type/string/length/MinLengthProcessor.kt | 16 ++++ .../openapigen/model/schema/SchemaModel.kt | 12 +++ 8 files changed, 163 insertions(+) create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/Length.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/Length.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/Length.kt new file mode 100644 index 000000000..1a105b125 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/Length.kt @@ -0,0 +1,9 @@ +package com.papsign.ktor.openapigen.annotations.type.string.length + +import com.papsign.ktor.openapigen.schema.processor.SchemaProcessorAnnotation +import com.papsign.ktor.openapigen.validation.ValidatorAnnotation + +@Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) +@SchemaProcessorAnnotation(LengthProcessor::class) +@ValidatorAnnotation(LengthProcessor::class) +annotation class Length(val min: Int, val max: Int, val message: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt new file mode 100644 index 000000000..5be5277e2 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt @@ -0,0 +1,75 @@ +package com.papsign.ktor.openapigen.annotations.type.string.length + +import com.papsign.ktor.openapigen.classLogger +import com.papsign.ktor.openapigen.getKType +import com.papsign.ktor.openapigen.model.schema.SchemaModel +import com.papsign.ktor.openapigen.schema.processor.SchemaProcessor +import com.papsign.ktor.openapigen.validation.Validator +import com.papsign.ktor.openapigen.validation.ValidatorBuilder +import java.lang.Exception +import kotlin.reflect.KType +import kotlin.reflect.full.withNullability + +abstract class LengthConstraintProcessor(): SchemaProcessor, ValidatorBuilder { + + private val log = classLogger() + + val types = listOf(getKType().withNullability(true), getKType().withNullability(false)) + + abstract fun process(model: SchemaModel.SchemaModelString, annotation: A): SchemaModel.SchemaModelString + + abstract fun getConstraint(annotation: A): LengthConstraint + + private class LengthConstraintValidator(private val constraint: LengthConstraint): Validator { + override fun validate(subject: T?): T? { + if (subject is String?) { + val value = subject?.length ?: 0 + if (constraint.min != null) { + if (value < constraint.min) throw LengthConstraintViolation(value, constraint) + } + if (constraint.max != null) { + if (value > constraint.max) throw LengthConstraintViolation(value, constraint) + } + } else { + throw NotAStringViolation(subject) + } + return subject + } + } + + override fun build(type: KType, annotation: A): Validator { + return if (types.contains(type)) { + LengthConstraintValidator(getConstraint(annotation)) + } else { + error("${annotation::class} can only be used on types: $types") + } + } + + override fun process(model: SchemaModel<*>, type: KType, annotation: A): SchemaModel<*> { + return if (model is SchemaModel.SchemaModelString && types.contains(type)) { + process(model, annotation) + } else { + log.warn("${annotation::class} can only be used on types: $types") + model + } + } +} + +data class LengthConstraint(val min: Int? = null, val max: Int? = null, val errorMessage: String? = null) + +open class ConstraintViolation(message: String, cause: Throwable? = null): Exception(message, cause) + +class LengthConstraintViolation(val actual: Number?, val constraint: LengthConstraint): ConstraintViolation(constraint.errorMessage ?: "Constraint violation: the length of the string should be ${ +{ + val min = "${constraint.min}" + val max = "${constraint.max}" + when { + constraint.min != null && constraint.max != null -> "between $min and $max" + constraint.min != null -> "at least $min" + constraint.max != null -> "at most $max" + else -> "anything" + } +}() +}, but it is $actual") + +class NotAStringViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a string") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt new file mode 100644 index 000000000..4becf4763 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt @@ -0,0 +1,17 @@ +package com.papsign.ktor.openapigen.annotations.type.string.length + +import com.papsign.ktor.openapigen.model.schema.SchemaModel + +object LengthProcessor : LengthConstraintProcessor() { + override fun process(model: SchemaModel.SchemaModelString, annotation: Length): SchemaModel.SchemaModelString { + return model.apply { + maxLength = annotation.max + minLength = annotation.min + } + } + + override fun getConstraint(annotation: Length): LengthConstraint { + val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null + return LengthConstraint(min = annotation.min, max = annotation.max, errorMessage = errorMessage) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt new file mode 100644 index 000000000..21fcfcee9 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt @@ -0,0 +1,9 @@ +package com.papsign.ktor.openapigen.annotations.type.string.length + +import com.papsign.ktor.openapigen.schema.processor.SchemaProcessorAnnotation +import com.papsign.ktor.openapigen.validation.ValidatorAnnotation + +@Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) +@SchemaProcessorAnnotation(LengthProcessor::class) +@ValidatorAnnotation(LengthProcessor::class) +annotation class MaxLength(val value: Int, val message: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt new file mode 100644 index 000000000..b9e4829e8 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt @@ -0,0 +1,16 @@ +package com.papsign.ktor.openapigen.annotations.type.string.length + +import com.papsign.ktor.openapigen.model.schema.SchemaModel + +object MaxLengthProcessor : LengthConstraintProcessor() { + override fun process(model: SchemaModel.SchemaModelString, annotation: MaxLength): SchemaModel.SchemaModelString { + return model.apply { + maxLength = annotation.value + } + } + + override fun getConstraint(annotation: MaxLength): LengthConstraint { + val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null + return LengthConstraint(max = annotation.value, errorMessage = errorMessage) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt new file mode 100644 index 000000000..a83cfdcfb --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt @@ -0,0 +1,9 @@ +package com.papsign.ktor.openapigen.annotations.type.string.length + +import com.papsign.ktor.openapigen.schema.processor.SchemaProcessorAnnotation +import com.papsign.ktor.openapigen.validation.ValidatorAnnotation + +@Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) +@SchemaProcessorAnnotation(LengthProcessor::class) +@ValidatorAnnotation(LengthProcessor::class) +annotation class MinLength(val value: Int, val message: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt new file mode 100644 index 000000000..5024a49b0 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt @@ -0,0 +1,16 @@ +package com.papsign.ktor.openapigen.annotations.type.string.length + +import com.papsign.ktor.openapigen.model.schema.SchemaModel + +object MinLengthProcessor : LengthConstraintProcessor() { + override fun process(model: SchemaModel.SchemaModelString, annotation: MinLength): SchemaModel.SchemaModelString { + return model.apply { + minLength = annotation.value + } + } + + override fun getConstraint(annotation: MinLength): LengthConstraint { + val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null + return LengthConstraint(min = annotation.value, errorMessage = errorMessage) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt b/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt index f5f3671f2..7556269df 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt @@ -60,6 +60,18 @@ sealed class SchemaModel: DataModel { override var description: String? = null ) : SchemaModel() + data class SchemaModelString( + var type: DataType? = null, + var format: DataFormat? = null, + var nullable: Boolean = false, + var minLength: Int? = null, + var maxLength: Int? = null, + var pattern: String? = null, + override var example: String? = null, + override var examples: List? = null, + override var description: String? = null + ) : SchemaModel() + data class SchemaModelRef(override val `$ref`: String) : SchemaModel(), RefModel> { override var example: T? = null override var examples: List? = null From e4442c08c4a92775141d270362907c4670cab0d7 Mon Sep 17 00:00:00 2001 From: bargergo Date: Sun, 24 May 2020 10:04:03 +0200 Subject: [PATCH 02/12] Fix validator, schemaprocessor annotation parameters --- .../openapigen/annotations/type/string/length/MaxLength.kt | 4 ++-- .../openapigen/annotations/type/string/length/MinLength.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt index 21fcfcee9..2f2699869 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt @@ -4,6 +4,6 @@ import com.papsign.ktor.openapigen.schema.processor.SchemaProcessorAnnotation import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) -@SchemaProcessorAnnotation(LengthProcessor::class) -@ValidatorAnnotation(LengthProcessor::class) +@SchemaProcessorAnnotation(MaxLengthProcessor::class) +@ValidatorAnnotation(MaxLengthProcessor::class) annotation class MaxLength(val value: Int, val message: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt index a83cfdcfb..d86e039ff 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt @@ -4,6 +4,6 @@ import com.papsign.ktor.openapigen.schema.processor.SchemaProcessorAnnotation import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) -@SchemaProcessorAnnotation(LengthProcessor::class) -@ValidatorAnnotation(LengthProcessor::class) +@SchemaProcessorAnnotation(MinLengthProcessor::class) +@ValidatorAnnotation(MinLengthProcessor::class) annotation class MinLength(val value: Int, val message: String = "") \ No newline at end of file From 582d240edb37fc8750138eee14cd6a50a00e8cdf Mon Sep 17 00:00:00 2001 From: bargergo Date: Mon, 25 May 2020 23:22:40 +0200 Subject: [PATCH 03/12] Add the new string attributes to SchemaModelLitteral instead of SchemaModelString --- .../string/length/LengthConstraintProcessor.kt | 4 ++-- .../type/string/length/LengthProcessor.kt | 5 +++-- .../type/string/length/MaxLengthProcessor.kt | 5 +++-- .../type/string/length/MinLengthProcessor.kt | 5 +++-- .../ktor/openapigen/model/schema/SchemaModel.kt | 15 +++------------ 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt index 5be5277e2..dff1e7771 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthConstraintProcessor.kt @@ -16,7 +16,7 @@ abstract class LengthConstraintProcessor(): SchemaProcessor, V val types = listOf(getKType().withNullability(true), getKType().withNullability(false)) - abstract fun process(model: SchemaModel.SchemaModelString, annotation: A): SchemaModel.SchemaModelString + abstract fun process(model: SchemaModel.SchemaModelLitteral<*>, annotation: A): SchemaModel.SchemaModelLitteral<*> abstract fun getConstraint(annotation: A): LengthConstraint @@ -46,7 +46,7 @@ abstract class LengthConstraintProcessor(): SchemaProcessor, V } override fun process(model: SchemaModel<*>, type: KType, annotation: A): SchemaModel<*> { - return if (model is SchemaModel.SchemaModelString && types.contains(type)) { + return if (model is SchemaModel.SchemaModelLitteral<*> && types.contains(type)) { process(model, annotation) } else { log.warn("${annotation::class} can only be used on types: $types") diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt index 4becf4763..829f0761c 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt @@ -3,8 +3,9 @@ package com.papsign.ktor.openapigen.annotations.type.string.length import com.papsign.ktor.openapigen.model.schema.SchemaModel object LengthProcessor : LengthConstraintProcessor() { - override fun process(model: SchemaModel.SchemaModelString, annotation: Length): SchemaModel.SchemaModelString { - return model.apply { + override fun process(model: SchemaModel.SchemaModelLitteral<*>, annotation: Length): SchemaModel.SchemaModelLitteral<*> { + @Suppress("UNCHECKED_CAST") + return (model as SchemaModel.SchemaModelLitteral).apply { maxLength = annotation.max minLength = annotation.min } diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt index b9e4829e8..7fafa369c 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt @@ -3,8 +3,9 @@ package com.papsign.ktor.openapigen.annotations.type.string.length import com.papsign.ktor.openapigen.model.schema.SchemaModel object MaxLengthProcessor : LengthConstraintProcessor() { - override fun process(model: SchemaModel.SchemaModelString, annotation: MaxLength): SchemaModel.SchemaModelString { - return model.apply { + override fun process(model: SchemaModel.SchemaModelLitteral<*>, annotation: MaxLength): SchemaModel.SchemaModelLitteral<*> { + @Suppress("UNCHECKED_CAST") + return (model as SchemaModel.SchemaModelLitteral).apply { maxLength = annotation.value } } diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt index 5024a49b0..3a2f235f2 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt @@ -3,8 +3,9 @@ package com.papsign.ktor.openapigen.annotations.type.string.length import com.papsign.ktor.openapigen.model.schema.SchemaModel object MinLengthProcessor : LengthConstraintProcessor() { - override fun process(model: SchemaModel.SchemaModelString, annotation: MinLength): SchemaModel.SchemaModelString { - return model.apply { + override fun process(model: SchemaModel.SchemaModelLitteral<*>, annotation: MinLength): SchemaModel.SchemaModelLitteral<*> { + @Suppress("UNCHECKED_CAST") + return (model as SchemaModel.SchemaModelLitteral).apply { minLength = annotation.value } } diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt b/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt index 7556269df..05f743b24 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt @@ -55,22 +55,13 @@ sealed class SchemaModel: DataModel { var nullable: Boolean = false, var minimum: T? = null, var maximum: T? = null, - override var example: T? = null, - override var examples: List? = null, - override var description: String? = null - ) : SchemaModel() - - data class SchemaModelString( - var type: DataType? = null, - var format: DataFormat? = null, - var nullable: Boolean = false, var minLength: Int? = null, var maxLength: Int? = null, var pattern: String? = null, - override var example: String? = null, - override var examples: List? = null, + override var example: T? = null, + override var examples: List? = null, override var description: String? = null - ) : SchemaModel() + ) : SchemaModel() data class SchemaModelRef(override val `$ref`: String) : SchemaModel(), RefModel> { override var example: T? = null From 1a0f363f3bbcc6e74db112a02d171f40d615d1c8 Mon Sep 17 00:00:00 2001 From: bargergo Date: Mon, 25 May 2020 23:35:46 +0200 Subject: [PATCH 04/12] Pattern not used, yet --- .../com/papsign/ktor/openapigen/model/schema/SchemaModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt b/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt index 05f743b24..b6256eaa3 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt @@ -57,7 +57,6 @@ sealed class SchemaModel: DataModel { var maximum: T? = null, var minLength: Int? = null, var maxLength: Int? = null, - var pattern: String? = null, override var example: T? = null, override var examples: List? = null, override var description: String? = null From 2a577143654c00232459fbfc765028ec8bab86a8 Mon Sep 17 00:00:00 2001 From: bargergo Date: Thu, 28 May 2020 21:56:32 +0200 Subject: [PATCH 05/12] Add pattern validator with regex --- .../type/string/pattern/RegularExpression.kt | 9 +++ .../RegularExpressionConstraintProcessor.kt | 61 +++++++++++++++++++ .../pattern/RegularExpressionProcessor.kt | 17 ++++++ .../openapigen/model/schema/SchemaModel.kt | 1 + 4 files changed, 88 insertions(+) create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt new file mode 100644 index 000000000..44620feb3 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt @@ -0,0 +1,9 @@ +package com.papsign.ktor.openapigen.annotations.type.string.pattern + +import com.papsign.ktor.openapigen.schema.processor.SchemaProcessorAnnotation +import com.papsign.ktor.openapigen.validation.ValidatorAnnotation + +@Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) +@SchemaProcessorAnnotation(RegularExpressionProcessor::class) +@ValidatorAnnotation(RegularExpressionProcessor::class) +annotation class RegularExpression(val pattern: String, val message: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt new file mode 100644 index 000000000..ef6009f72 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt @@ -0,0 +1,61 @@ +package com.papsign.ktor.openapigen.annotations.type.string.pattern + +import com.papsign.ktor.openapigen.classLogger +import com.papsign.ktor.openapigen.getKType +import com.papsign.ktor.openapigen.model.schema.SchemaModel +import com.papsign.ktor.openapigen.schema.processor.SchemaProcessor +import com.papsign.ktor.openapigen.validation.Validator +import com.papsign.ktor.openapigen.validation.ValidatorBuilder +import java.lang.Exception +import kotlin.reflect.KType +import kotlin.reflect.full.withNullability + +abstract class RegularExpressionConstraintProcessor(): SchemaProcessor, ValidatorBuilder { + + private val log = classLogger() + + val types = listOf(getKType().withNullability(true), getKType().withNullability(false)) + + abstract fun process(model: SchemaModel.SchemaModelLitteral<*>, annotation: A): SchemaModel.SchemaModelLitteral<*> + + abstract fun getConstraint(annotation: A): RegularExpressionConstraint + + private class RegularExpressionConstraintValidator(private val constraint: RegularExpressionConstraint): Validator { + override fun validate(subject: T?): T? { + if (subject is String?) { + if (subject == null || !constraint.pattern.toRegex().containsMatchIn(subject)) { + throw RegularExpressionConstraintViolation(subject, constraint) + } + } else { + throw NotAStringViolation(subject) + } + return subject + } + } + + override fun build(type: KType, annotation: A): Validator { + return if (types.contains(type)) { + RegularExpressionConstraintValidator(getConstraint(annotation)) + } else { + error("${annotation::class} can only be used on types: $types") + } + } + + override fun process(model: SchemaModel<*>, type: KType, annotation: A): SchemaModel<*> { + return if (model is SchemaModel.SchemaModelLitteral<*> && types.contains(type)) { + process(model, annotation) + } else { + log.warn("${annotation::class} can only be used on types: $types") + model + } + } +} + +data class RegularExpressionConstraint(val pattern: String, val errorMessage: String? = null) + +open class ConstraintViolation(message: String, cause: Throwable? = null): Exception(message, cause) + +class RegularExpressionConstraintViolation(val actual: String?, val constraint: RegularExpressionConstraint): ConstraintViolation(constraint.errorMessage ?: "Constraint violation: the string " + +"\'$actual\' does not match the regular expression ${constraint.pattern}") + +class NotAStringViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a string") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt new file mode 100644 index 000000000..8cda4aa22 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt @@ -0,0 +1,17 @@ +package com.papsign.ktor.openapigen.annotations.type.string.pattern + +import com.papsign.ktor.openapigen.model.schema.SchemaModel + +object RegularExpressionProcessor : RegularExpressionConstraintProcessor() { + override fun process(model: SchemaModel.SchemaModelLitteral<*>, annotation: RegularExpression): SchemaModel.SchemaModelLitteral<*> { + @Suppress("UNCHECKED_CAST") + return (model as SchemaModel.SchemaModelLitteral).apply { + pattern = annotation.pattern + } + } + + override fun getConstraint(annotation: RegularExpression): RegularExpressionConstraint { + val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null + return RegularExpressionConstraint(annotation.pattern, errorMessage) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt b/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt index b6256eaa3..05f743b24 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/model/schema/SchemaModel.kt @@ -57,6 +57,7 @@ sealed class SchemaModel: DataModel { var maximum: T? = null, var minLength: Int? = null, var maxLength: Int? = null, + var pattern: String? = null, override var example: T? = null, override var examples: List? = null, override var description: String? = null From 9048497c5a8fbba50640b445eecf6f62d7c48a9c Mon Sep 17 00:00:00 2001 From: bargergo Date: Thu, 28 May 2020 22:24:00 +0200 Subject: [PATCH 06/12] Add example usage for string validators --- src/test/kotlin/TestServer.kt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/kotlin/TestServer.kt b/src/test/kotlin/TestServer.kt index 249f09c49..70b990857 100644 --- a/src/test/kotlin/TestServer.kt +++ b/src/test/kotlin/TestServer.kt @@ -33,6 +33,11 @@ import com.papsign.ktor.openapigen.annotations.type.number.integer.clamp.Clamp import com.papsign.ktor.openapigen.annotations.type.number.integer.max.Max import com.papsign.ktor.openapigen.annotations.type.number.integer.min.Min import com.papsign.ktor.openapigen.annotations.type.string.example.StringExample +import com.papsign.ktor.openapigen.annotations.type.string.length.ConstraintViolation +import com.papsign.ktor.openapigen.annotations.type.string.length.Length +import com.papsign.ktor.openapigen.annotations.type.string.length.MaxLength +import com.papsign.ktor.openapigen.annotations.type.string.length.MinLength +import com.papsign.ktor.openapigen.annotations.type.string.pattern.RegularExpression import io.ktor.application.application import io.ktor.application.call import io.ktor.application.install @@ -113,6 +118,12 @@ object TestServer { exception(HttpStatusCode.BadRequest) { Error("violation.constraint", it.localizedMessage) } + exception(HttpStatusCode.BadRequest) { + Error("violation.constraint", it.localizedMessage) + } + exception(HttpStatusCode.BadRequest) { + Error("violation.constraint", it.localizedMessage) + } exception(HttpStatusCode.BadRequest) { it.printStackTrace() Error(it.id, it.localizedMessage) @@ -187,6 +198,18 @@ object TestServer { respond(LongResponse(params.a)) } + route("validate-string").post( + info("This endpoint demonstrates the usage of String validators", "This endpoint demonstrates the usage of String validators"), + exampleRequest = StringValidatorsExample( + "A string that is at least 2 characters long", + "A short string", + "Between 2 and 20", + "5a21be2"), + exampleResponse = StringResponse("All of the fields were valid") + ) { params, body -> + respond(StringResponse("All of the fields were valid")) + } + route("again") { tag(TestServer.Tags.EXAMPLE) { @@ -231,6 +254,13 @@ object TestServer { @Response("A Response for header param example") data class NameGreetingResponse(@StringExample("Hi, John!") val str: String) + @Request("A Request with String fields validated for length or pattern") + data class StringValidatorsExample( + @MinLength(2,"Optional custom error message") val strWithMinLength: String, + @MaxLength( 20 ) val strWithMaxLength: String, + @Length(2, 20 ) val strWithLength: String, + @RegularExpression("^[0-9a-fA-F]*$", "The field strHexaDec should only contain hexadecimal digits") val strHexaDec: String + ) @Response("A String Response") @Request("A String Request") From d527139daab18b85ae2b707484248336eb417eaf Mon Sep 17 00:00:00 2001 From: bargergo Date: Thu, 28 May 2020 22:54:27 +0200 Subject: [PATCH 07/12] Use common base class for validation exceptions --- .../type/common/ConstraintViolation.kt | 6 +++ .../type/number/NumberConstraintProcessor.kt | 8 ++-- .../type/string/NotAStringViolation.kt | 5 +++ .../length/LengthConstraintProcessor.kt | 13 +++--- .../type/string/length/LengthProcessor.kt | 3 +- .../type/string/length/MaxLengthProcessor.kt | 3 +- .../type/string/length/MinLengthProcessor.kt | 3 +- .../RegularExpressionConstraintProcessor.kt | 13 +++--- .../pattern/RegularExpressionProcessor.kt | 3 +- src/test/kotlin/TestServer.kt | 45 ++++++++++++------- 10 files changed, 57 insertions(+), 45 deletions(-) create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/common/ConstraintViolation.kt create mode 100644 src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/NotAStringViolation.kt diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/common/ConstraintViolation.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/common/ConstraintViolation.kt new file mode 100644 index 000000000..ac6d6b854 --- /dev/null +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/common/ConstraintViolation.kt @@ -0,0 +1,6 @@ +package com.papsign.ktor.openapigen.annotations.type.common + +import java.lang.Exception + +abstract class ConstraintViolation(defaultMessage: String, message: String = "", cause: Throwable? = null) + : Exception(if (message.isEmpty()) defaultMessage else message, cause) \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt index 354173711..c8d60945f 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt @@ -1,11 +1,11 @@ package com.papsign.ktor.openapigen.annotations.type.number +import com.papsign.ktor.openapigen.annotations.type.common.ConstraintViolation import com.papsign.ktor.openapigen.classLogger import com.papsign.ktor.openapigen.model.schema.SchemaModel import com.papsign.ktor.openapigen.schema.processor.SchemaProcessor import com.papsign.ktor.openapigen.validation.Validator import com.papsign.ktor.openapigen.validation.ValidatorBuilder -import java.lang.Exception import java.math.BigDecimal import kotlin.reflect.KType import kotlin.reflect.full.withNullability @@ -61,9 +61,7 @@ abstract class NumberConstraintProcessor(allowedTypes: Iterable(): SchemaProcessor, V } } -data class LengthConstraint(val min: Int? = null, val max: Int? = null, val errorMessage: String? = null) +data class LengthConstraint(val min: Int? = null, val max: Int? = null, val errorMessage: String) -open class ConstraintViolation(message: String, cause: Throwable? = null): Exception(message, cause) - -class LengthConstraintViolation(val actual: Number?, val constraint: LengthConstraint): ConstraintViolation(constraint.errorMessage ?: "Constraint violation: the length of the string should be ${ +class LengthConstraintViolation(val actual: Number?, val constraint: LengthConstraint): ConstraintViolation("Constraint violation: the length of the string should be ${ { val min = "${constraint.min}" val max = "${constraint.max}" @@ -70,6 +69,4 @@ class LengthConstraintViolation(val actual: Number?, val constraint: LengthConst else -> "anything" } }() -}, but it is $actual") - -class NotAStringViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a string") \ No newline at end of file +}, but it is $actual", constraint.errorMessage) \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt index 829f0761c..4228796fe 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt @@ -12,7 +12,6 @@ object LengthProcessor : LengthConstraintProcessor() { } override fun getConstraint(annotation: Length): LengthConstraint { - val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null - return LengthConstraint(min = annotation.min, max = annotation.max, errorMessage = errorMessage) + return LengthConstraint(min = annotation.min, max = annotation.max, errorMessage = annotation.message) } } \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt index 7fafa369c..8c4b252b8 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt @@ -11,7 +11,6 @@ object MaxLengthProcessor : LengthConstraintProcessor() { } override fun getConstraint(annotation: MaxLength): LengthConstraint { - val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null - return LengthConstraint(max = annotation.value, errorMessage = errorMessage) + return LengthConstraint(max = annotation.value, errorMessage = annotation.message) } } \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt index 3a2f235f2..e1ca0fd45 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt @@ -11,7 +11,6 @@ object MinLengthProcessor : LengthConstraintProcessor() { } override fun getConstraint(annotation: MinLength): LengthConstraint { - val errorMessage = if (annotation.message.isNotEmpty()) annotation.message else null - return LengthConstraint(min = annotation.value, errorMessage = errorMessage) + return LengthConstraint(min = annotation.value, errorMessage = annotation.message) } } \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt index ef6009f72..353a99f31 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionConstraintProcessor.kt @@ -1,12 +1,13 @@ package com.papsign.ktor.openapigen.annotations.type.string.pattern +import com.papsign.ktor.openapigen.annotations.type.common.ConstraintViolation +import com.papsign.ktor.openapigen.annotations.type.string.NotAStringViolation import com.papsign.ktor.openapigen.classLogger import com.papsign.ktor.openapigen.getKType import com.papsign.ktor.openapigen.model.schema.SchemaModel import com.papsign.ktor.openapigen.schema.processor.SchemaProcessor import com.papsign.ktor.openapigen.validation.Validator import com.papsign.ktor.openapigen.validation.ValidatorBuilder -import java.lang.Exception import kotlin.reflect.KType import kotlin.reflect.full.withNullability @@ -51,11 +52,7 @@ abstract class RegularExpressionConstraintProcessor(): SchemaProc } } -data class RegularExpressionConstraint(val pattern: String, val errorMessage: String? = null) +data class RegularExpressionConstraint(val pattern: String, val errorMessage: String) -open class ConstraintViolation(message: String, cause: Throwable? = null): Exception(message, cause) - -class RegularExpressionConstraintViolation(val actual: String?, val constraint: RegularExpressionConstraint): ConstraintViolation(constraint.errorMessage ?: "Constraint violation: the string " + -"\'$actual\' does not match the regular expression ${constraint.pattern}") - -class NotAStringViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a string") \ No newline at end of file +class RegularExpressionConstraintViolation(val actual: String?, val constraint: RegularExpressionConstraint): ConstraintViolation("Constraint violation: the string " + +"'$actual' does not match the regular expression ${constraint.pattern}", constraint.errorMessage) \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt index 8cda4aa22..95f68e95c 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt @@ -11,7 +11,6 @@ object RegularExpressionProcessor : RegularExpressionConstraintProcessor(HttpStatusCode.BadRequest) { - Error("violation.constraint", it.localizedMessage) - } exception(HttpStatusCode.BadRequest) { Error("violation.constraint", it.localizedMessage) } - exception(HttpStatusCode.BadRequest) { - Error("violation.constraint", it.localizedMessage) - } exception(HttpStatusCode.BadRequest) { it.printStackTrace() Error(it.id, it.localizedMessage) @@ -210,6 +203,18 @@ object TestServer { respond(StringResponse("All of the fields were valid")) } + route("validate-number").post( + info("This endpoint demonstrates the usage of number validators", "This endpoint demonstrates the usage of number validators"), + exampleRequest = NumberValidatorsExample( + 1, + 56, + 15.02f, + 0.023f), + exampleResponse = StringResponse("All of the fields were valid") + ) { params, body -> + respond(StringResponse("All of the fields were valid")) + } + route("again") { tag(TestServer.Tags.EXAMPLE) { @@ -262,6 +267,14 @@ object TestServer { @RegularExpression("^[0-9a-fA-F]*$", "The field strHexaDec should only contain hexadecimal digits") val strHexaDec: String ) + @Request("A Request with validated number fields") + data class NumberValidatorsExample( + @Min(0) val intWithMin: Int, + @Clamp( 1, 90 ) val intBetween: Int, + @Max(100 ) val floatMax: Float, + @Clamp(0, 1) val floatBetween: Float + ) + @Response("A String Response") @Request("A String Request") data class StringUsable(val str: String) From fe5758a56cb4d186222b7ca0984765481245d836 Mon Sep 17 00:00:00 2001 From: bargergo Date: Thu, 28 May 2020 22:58:31 +0200 Subject: [PATCH 08/12] Rename message annotation parameter to errorMessage --- .../ktor/openapigen/annotations/type/string/length/Length.kt | 2 +- .../annotations/type/string/length/LengthProcessor.kt | 2 +- .../ktor/openapigen/annotations/type/string/length/MaxLength.kt | 2 +- .../annotations/type/string/length/MaxLengthProcessor.kt | 2 +- .../ktor/openapigen/annotations/type/string/length/MinLength.kt | 2 +- .../annotations/type/string/length/MinLengthProcessor.kt | 2 +- .../annotations/type/string/pattern/RegularExpression.kt | 2 +- .../type/string/pattern/RegularExpressionProcessor.kt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/Length.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/Length.kt index 1a105b125..5ecba5a68 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/Length.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/Length.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(LengthProcessor::class) @ValidatorAnnotation(LengthProcessor::class) -annotation class Length(val min: Int, val max: Int, val message: String = "") \ No newline at end of file +annotation class Length(val min: Int, val max: Int, val errorMessage: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt index 4228796fe..7ab93c20f 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/LengthProcessor.kt @@ -12,6 +12,6 @@ object LengthProcessor : LengthConstraintProcessor() { } override fun getConstraint(annotation: Length): LengthConstraint { - return LengthConstraint(min = annotation.min, max = annotation.max, errorMessage = annotation.message) + return LengthConstraint(min = annotation.min, max = annotation.max, errorMessage = annotation.errorMessage) } } \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt index 2f2699869..e689a4b8b 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLength.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(MaxLengthProcessor::class) @ValidatorAnnotation(MaxLengthProcessor::class) -annotation class MaxLength(val value: Int, val message: String = "") \ No newline at end of file +annotation class MaxLength(val value: Int, val errorMessage: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt index 8c4b252b8..ea072d26d 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MaxLengthProcessor.kt @@ -11,6 +11,6 @@ object MaxLengthProcessor : LengthConstraintProcessor() { } override fun getConstraint(annotation: MaxLength): LengthConstraint { - return LengthConstraint(max = annotation.value, errorMessage = annotation.message) + return LengthConstraint(max = annotation.value, errorMessage = annotation.errorMessage) } } \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt index d86e039ff..0c5f82f1e 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLength.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(MinLengthProcessor::class) @ValidatorAnnotation(MinLengthProcessor::class) -annotation class MinLength(val value: Int, val message: String = "") \ No newline at end of file +annotation class MinLength(val value: Int, val errorMessage: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt index e1ca0fd45..ac4c27c2e 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/length/MinLengthProcessor.kt @@ -11,6 +11,6 @@ object MinLengthProcessor : LengthConstraintProcessor() { } override fun getConstraint(annotation: MinLength): LengthConstraint { - return LengthConstraint(min = annotation.value, errorMessage = annotation.message) + return LengthConstraint(min = annotation.value, errorMessage = annotation.errorMessage) } } \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt index 44620feb3..8ec36e276 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(RegularExpressionProcessor::class) @ValidatorAnnotation(RegularExpressionProcessor::class) -annotation class RegularExpression(val pattern: String, val message: String = "") \ No newline at end of file +annotation class RegularExpression(val pattern: String, val errorMessage: String = "") \ No newline at end of file diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt index 95f68e95c..ac4b52afa 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpressionProcessor.kt @@ -11,6 +11,6 @@ object RegularExpressionProcessor : RegularExpressionConstraintProcessor Date: Thu, 28 May 2020 23:01:13 +0200 Subject: [PATCH 09/12] Correct example: use the floating number annotations for floating numbers --- src/test/kotlin/TestServer.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/TestServer.kt b/src/test/kotlin/TestServer.kt index d777b8ed9..7990fe128 100644 --- a/src/test/kotlin/TestServer.kt +++ b/src/test/kotlin/TestServer.kt @@ -19,6 +19,8 @@ import com.papsign.ktor.openapigen.annotations.properties.description.Descriptio import com.papsign.ktor.openapigen.annotations.type.`object`.example.ExampleProvider import com.papsign.ktor.openapigen.annotations.type.`object`.example.WithExample import com.papsign.ktor.openapigen.annotations.type.common.ConstraintViolation +import com.papsign.ktor.openapigen.annotations.type.number.floating.clamp.FClamp +import com.papsign.ktor.openapigen.annotations.type.number.floating.max.FMax import com.papsign.ktor.openapigen.annotations.type.number.integer.clamp.Clamp import com.papsign.ktor.openapigen.annotations.type.number.integer.max.Max import com.papsign.ktor.openapigen.annotations.type.number.integer.min.Min @@ -271,8 +273,8 @@ object TestServer { data class NumberValidatorsExample( @Min(0) val intWithMin: Int, @Clamp( 1, 90 ) val intBetween: Int, - @Max(100 ) val floatMax: Float, - @Clamp(0, 1) val floatBetween: Float + @FMax(100.0 ) val floatMax: Float, + @FClamp(0.0, 1.0) val floatBetween: Float ) @Response("A String Response") From a3c2171993aefac9b7326445594718eff3d66e5f Mon Sep 17 00:00:00 2001 From: bargergo Date: Thu, 28 May 2020 23:20:44 +0200 Subject: [PATCH 10/12] Add optional errorMessage parameter for number validation annotations --- .../type/number/NumberConstraintProcessor.kt | 4 ++-- .../annotations/type/number/floating/clamp/FClamp.kt | 2 +- .../type/number/floating/clamp/FClampProcessor.kt | 2 +- .../annotations/type/number/floating/max/FMax.kt | 2 +- .../type/number/floating/max/FMaxProcessor.kt | 2 +- .../annotations/type/number/floating/min/FMin.kt | 2 +- .../type/number/floating/min/FMinProcessor.kt | 2 +- .../annotations/type/number/integer/clamp/Clamp.kt | 2 +- .../type/number/integer/clamp/ClampProcessor.kt | 2 +- .../annotations/type/number/integer/max/Max.kt | 2 +- .../type/number/integer/max/MaxProcessor.kt | 2 +- .../annotations/type/number/integer/min/Min.kt | 2 +- .../type/number/integer/min/MinProcessor.kt | 2 +- src/test/kotlin/TestServer.kt | 10 +++++----- 14 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt index c8d60945f..955102bc8 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/NumberConstraintProcessor.kt @@ -59,7 +59,7 @@ abstract class NumberConstraintProcessor(allowedTypes: Iterable "anything" } }() -}") +}", constraint.errorMessage) class NotANumberViolationViolation(val value: Any?): ConstraintViolation("Constraint violation: $value is not a number") diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/clamp/FClamp.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/clamp/FClamp.kt index 7a1d240e1..67bba6664 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/clamp/FClamp.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/clamp/FClamp.kt @@ -6,6 +6,6 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(FClampProcessor::class) @ValidatorAnnotation(FClampProcessor::class) -annotation class FClamp(val min: Double, val max: Double) +annotation class FClamp(val min: Double, val max: Double, val errorMessage: String = "") diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/clamp/FClampProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/clamp/FClampProcessor.kt index 0c4b537a1..437eb850b 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/clamp/FClampProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/clamp/FClampProcessor.kt @@ -18,6 +18,6 @@ object FClampProcessor : FloatingNumberConstraintProcessor() { } override fun getConstraint(annotation: FClamp): NumberConstraint { - return NumberConstraint(BigDecimal(annotation.min), BigDecimal(annotation.max)) + return NumberConstraint(BigDecimal(annotation.min), BigDecimal(annotation.max), errorMessage = annotation.errorMessage) } } diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/max/FMax.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/max/FMax.kt index 56b12cbeb..5c32a62ee 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/max/FMax.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/max/FMax.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(FMaxProcessor::class) @ValidatorAnnotation(FMaxProcessor::class) -annotation class FMax(val value: Double) +annotation class FMax(val value: Double, val errorMessage: String = "") diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/max/FMaxProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/max/FMaxProcessor.kt index 44fdba0fe..6a2bbcb9f 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/max/FMaxProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/max/FMaxProcessor.kt @@ -16,6 +16,6 @@ object FMaxProcessor: FloatingNumberConstraintProcessor() { } } override fun getConstraint(annotation: FMax): NumberConstraint { - return NumberConstraint(max= BigDecimal(annotation.value)) + return NumberConstraint(max= BigDecimal(annotation.value), errorMessage = annotation.errorMessage) } } diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/min/FMin.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/min/FMin.kt index bbef42c80..dc5f39deb 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/min/FMin.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/min/FMin.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(FMinProcessor::class) @ValidatorAnnotation(FMinProcessor::class) -annotation class FMin(val value: Double) +annotation class FMin(val value: Double, val errorMessage: String = "") diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/min/FMinProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/min/FMinProcessor.kt index ab7d2cb87..f1417c86e 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/min/FMinProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/floating/min/FMinProcessor.kt @@ -17,6 +17,6 @@ object FMinProcessor: FloatingNumberConstraintProcessor() { } override fun getConstraint(annotation: FMin): NumberConstraint { - return NumberConstraint(min = BigDecimal(annotation.value)) + return NumberConstraint(min = BigDecimal(annotation.value), errorMessage = annotation.errorMessage) } } diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/clamp/Clamp.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/clamp/Clamp.kt index 88d358b70..dc6d0bc20 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/clamp/Clamp.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/clamp/Clamp.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(ClampProcessor::class) @ValidatorAnnotation(ClampProcessor::class) -annotation class Clamp(val min: Long, val max: Long) +annotation class Clamp(val min: Long, val max: Long, val errorMessage: String = "") diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/clamp/ClampProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/clamp/ClampProcessor.kt index d88d14f04..3da02d98a 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/clamp/ClampProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/clamp/ClampProcessor.kt @@ -18,6 +18,6 @@ object ClampProcessor: IntegerNumberConstraintProcessor() { } override fun getConstraint(annotation: Clamp): NumberConstraint { - return NumberConstraint(BigDecimal(annotation.min), BigDecimal(annotation.max)) + return NumberConstraint(BigDecimal(annotation.min), BigDecimal(annotation.max), errorMessage = annotation.errorMessage) } } diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/max/Max.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/max/Max.kt index 822f415a3..75600e63c 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/max/Max.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/max/Max.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(MaxProcessor::class) @ValidatorAnnotation(MaxProcessor::class) -annotation class Max(val value: Long) +annotation class Max(val value: Long, val errorMessage: String = "") diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/max/MaxProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/max/MaxProcessor.kt index 360b6a5c0..87245a256 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/max/MaxProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/max/MaxProcessor.kt @@ -16,6 +16,6 @@ object MaxProcessor: IntegerNumberConstraintProcessor() { } } override fun getConstraint(annotation: Max): NumberConstraint { - return NumberConstraint(max= BigDecimal(annotation.value)) + return NumberConstraint(max= BigDecimal(annotation.value), errorMessage = annotation.errorMessage) } } diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/min/Min.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/min/Min.kt index 9c75d8de4..13a2a37ed 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/min/Min.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/min/Min.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(MinProcessor::class) @ValidatorAnnotation(MinProcessor::class) -annotation class Min(val value: Long) +annotation class Min(val value: Long, val errorMessage: String = "") diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/min/MinProcessor.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/min/MinProcessor.kt index 1a595396e..4c253b52e 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/min/MinProcessor.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/number/integer/min/MinProcessor.kt @@ -17,6 +17,6 @@ object MinProcessor: IntegerNumberConstraintProcessor() { } override fun getConstraint(annotation: Min): NumberConstraint { - return NumberConstraint(min = BigDecimal(annotation.value)) + return NumberConstraint(min = BigDecimal(annotation.value), errorMessage = annotation.errorMessage) } } diff --git a/src/test/kotlin/TestServer.kt b/src/test/kotlin/TestServer.kt index 7990fe128..7b16fa227 100644 --- a/src/test/kotlin/TestServer.kt +++ b/src/test/kotlin/TestServer.kt @@ -271,10 +271,10 @@ object TestServer { @Request("A Request with validated number fields") data class NumberValidatorsExample( - @Min(0) val intWithMin: Int, + @Min(0, "The value of field intWithMin should be a positive integer") val intWithMin: Int, @Clamp( 1, 90 ) val intBetween: Int, - @FMax(100.0 ) val floatMax: Float, - @FClamp(0.0, 1.0) val floatBetween: Float + @FMax(100.0) val floatMax: Float, + @FClamp(0.0, 1.0, "The value of field floatBetween should be a between 0 and 1") val floatBetween: Float ) @Response("A String Response") @@ -297,10 +297,10 @@ object TestServer { class A(val str: String) : Base() - class B(val i: @Min(0) @Max(2) Int) : Base() + class B(@Min(0) @Max(2) val i: Int) : Base() @WithExample - class C(val l: @Clamp(0, 10) Long) : Base() { + class C( @Clamp(0, 10) val l: Long) : Base() { companion object: ExampleProvider { override val example: C? = C(5) } From 396e936ecaec16928c151e0c56b57073292f7d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Nieto?= Date: Wed, 3 Jun 2020 12:39:29 +0200 Subject: [PATCH 11/12] Added Syntax Highlighting for Regex --- .../annotations/type/string/pattern/RegularExpression.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt index 8ec36e276..862a1890a 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(RegularExpressionProcessor::class) @ValidatorAnnotation(RegularExpressionProcessor::class) -annotation class RegularExpression(val pattern: String, val errorMessage: String = "") \ No newline at end of file +annotation class RegularExpression(@Language("RegExp") val pattern: String, val errorMessage: String = "") From 5874a1fd2e17c35cae089f86238febd2a693c74e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Nieto?= Date: Wed, 3 Jun 2020 12:44:57 +0200 Subject: [PATCH 12/12] Update RegularExpression.kt --- .../annotations/type/string/pattern/RegularExpression.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt index 862a1890a..06e054177 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/annotations/type/string/pattern/RegularExpression.kt @@ -6,4 +6,4 @@ import com.papsign.ktor.openapigen.validation.ValidatorAnnotation @Target(AnnotationTarget.TYPE, AnnotationTarget.PROPERTY) @SchemaProcessorAnnotation(RegularExpressionProcessor::class) @ValidatorAnnotation(RegularExpressionProcessor::class) -annotation class RegularExpression(@Language("RegExp") val pattern: String, val errorMessage: String = "") +annotation class RegularExpression(@org.intellij.lang.annotations.Language("RegExp") val pattern: String, val errorMessage: String = "")