From d37b5dc9a16ecbd1d7935d4b80ec02dcd04fdb31 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 10 May 2024 15:18:08 +0100 Subject: [PATCH] Basic Client / Provider implementation --- .../openfeature/EvaluationContext.scala | 3 +- .../cardell/openfeature/FeatureClient.scala | 104 ++++++++++++------ .../io/cardell/openfeature/OpenFeature.scala | 17 ++- 3 files changed, 86 insertions(+), 38 deletions(-) diff --git a/open-feature/sdk/src/main/scala/io/cardell/openfeature/EvaluationContext.scala b/open-feature/sdk/src/main/scala/io/cardell/openfeature/EvaluationContext.scala index db56179..e19ef80 100644 --- a/open-feature/sdk/src/main/scala/io/cardell/openfeature/EvaluationContext.scala +++ b/open-feature/sdk/src/main/scala/io/cardell/openfeature/EvaluationContext.scala @@ -1,6 +1,7 @@ package io.cardell.openfeature sealed trait ContextValue + object ContextValue { case class BooleanValue(value: Boolean) extends ContextValue case class StringValue(value: String) extends ContextValue @@ -15,7 +16,7 @@ case class EvaluationContext( def ++(other: EvaluationContext): EvaluationContext = EvaluationContext( other.targetingKey.orElse(targetingKey), - // TODO check other overrides this + // TODO check override order in spec values ++ other.values ) } diff --git a/open-feature/sdk/src/main/scala/io/cardell/openfeature/FeatureClient.scala b/open-feature/sdk/src/main/scala/io/cardell/openfeature/FeatureClient.scala index 9bff132..dedce3a 100644 --- a/open-feature/sdk/src/main/scala/io/cardell/openfeature/FeatureClient.scala +++ b/open-feature/sdk/src/main/scala/io/cardell/openfeature/FeatureClient.scala @@ -13,23 +13,20 @@ object EvaluationOptions { val Defaults: EvaluationOptions = EvaluationOptions() } -case class ClientMetadata() - -trait Hook +// trait Hook case class EvaluationDetails[A](value: A) trait FeatureClient[F[_]] { - def metadata: ClientMetadata def providerMetadata: ProviderMetadata def evaluationContext: EvaluationContext def withEvaluationContext(context: EvaluationContext): FeatureClient[F] - def hooks: List[Hook] - def withHook(hook: Hook): FeatureClient[F] - def withHooks(hooks: List[Hook]): FeatureClient[F] + // def hooks: List[Hook] + // def withHook(hook: Hook): FeatureClient[F] + // def withHooks(hooks: List[Hook]): FeatureClient[F] def getBooleanValue(flagKey: String, default: Boolean): F[Boolean] def getBooleanValue( @@ -102,8 +99,6 @@ protected final class OpenFeatureClient[F[_]: Monad]( clientEvaluationContext: EvaluationContext ) extends FeatureClient[F] { - override def metadata: ClientMetadata = ??? - override def providerMetadata: ProviderMetadata = provider.metadata override def evaluationContext: EvaluationContext = clientEvaluationContext @@ -113,92 +108,133 @@ protected final class OpenFeatureClient[F[_]: Monad]( ): FeatureClient[F] = new OpenFeatureClient[F](provider, clientEvaluationContext ++ context) - override def hooks: List[Hook] = ??? - - override def withHook(hook: Hook): FeatureClient[F] = ??? - - override def withHooks(hooks: List[Hook]): FeatureClient[F] = ??? + // override def hooks: List[Hook] = ??? + // override def withHook(hook: Hook): FeatureClient[F] = ??? + // override def withHooks(hooks: List[Hook]): FeatureClient[F] = ??? override def getBooleanValue(flagKey: String, default: Boolean): F[Boolean] = - provider - .getBooleanEvaluation(flagKey, default, EvaluationContext.empty) - .map(_.value) + getBooleanValue(flagKey, default, EvaluationContext.empty) override def getBooleanValue( flagKey: String, default: Boolean, context: EvaluationContext - ): F[Boolean] = provider - .getBooleanEvaluation(flagKey, default, evaluationContext ++ context) - .map(_.value) + ): F[Boolean] = + getBooleanValue( + flagKey, + default, + context, + EvaluationOptions.Defaults + ) override def getBooleanValue( flagKey: String, default: Boolean, context: EvaluationContext, - options: EvaluationOptions - ): F[Boolean] = ??? + options: EvaluationOptions // TODO handle options + ): F[Boolean] = + provider + .getBooleanEvaluation( + flagKey, + default, + clientEvaluationContext ++ context + ) + .map(_.value) - override def getStringValue(flagKey: String, default: String): F[String] = ??? + override def getStringValue(flagKey: String, default: String): F[String] = + getStringValue(flagKey, default, EvaluationContext.empty) override def getStringValue( flagKey: String, default: String, context: EvaluationContext - ): F[String] = ??? + ): F[String] = + getStringValue( + flagKey, + default, + context, + EvaluationOptions.Defaults + ) override def getStringValue( flagKey: String, default: String, context: EvaluationContext, options: EvaluationOptions - ): F[String] = ??? + ): F[String] = + provider + .getStringEvaluation( + flagKey, + default, + clientEvaluationContext ++ context + ) + .map(_.value) - override def getIntValue(flagKey: String, default: Int): F[Int] = ??? + override def getIntValue(flagKey: String, default: Int): F[Int] = + getIntValue(flagKey, default, EvaluationContext.empty) override def getIntValue( flagKey: String, default: Int, context: EvaluationContext - ): F[Int] = ??? + ): F[Int] = getIntValue(flagKey, default, context, EvaluationOptions.Defaults) override def getIntValue( flagKey: String, default: Int, context: EvaluationContext, options: EvaluationOptions - ): F[Int] = ??? + ): F[Int] = + provider + .getIntEvaluation( + flagKey, + default, + clientEvaluationContext ++ context + ) + .map(_.value) - override def getDoubleValue(flagKey: String, default: Double): F[Double] = ??? + override def getDoubleValue(flagKey: String, default: Double): F[Double] = + getDoubleValue(flagKey, default, EvaluationContext.empty) override def getDoubleValue( flagKey: String, default: Double, context: EvaluationContext - ): F[Double] = ??? + ): F[Double] = + getDoubleValue(flagKey, default, context, EvaluationOptions.Defaults) override def getDoubleValue( flagKey: String, default: Double, context: EvaluationContext, options: EvaluationOptions - ): F[Double] = ??? + ): F[Double] = + provider + .getDoubleEvaluation( + flagKey, + default, + clientEvaluationContext ++ context + ) + .map(_.value) override def getObjectValue[A: ObjectDecoder]( flagKey: String, default: A - ): F[A] = ??? + ): F[A] = + getObjectValue[A](flagKey, default, EvaluationContext.empty) override def getObjectValue[A: ObjectDecoder]( flagKey: String, default: A, context: EvaluationContext - ): F[A] = ??? + ): F[A] = + getObjectValue[A](flagKey, default, context, EvaluationOptions.Defaults) override def getObjectValue[A: ObjectDecoder]( flagKey: String, default: A, context: EvaluationContext, options: EvaluationOptions - ): F[A] = ??? + ): F[A] = + getObjectValue[A](flagKey, default, clientEvaluationContext ++ context) } diff --git a/open-feature/sdk/src/main/scala/io/cardell/openfeature/OpenFeature.scala b/open-feature/sdk/src/main/scala/io/cardell/openfeature/OpenFeature.scala index 290cfc7..97a20af 100644 --- a/open-feature/sdk/src/main/scala/io/cardell/openfeature/OpenFeature.scala +++ b/open-feature/sdk/src/main/scala/io/cardell/openfeature/OpenFeature.scala @@ -1,8 +1,19 @@ package io.cardell.openfeature -import io.cardell.openfeature.provider.ProviderMetadata +import cats.Monad +import cats.syntax.all.* +import io.cardell.openfeature.EvaluationContext +import io.cardell.openfeature.provider.Provider trait OpenFeature[F[_]] { - def client: FeatureClient[F] - def providerMetadata: ProviderMetadata + + /** Create a client using the default provider + */ + def client: F[FeatureClient[F]] +} + +protected[openfeature] class OpenFeatureSdk[F[_]: Monad](provider: Provider[F]) + extends OpenFeature[F] { + def client: F[FeatureClient[F]] = + new OpenFeatureClient[F](provider, EvaluationContext.empty).pure[F].widen }