Larry Garfield
05/15/2024, 4:55 PMsealed class RefreshResult(val message: String) {
class Queued(val id: UUID) : RefreshResult("queued")
class Exists(val id: UUID) : RefreshResult("exists")
class Rejected(val reason: String) : RefreshResult("rejected")
}
Is there a more Arrow-y tool for such a tri-state case? or utilities that would make sense to borrow from Arrow/Either to make it cleaner? Or would you typically shoehorn that into an Either somehow? (I’m not sure how effectively I can do that given the specific properties involved.)simon.vergauwen
05/15/2024, 5:02 PMLarry Garfield
05/15/2024, 5:05 PMLarry Garfield
05/15/2024, 5:05 PMLarry Garfield
05/15/2024, 5:08 PMclass Controller {
fun refresh(payload: RefreshRequest): ResponseEntity<String> {
val validatedRequest = payload.toValidated()
return validatedRequest.fold<ResponseEntity<String>>({
return ResponseEntity.badRequest().body(it.errorMessage())
}, {
return when (val result = someService.refreshEntity(it)) {
is RefreshResult.Queued ->
ResponseEntity
.accepted()
.body(objectMapper.writeValueAsString(EntityRefreshResponse(result.id, result.message)))
is RefreshResult.Exists ->
ResponseEntity
.accepted()
.body(objectMapper.writeValueAsString(EntityRefreshResponse(result.id, result.message)))
is RefreshResult.Rejected ->
ResponseEntity
.internalServerError()
.body(objectMapper.writeValueAsString(EntityRefreshResponse(null, result.message)))
}
})
}
}
class SomeService(request: ValidatedRequest): RefreshResult {
if (we already got the request) {
return RefreshResult.Exists(existing id)
}
// Save new record
if (save failed for some reason) {
return RefreshResult.Rejected(some message)
}
return RefreshResult.Queued(newly created id)
}
Larry Garfield
05/15/2024, 5:11 PMLarry Garfield
05/15/2024, 5:11 PMLarry Garfield
05/15/2024, 5:27 PMsimon.vergauwen
05/15/2024, 5:36 PMfold
there should no additional returns, those are redundant in lambdas and thus they return the result to the refresh function. Bypassing fold entirely.
I would split the code a bit more, and perhaps use getOrElse
instead of fold
like this, and the DSL to clean it up a bit more.
fun refresh(payload: RefreshRequest): ResponseEntity<String> =
either {
val value = payload.toValidated().bind()
val result = someService.refreshEntity(value)
result.toResponseEntity()
}.getOrElse { ResponseEntity.badRequest().body(it.errorMessage()) }
fun RefreshResult.toReponseEntity() =
when (this) {
is RefreshResult.Queued ->
ResponseEntity
.accepted()
.body(objectMapper.writeValueAsString(EntityRefreshResponse(id, message)))
is RefreshResult.Exists ->
ResponseEntity
.accepted()
.body(objectMapper.writeValueAsString(EntityRefreshResponse(id, message)))
is RefreshResult.Rejected ->
ResponseEntity
.internalServerError()
.body(objectMapper.writeValueAsString(EntityRefreshResponse(null, message)))
}
Youssef Shoaib [MOD]
05/15/2024, 5:40 PMEither<String, Pair<UUID, Bool>>
but I think having your own custom type is fine.
I think you likely want to define a DSL like the following to let you use your type much more easily:
inline fun refreshResult(block: Raise<String> -> Pair<UUID, Boolean>): RefreshResult = fold(block, RefreshResult::Rejected) { (queued, id) -> if(queued) RefreshResult.Queued(id) else RefreshResult.Exists(id) }
Then you'll be able to raise("my reason")
when you want to reject a refreshLarry Garfield
05/15/2024, 5:43 PMLarry Garfield
05/15/2024, 5:44 PMLarry Garfield
05/15/2024, 5:49 PMsimon.vergauwen
05/15/2024, 5:53 PMsimon.vergauwen
05/15/2024, 5:53 PMResponseEntity<String>
in cases like this I return ResponseEntity<*>
.. Not great, but I can just return typed entities and Spring will do the serialisation.Larry Garfield
05/15/2024, 7:24 PMLarry Garfield
05/15/2024, 7:38 PMsimon.vergauwen
05/15/2024, 7:45 PMLarry Garfield
05/15/2024, 7:46 PMYoussef Shoaib [MOD]
05/15/2024, 7:47 PMsimon.vergauwen
05/15/2024, 7:48 PMResponseEntity<*>
, so imho it's not problematic. What is the benefit of splitting, besides aesthetically?Larry Garfield
05/15/2024, 7:52 PMsimon.vergauwen
05/15/2024, 7:59 PMsimon.vergauwen
05/15/2024, 8:00 PMLarry Garfield
05/15/2024, 8:00 PM