diff --git a/openfeature/sdk/src/main/scala/io/cardell/openfeature/FeatureClient.scala b/openfeature/sdk/src/main/scala/io/cardell/openfeature/FeatureClient.scala index 73fba68..7b47e30 100644 --- a/openfeature/sdk/src/main/scala/io/cardell/openfeature/FeatureClient.scala +++ b/openfeature/sdk/src/main/scala/io/cardell/openfeature/FeatureClient.scala @@ -25,8 +25,6 @@ trait FeatureClient[F[_]] { def evaluationContext: EvaluationContext def withEvaluationContext(context: EvaluationContext): FeatureClient[F] - def beforeHooks: List[BeforeHook[F]] - def withHook(hook: Hook[F]): FeatureClient[F] // def withHooks(hooks: List[Hook]): FeatureClient[F] diff --git a/openfeature/sdk/src/main/scala/io/cardell/openfeature/FeatureClientImpl.scala b/openfeature/sdk/src/main/scala/io/cardell/openfeature/FeatureClientImpl.scala index b9a3cd6..8f68271 100644 --- a/openfeature/sdk/src/main/scala/io/cardell/openfeature/FeatureClientImpl.scala +++ b/openfeature/sdk/src/main/scala/io/cardell/openfeature/FeatureClientImpl.scala @@ -27,7 +27,8 @@ protected[openfeature] final class FeatureClientImpl[F[_]]( provider: EvaluationProvider[F], val clientEvaluationContext: EvaluationContext, val beforeHooks: List[BeforeHook[F]], - val errorHooks: List[ErrorHook[F]] + val errorHooks: List[ErrorHook[F]], + val afterHooks: List[AfterHook[F]] )(implicit M: MonadThrow[F]) extends FeatureClient[F] { @@ -42,7 +43,8 @@ protected[openfeature] final class FeatureClientImpl[F[_]]( provider, clientEvaluationContext ++ context, beforeHooks, - errorHooks + errorHooks, + afterHooks ) override def withHook(hook: Hook[F]): FeatureClient[F] = @@ -52,16 +54,25 @@ protected[openfeature] final class FeatureClientImpl[F[_]]( provider, clientEvaluationContext, beforeHooks.appended(h), - errorHooks + errorHooks, + afterHooks ) case h: ErrorHook[F] => new FeatureClientImpl[F]( provider, clientEvaluationContext, beforeHooks, - errorHooks.appended(h) + errorHooks.appended(h), + afterHooks + ) + case h: AfterHook[F] => + new FeatureClientImpl[F]( + provider, + clientEvaluationContext, + beforeHooks, + errorHooks, + afterHooks.appended(h) ) - case _ => ??? } override def getBooleanValue(flagKey: String, default: Boolean): F[Boolean] = @@ -361,11 +372,16 @@ protected[openfeature] final class FeatureClientImpl[F[_]]( for { newContext <- Hooks.runBefore[F](beforeHooks)(hookContext, hookHints) evaluation <- evaluate(newContext) + _ <- + Hooks.runAfter[F](afterHooks)( + hookContext.copy(evaluationContext = newContext), + hookHints + ) } yield evaluation run - .onError { case e => - Hooks.runErrors(errorHooks)(hookContext, hookHints, e) + .onError { case error => + Hooks.runErrors(errorHooks)(hookContext, hookHints, error) } .handleError(error => EvaluationDetails(flagKey, ResolutionDetails.error(default, error)) @@ -384,7 +400,8 @@ object FeatureClientImpl { provider = provider, clientEvaluationContext = EvaluationContext.empty, beforeHooks = List.empty[BeforeHook[F]], - errorHooks = List.empty[ErrorHook[F]] + errorHooks = List.empty[ErrorHook[F]], + afterHooks = List.empty[AfterHook[F]] ) } diff --git a/openfeature/sdk/src/main/scala/io/cardell/openfeature/Hooks.scala b/openfeature/sdk/src/main/scala/io/cardell/openfeature/Hooks.scala index 84a28bc..af6316f 100644 --- a/openfeature/sdk/src/main/scala/io/cardell/openfeature/Hooks.scala +++ b/openfeature/sdk/src/main/scala/io/cardell/openfeature/Hooks.scala @@ -16,6 +16,7 @@ package io.cardell.openfeature +import cats.Applicative import cats.Monad import cats.syntax.all._ @@ -24,7 +25,6 @@ import io.cardell.openfeature.FlagValue.DoubleValue import io.cardell.openfeature.FlagValue.IntValue import io.cardell.openfeature.FlagValue.StringValue import io.cardell.openfeature.FlagValue.StructureValue -import cats.Applicative sealed trait FlagValueType diff --git a/openfeature/sdk/src/main/scala/io/cardell/openfeature/provider/Provider.scala b/openfeature/sdk/src/main/scala/io/cardell/openfeature/provider/Provider.scala index c7a090d..5f43108 100644 --- a/openfeature/sdk/src/main/scala/io/cardell/openfeature/provider/Provider.scala +++ b/openfeature/sdk/src/main/scala/io/cardell/openfeature/provider/Provider.scala @@ -21,6 +21,7 @@ import io.cardell.openfeature.ErrorHook import io.cardell.openfeature.Hook trait Provider[F[_]] extends EvaluationProvider[F] { + // TODO remove def beforeHooks: List[BeforeHook[F]] def errorHooks: List[ErrorHook[F]] diff --git a/openfeature/sdk/src/main/scala/io/cardell/openfeature/provider/ProviderImpl.scala b/openfeature/sdk/src/main/scala/io/cardell/openfeature/provider/ProviderImpl.scala index 2f1617e..bef7827 100644 --- a/openfeature/sdk/src/main/scala/io/cardell/openfeature/provider/ProviderImpl.scala +++ b/openfeature/sdk/src/main/scala/io/cardell/openfeature/provider/ProviderImpl.scala @@ -19,9 +19,9 @@ package io.cardell.openfeature.provider import cats.MonadThrow import cats.syntax.all._ +import io.cardell.openfeature.AfterHook import io.cardell.openfeature.BeforeHook import io.cardell.openfeature.ErrorHook -import io.cardell.openfeature.AfterHook import io.cardell.openfeature.EvaluationContext import io.cardell.openfeature.FlagValue import io.cardell.openfeature.Hook diff --git a/openfeature/sdk/src/test/scala/io/cardell/openfeature/FeatureClientImplTest.scala b/openfeature/sdk/src/test/scala/io/cardell/openfeature/FeatureClientImplTest.scala index 5b82965..93a1553 100644 --- a/openfeature/sdk/src/test/scala/io/cardell/openfeature/FeatureClientImplTest.scala +++ b/openfeature/sdk/src/test/scala/io/cardell/openfeature/FeatureClientImplTest.scala @@ -48,7 +48,11 @@ class FeatureClientImplTest extends CatsEffectSuite { val client = FeatureClientImpl[IO](provider) - val result = client.withHook(beforeHook1).beforeHooks + val result = + client + .withHook(beforeHook1) + .asInstanceOf[FeatureClientImpl[IO]] + .beforeHooks assertEquals(expected, result) } @@ -114,6 +118,21 @@ class FeatureClientImplTest extends CatsEffectSuite { } } + test("before hooks run on boolean evaluation") { + val ref = Ref.unsafe[IO, Int](0) + + val afterHook = AfterHook[IO] { case _ => ref.update(_ + 2) } + + val client = FeatureClientImpl[IO](provider).withHook(afterHook) + + val expected = 2 + + for { + _ <- client.getBooleanValue("test-flag", false) + result <- ref.get + } yield assertEquals(result, expected) + } + } object ThrowingEvaluationProvider extends EvaluationProvider[IO] { diff --git a/openfeature/sdk/src/test/scala/io/cardell/openfeature/provider/ProviderImplTest.scala b/openfeature/sdk/src/test/scala/io/cardell/openfeature/provider/ProviderImplTest.scala index 2540874..1c1c67d 100644 --- a/openfeature/sdk/src/test/scala/io/cardell/openfeature/provider/ProviderImplTest.scala +++ b/openfeature/sdk/src/test/scala/io/cardell/openfeature/provider/ProviderImplTest.scala @@ -20,9 +20,9 @@ import cats.effect.IO import cats.effect.kernel.Ref import munit.CatsEffectSuite +import io.cardell.openfeature.AfterHook import io.cardell.openfeature.BeforeHook import io.cardell.openfeature.ErrorHook -import io.cardell.openfeature.AfterHook import io.cardell.openfeature.EvaluationContext import io.cardell.openfeature.HookContext import io.cardell.openfeature.HookHints