dave08
02/28/2023, 3:07 PMsimon.vergauwen
02/28/2023, 3:25 PMcall.receive<A>() either catch and turn it into JsonError or letting the KotlinX exception be handled by Ktor itself.
2. Validate the individual params of the resulting intermediate DTO class. (in service layer).
3. After validation turn into proper domain object.
Should turn this into Either validation pattern from 1.2.x, but if you've seen both my webinars with Anton then it should be quite easy to update the example. A PR would also be very welcomed, and I would be happy to guide you there in more details as well 😉
(Update Validated to Either, and use Either.zipOrAccumulate instead of Validated.zip)dave08
02/28/2023, 4:30 PMsimon.vergauwen
02/28/2023, 4:33 PMdave08
02/28/2023, 4:44 PM@Serializable
data class FooRequest(val baz: Baz,val bar: Bar)
@Serializable
@JvmInline
value class Baz(val value: Int) {
init { require(value in 21..39) }
}
@Serializable
@JvmInline
value class Bar(val value: Int) {
init { require(value in 5..10) }
}
This has validation logic INSIDE the value classes in a way that you can't deserialize something wrong... so in tests I don't have to remember to validate before (or take the chance of accidentally putting the system in a wrong state in that test).
I'm wondering (if they work...) if I would have context receivers on those classes with Raise<RequestError> I could technically use ensure instead of require... but I guess that's not right now...simon.vergauwen
02/28/2023, 4:46 PMFooRequest.dave08
02/28/2023, 4:47 PMcatch({ Json.decodeFromString<FooRequest>(json) }) {...} wouldn't be enough?simon.vergauwen
02/28/2023, 4:48 PMBaz if it's value is not inside 21..39 and just result in that.simon.vergauwen
02/28/2023, 4:49 PMdave08
02/28/2023, 4:51 PMRaise.ensure is being used instead of require in those init blocks, I wouldn't even need catch... either { } would be enough, unless you mean that inside KotlinX Serialization they loose the coroutine context and ensure wouldn't work properly...?simon.vergauwen
02/28/2023, 4:57 PMRaise<RequestError> on your value class.
You would also still need to deal with MissingFieldException from KotlinX though. So it might be easier to write an utility function that deals with both the exceptions from require and MissingFieldException.dave08
02/28/2023, 5:01 PMrequire with a ``catch`...
Yeah the point WAS to accumulate errors... but i guess I'm not understanding how validation works well enough I thought they automatically accumulate themselves in a Left<NotEmptyList<RequestError>> each time one calls ensure...?simon.vergauwen
02/28/2023, 5:06 PMensureNotNull statements can depend on each other. Here the second ensure depends on the result of the previous one, so it's not possible to accumulate.
fun Raise<String>.example() {
val x: Int? = ...
ensureNotNull<Int>(x) { "fail-1" }
val str = x.toString()
ensure(str.size > 3) { "not long enough" }
}
To accumulate you need to explicitly use xOrAccumulate APIs, zipOrAccumulate or Iterable.mapOrAccumulate, etc.