David Kubecka
10/25/2024, 12:17 PMwithApiError
which I call in my controller methods. Besides setting up Raise<ApiError>
context, I'm also setting other common contexts. E.g. I inject an Account
via withAccount
function. This function can naturally raise an error (e.g. if the account does not exist), therefore it itself needs the Raise<ApiError>
context. So my typical controller method looks like
withApiError {
withAccount {
// do some stuff which needs an account and can raise an error
}
}
So far so good.
The catch (I would say catch 22 🙂) is that in the withApiError
function I would like to log some stuff which itself needs some account info. Obviouly, this can be used only if the account was successfully fetched. My question is how to best model this. Obviously I can do something like
withApiError { // this is just for the account
withAccount {
withApiErrorAndAccount { // inject full error context with access to account
// do some stuff which needs an account and can raise an error
}
}
}
Is there a more elegant way how to do that? Perhaps some clever tricks with nullability of receivers?Alejandro Serrano.Mena
10/25/2024, 12:21 PMval account = withApiError {
...
}
wihApiErrorAndAccount(account) {
...
}
but at the end of the day you have two different ApiError
contexts that need to be injected separatelyDavid Kubecka
10/25/2024, 12:23 PMAlejandro Serrano.Mena
10/25/2024, 12:24 PMYoussef Shoaib [MOD]
10/25/2024, 12:42 PMwithAccount
should inject a new Raise
context that does the extra logging. Hence`withApiError` augments the pre-existing contextDavid Kubecka
11/08/2024, 9:55 AMclass TraceLogger(var accountName: String? = null) {
fun log() = traceClient.log(accountName)
}
fun <R, E : ApiError> withApiError(traceLogger: TraceLogger, block: Raise<E>.() -> R): R =
recover(block) { error ->
traceLogger.log()
...
}
fun <R, E : ApiError> withApiTraceError(
block: context(Account, Raise<E>) () -> R,
): R {
val traceLogger = TraceLogger() {
return withApiError(traceLogger) {
withAccount {
traceLogger.accountName = name
block(this@withAccount, this@withApiError)
}
}
}
I'm quite happy with this solution because it exactly mimics the actual data flow.