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.dave08
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.