Skip to content

Instantly share code, notes, and snippets.

@EXPEylazzari
Created August 8, 2022 14:44
Show Gist options
  • Save EXPEylazzari/dee99c40b5d68204756d3fd1c7a9f336 to your computer and use it in GitHub Desktop.
Save EXPEylazzari/dee99c40b5d68204756d3fd1c7a9f336 to your computer and use it in GitHub Desktop.
gRPC Effect Builder
import arrow.core.continuations.Effect
import arrow.core.continuations.EffectScope
import arrow.core.continuations.effect
import arrow.core.nonFatalOrThrow
interface GrpcEffectContext<E> {
fun handleThrowable(t: Throwable): E
}
object DefaultGrpcEffectContext : GrpcEffectContext<String> {
override fun handleThrowable(t: Throwable): String = t.message ?: "Some error"
}
class GrpcEffectScope<R>(
private val context: GrpcEffectContext<R>,
private val continuation: EffectScope<R>
) : EffectScope<R> {
fun <A> call(f: suspend EffectScope<R>.() -> A): Effect<R, A> {
return effect {
try {
f()
} catch (t: Throwable) {
shift(context.handleThrowable(t.nonFatalOrThrow()))
}
}
}
override suspend fun <B> shift(r: R): B = continuation.shift(r)
}
suspend fun <R, A> grpc(
context: GrpcEffectContext<R>,
f: suspend GrpcEffectScope<R>.() -> A
): Effect<R, A> = effect {
f(GrpcEffectScope(context, this))
}
import arrow.core.continuations.Effect
import arrow.core.continuations.effect
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.fail
class GrpcEffectTest {
class GrpcService1 {
suspend fun fun1(): String = "value_1"
suspend fun fun1ThatFails(): String = throw RuntimeException("kaboom_1")
}
class GrpcService2 {
suspend fun fun2(): String = "value_2"
suspend fun fun2ThatFails(): String = throw RuntimeException("kaboom_2")
}
class OtherService3 {
suspend fun fun3(): String = "value_3"
}
@Test
fun `should fold successfully when both calls succeed`() {
val service1 = GrpcService1()
val service2 = GrpcService2()
runBlocking {
val outerEffect: Effect<String, String> = grpc(DefaultGrpcEffectContext) {
val innerEffect1: Effect<String, String> = call {
service1.fun1()
}
val innerEffect2: Effect<String, String> = call {
service2.fun2()
}
val value1 = innerEffect1.bind()
val value2 = innerEffect2.bind()
"$value1 $value2"
}
outerEffect.fold(
{ fail("Should not have failed but failed with $it") },
{ assertEquals("value_1 value_2", it) }
)
}
}
@Test
fun `should should shift correctly when first call fails`() {
val service1 = GrpcService1()
val service2 = GrpcService2()
runBlocking {
val outerEffect: Effect<String, String> = grpc(DefaultGrpcEffectContext) {
val innerEffect1: Effect<String, String> = call {
service1.fun1ThatFails()
}
val innerEffect2: Effect<String, String> = call {
service2.fun2()
}
val value1 = innerEffect1.bind()
val value2 = innerEffect2.bind()
"$value1 $value2"
}
outerEffect.fold(
{ assertEquals("kaboom_1", it) },
{ fail("Should not have succeeded but succeeded with $it") }
)
}
}
@Test
fun `should should shift correctly when second call fails`() {
val service1 = GrpcService1()
val service2 = GrpcService2()
runBlocking {
val outerEffect: Effect<String, String> = grpc(DefaultGrpcEffectContext) {
val innerEffect1: Effect<String, String> = call {
service1.fun1()
}
val innerEffect2: Effect<String, String> = call {
service2.fun2ThatFails()
}
val value1 = innerEffect1.bind()
val value2 = innerEffect2.bind()
"$value1 $value2"
}
outerEffect.fold(
{ assertEquals("kaboom_2", it) },
{ fail("Should not have succeeded but succeeded with $it") }
)
}
}
@Test
fun `should be able to nest effect block`() {
val service1 = GrpcService1()
val service2 = GrpcService2()
val service3 = OtherService3()
runBlocking {
val outerEffect: Effect<String, String> = grpc(DefaultGrpcEffectContext) {
val innerEffect1: Effect<String, String> = call {
service1.fun1()
}
val innerEffect2: Effect<String, String> = call {
service2.fun2()
}
val innerEffect3: Effect<String, String> = effect {
service3.fun3()
}
val value1 = innerEffect1.bind()
val value2 = innerEffect2.bind()
val value3 = innerEffect3.bind()
"$value1 $value2 $value3"
}
outerEffect.fold(
{ fail("Should not have failed but failed with $it") },
{ assertEquals("value_1 value_2 value_3", it) }
)
}
}
@Test
fun `should should be able to nest gRPC calls inside effect block`() {
val service1 = GrpcService1()
runBlocking {
val outerEffect: Effect<String, String> = effect {
val innerEffect: Effect<String, String> = grpc(DefaultGrpcEffectContext) {
call { service1.fun1() }.bind()
}
innerEffect.bind()
}
outerEffect.fold(
{ fail("Should not have failed but failed with $it") },
{ assertEquals("value_1", it) }
)
}
}
@Test
fun `should should be able to provide custom throwable handling function`() {
val service1 = GrpcService1()
val customGrpcEffectContext = object : GrpcEffectContext<Int> {
override fun handleThrowable(t: Throwable): Int = 0
}
runBlocking {
val outerEffect: Effect<Int, String> = grpc(customGrpcEffectContext) {
val innerEffect: Effect<Int, String> = call {
service1.fun1ThatFails()
}
innerEffect.bind()
}
outerEffect.fold(
{ assertEquals(0, it) },
{ fail("Should not have succeeded but succeeded with $it") }
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment