Skip to content

Commit

Permalink
Add MTL version of launch darkly client
Browse files Browse the repository at this point in the history
  • Loading branch information
zarthross committed Jul 19, 2024
1 parent 382a167 commit f5325d9
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 3 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p core/.jvm/target testkit/.jvm/target project/target
run: mkdir -p core/.jvm/target mtl/.jvm/target testkit/.jvm/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar core/.jvm/target testkit/.jvm/target project/target
run: tar cf targets.tar core/.jvm/target mtl/.jvm/target testkit/.jvm/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down
13 changes: 12 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ val Scala213 = "2.13.12"
ThisBuild / crossScalaVersions := Seq(Scala213, "3.3.3")
ThisBuild / scalaVersion := Scala213 // the default Scala

lazy val root = tlCrossRootProject.aggregate(core, testkit)
lazy val root = tlCrossRootProject.aggregate(core, mtl, testkit)

lazy val testkit = crossProject(JVMPlatform)
.crossType(CrossType.Pure)
Expand Down Expand Up @@ -49,4 +49,15 @@ lazy val core = crossProject(JVMPlatform)
),
)

lazy val mtl = crossProject(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("mtl"))
.settings(
name := "catapult-mtl",
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-mtl" % "1.4.0"
),
)
.dependsOn(core)

lazy val docs = project.in(file("site")).enablePlugins(TypelevelSitePlugin)
156 changes: 156 additions & 0 deletions mtl/src/main/scala/org.typelevel/catapult/LaunchDarklyMTLClient.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright 2022 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.typelevel.catapult

import cats.effect.{Async, Resource}
import cats.~>
import com.launchdarkly.sdk.server.interfaces.{FlagValueChangeEvent, FlagValueChangeListener}
import com.launchdarkly.sdk.server.{LDClient, LDConfig}
import com.launchdarkly.sdk.LDContext
import com.launchdarkly.sdk.LDValue
import fs2._
import cats._
import cats.implicits._
import cats.mtl._
import cats.mtl.syntax._

trait LaunchDarklyMTLClient[F[_]] {

/** @param featureKey the key of the flag to be evaluated
* @param defaultValue the value to use if evaluation fails for any reason
* @return the flag value, suspended in the `F` effect. If evaluation fails for any reason, or the evaluated value is not of type Boolean, returns the default value.
* @see [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/server/interfaces/LDClientInterface.html#boolVariation(java.lang.String,com.launchdarkly.sdk.LDContext,boolean) LDClientInterface#boolVariation]]
*/
def boolVariation(
featureKey: String,
defaultValue: Boolean,
): F[Boolean]

/** @param featureKey the key of the flag to be evaluated
* @param defaultValue the value to use if evaluation fails for any reason
* @return the flag value, suspended in the `F` effect. If evaluation fails for any reason, or the evaluated value is not of type String, returns the default value.
* @see [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/server/interfaces/LDClientInterface.html#stringVariation(java.lang.String,com.launchdarkly.sdk.LDContext,string) LDClientInterface#stringVariation]]
*/
def stringVariation(
featureKey: String,
defaultValue: String,
): F[String]

/** @param featureKey the key of the flag to be evaluated
* @param defaultValue the value to use if evaluation fails for any reason
* @return the flag value, suspended in the `F` effect. If evaluation fails for any reason, or the evaluated value cannot be represented as type Int, returns the default value.
* @see [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/server/interfaces/LDClientInterface.html#intVariation(java.lang.String,com.launchdarkly.sdk.LDContext,int) LDClientInterface#intVariation]]
*/

/** @param featureKey the key of the flag to be evaluated
* @param defaultValue the value to use if evaluation fails for any reason
* @return the flag value, suspended in the `F` effect. If evaluation fails for any reason, or the evaluated value cannot be represented as type Double, returns the default value.
* @see [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/server/interfaces/LDClientInterface.html#doubleVariation(java.lang.String,com.launchdarkly.sdk.LDContext,double) LDClientInterface#doubleVariation]]
*/
def doubleVariation(
featureKey: String,
defaultValue: Double,
): F[Double]

/** @param featureKey the key of the flag to be evaluated
* @param defaultValue the value to use if evaluation fails for any reason
* @return the flag value, suspended in the `F` effect. If evaluation fails for any reason, returns the default value.
* @see [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/server/interfaces/LDClientInterface.html#jsonValueVariation(java.lang.String,com.launchdarkly.sdk.LDContext,com.launchdarkly.sdk.LDValue) LDClientInterface#jsonValueVariation]]
*/
def jsonValueVariation(
featureKey: String,
defaultValue: LDValue,
): F[LDValue]

/** @param featureKey the key of the flag to be evaluated
* @return A `Stream` of [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/server/interfaces/FlagValueChangeEvent.html FlagValueChangeEvent]] instances representing changes to the value of the flag in the provided context. Note: if the flag value changes multiple times in quick succession, some intermediate values may be missed; for example, a change from 1` to `2` to `3` may be represented only as a change from `1` to `3`
* @see [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/server/interfaces/FlagTracker.html FlagTracker]]
*/
def trackFlagValueChanges(
featureKey: String
): Stream[F, FlagValueChangeEvent]

/** @see [[https://javadoc.io/doc/com.launchdarkly/launchdarkly-java-server-sdk/latest/com/launchdarkly/sdk/server/interfaces/LDClientInterface.html#flush() LDClientInterface#flush]]
*/
def flush: F[Unit]

def mapK[G[_]](fk: F ~ G): LaunchDarklyMTLClient[G] = LaunchDarklyMTLClient.mapk(this)(fk)
}

object LaunchDarklyMTLClient {

/** @return a Catapult [[LaunchDarklyMTLClient]] wrapped in [[cats.effect.Resource]], created using the given SDK key and config
*/
def resource[F[_]: Async](sdkKey: String, config: LDConfig)(implicit
contextAsk: Ask[F, LDContext]
): Resource[F, LaunchDarklyMTLClient[F]] =
LaunchDarklyClient.resource[F](sdkKey, config).map(fromLaunchDarklyClient[F](_))

/** @return a Catapult [[LaunchDarklyMTLClient]] wrapped in [[cats.effect.Resource]], created using the given SDK key and default config
*/
def resource[F[_]: Async](sdkKey: String)(implicit
contextAsk: Ask[F, LDContext]
): Resource[F, LaunchDarklyMTLClient[F]] =
LaunchDarklyClient.resource[F](sdkKey).map(fromLaunchDarklyClient[F](_))

def fromLaunchDarklyClient[F[_]](
launchDarklyClient: LaunchDarklyClient[F]
)(implicit F: Monad[F], contextAsk: Ask[F, LDContext]): LaunchDarklyMTLClient[F] =
new LaunchDarklyMTLClient[F] {
def flush: F[Unit] = launchDarklyClient.flush

def boolVariation(featureKey: String, defaultValue: Boolean): F[Boolean] =
contextAsk.ask.flatMap(launchDarklyClient.boolVariation(featureKey, _, defaultValue))

def doubleVariation(featureKey: String, defaultValue: Double): F[Double] =
contextAsk.ask.flatMap(launchDarklyClient.doubleVariation(featureKey, _, defaultValue))

def jsonValueVariation(
featureKey: String,
defaultValue: com.launchdarkly.sdk.LDValue,
): F[com.launchdarkly.sdk.LDValue] =
contextAsk.ask.flatMap(launchDarklyClient.jsonValueVariation(featureKey, _, defaultValue))

def stringVariation(featureKey: String, defaultValue: String): F[String] =
contextAsk.ask.flatMap(launchDarklyClient.stringVariation(featureKey, _, defaultValue))

def trackFlagValueChanges(
featureKey: String
): fs2.Stream[F, com.launchdarkly.sdk.server.interfaces.FlagValueChangeEvent] =
Stream.eval(contextAsk.ask).flatMap(launchDarklyClient.trackFlagValueChanges(featureKey, _))
}

def mapK[F[_], G[_]](ldc: LaunchDarklyMTLClient[F])(fk: F ~> G): LaunchDarklyMTLClient[G] =
new LaunchDarklyMTLClient[G] {
def boolVariation(featureKey: String, defaultValue: Boolean): G[Boolean] =
fk(ldc.boolVariation(featureKey, defaultValue))

def stringVariation(featureKey: String, defaultValue: String): G[String] =
fk(ldc.stringVariation(featureKey, defaultValue))

def doubleVariation(featureKey: String, defaultValue: Double): G[Double] =
fk(ldc.doubleVariation(featureKey, defaultValue))

def jsonValueVariation(featureKey: String, defaultValue: LDValue): G[LDValue] =
fk(ldc.jsonValueVariation(featureKey, defaultValue))

def trackFlagValueChanges(featureKey: String): Stream[G, FlagValueChangeEvent] =
ldc.trackFlagValueChanges(featureKey).translate(fk)

def flush: G[Unit] = fk(ldc.flush)
}
}

0 comments on commit f5325d9

Please sign in to comment.