diff --git a/src/main/kotlin/com/papsign/ktor/openapigen/validation/ValidationHandler.kt b/src/main/kotlin/com/papsign/ktor/openapigen/validation/ValidationHandler.kt index cdb19f36a..350312010 100644 --- a/src/main/kotlin/com/papsign/ktor/openapigen/validation/ValidationHandler.kt +++ b/src/main/kotlin/com/papsign/ktor/openapigen/validation/ValidationHandler.kt @@ -4,8 +4,7 @@ import com.papsign.ktor.openapigen.* import com.papsign.ktor.openapigen.classLogger import kotlin.reflect.* import kotlin.reflect.full.* -import kotlin.reflect.jvm.javaField -import kotlin.reflect.jvm.jvmErasure +import kotlin.reflect.jvm.* /** @@ -139,7 +138,7 @@ class ValidationHandler private constructor( } else { val appropriateConstructor = type.jvmErasure.constructors.find { it.parameters.size == 1 && it.parameters[0].type.isSubtypeOf(iterableType) - } ?: error("Unsupported Iterable type $type, must have a constructor that takes an iterable"); + } ?: error("Unsupported Iterable type $type, must have a constructor that takes an iterable") when { handler.isUseful() && shouldTransform -> { transformFun = { t: Any? -> @@ -208,8 +207,7 @@ class ValidationHandler private constructor( } else -> { val handled = type.memberProperties.mapNotNull { prop -> - val validator = - build(prop) + val validator = build(prop) if (validator.isUseful()) { prop.source.javaField.also { if (it == null) { @@ -236,15 +234,25 @@ class ValidationHandler private constructor( handled.isNotEmpty() -> { transformFun = { t: Any? -> if (t != null) { + val copy = t.javaClass.kotlin.memberFunctions.find { it.name == "copy" } + val copyParams = copy?.instanceParameter?.let { mutableMapOf(it to t) } handled.forEach { (handler, field) -> - // TODO convert this to canAccess and only change status if false - val accessible = field.isAccessible - field.isAccessible = true - field.set(t, handler.handle(field.get(t))) - field.isAccessible = accessible + val getter = field.kotlinProperty?.javaGetter + if (copy != null && copyParams != null && getter != null) { + val param = copy.parameters.first { it.name == field.name } + copyParams[param] = handler.handle(getter(t)) + } else { + // TODO convert this to canAccess and only change status if false + val accessible = field.isAccessible + field.isAccessible = true + field.set(t, handler.handle(field.get(t))) + field.isAccessible = accessible + } } - } - t + if (copy != null && copyParams != null) { + copy.callBy(copyParams) + } else t + } else t } } shouldTransform -> { diff --git a/src/test/kotlin/TestServer.kt b/src/test/kotlin/TestServer.kt index 703580676..0355f40e8 100644 --- a/src/test/kotlin/TestServer.kt +++ b/src/test/kotlin/TestServer.kt @@ -30,6 +30,7 @@ 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 com.papsign.ktor.openapigen.annotations.type.string.trim.Trim import com.papsign.ktor.openapigen.interop.withAPI import com.papsign.ktor.openapigen.model.Described import com.papsign.ktor.openapigen.model.server.ServerModel @@ -319,6 +320,12 @@ object TestServer { } } } + + route("employees") { + get { params -> + respond(params) + } + } } }.start(true) } @@ -412,4 +419,52 @@ object TestServer { data class InstantResponse(val instant: Instant) data class LocalTimeResponse(val time: LocalTime?) data class OffsetTimeResponse(val time: OffsetTime?) + + data class FilterQuery( + @QueryParam("Vendor Code") @Trim + val vendorCode: String? = null, + @QueryParam("Organization") @Trim + val organization: String? = null, + @QueryParam("startDate") + val startDate: LocalDate? = null, + @QueryParam("endDate") + val endDate: LocalDate? = null, + @QueryParam("tenant") @Trim + val tenant: String? = null, + @QueryParam("manager") @Trim + val manager: String? = null, + @QueryParam("performer") @Trim + val performer: String? = null, + @QueryParam("condition") @Trim + val condition: String? = null, + @QueryParam("onlyNew") + val onlyNew: Boolean? = null, + @QueryParam("name") @Trim + val name: String? = null, + @QueryParam("minQuantity") @Min(0) + val minQuantity: Int? = null, + @QueryParam("maxQuantity") @Min(0) + val maxQuantity: Int? = null, + @QueryParam("minCost") @Min(0) + val minCost: Int? = null, + @QueryParam("maxCost") @Min(0) + val maxCost: Int? = null, + @QueryParam("inStock") + val inStock: Boolean? = null, + @QueryParam("active") + val active: Boolean? = null, + + @QueryParam("employeeName") @Trim + val employeeName: String? = null, + + @QueryParam("sortToken") + @StringExample("fullName") @Trim + val sortToken: String? = null, + @QueryParam("pageSize") + @Min(1) + val pageSize: Int? = null, + @QueryParam("page") + @Min(0) + val page: Long? = 0 + ) }