From cc69ed9ca2a02dcd9f85f12f5cb9e44ee116c662 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 14:25:42 -0700 Subject: [PATCH 01/14] Update android instructions --- internal/sdks/sdk_instructions/android.md | 146 +++++++++++++--------- 1 file changed, 89 insertions(+), 57 deletions(-) diff --git a/internal/sdks/sdk_instructions/android.md b/internal/sdks/sdk_instructions/android.md index b757b301..561f3052 100644 --- a/internal/sdks/sdk_instructions/android.md +++ b/internal/sdks/sdk_instructions/android.md @@ -11,41 +11,66 @@ dependencies { 3. Open the file `MainActivity.kt` and add the following code: ```java +package com.launchdarkly.hello_android + +import android.os.Bundle +import android.view.View +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import com.launchdarkly.hello_android.MainApplication.Companion.LAUNCHDARKLY_MOBILE_KEY import com.launchdarkly.sdk.android.LDClient class MainActivity : AppCompatActivity() { - // Set BOOLEAN_FLAG_KEY to the boolean feature flag you want to evaluate. - val BOOLEAN_FLAG_KEY = "my-flag-key" - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - val textView : TextView = findViewById(R.id.textview) - - val client = LDClient.get() - - // to get the variation the SDK has cached - textView.text = getString( - R.string.flag_evaluated, - BOOLEAN_FLAG_KEY, - client.boolVariation(BOOLEAN_FLAG_KEY, false).toString() - ) - - // to register a listener to get updates in real time - client.registerFeatureFlagListener(BOOLEAN_FLAG_KEY) { - textView.text = getString( - R.string.flag_evaluated, - BOOLEAN_FLAG_KEY, - client.boolVariation(BOOLEAN_FLAG_KEY, false).toString() - ) - } - - // This call ensures all evaluation events show up immediately for this demo. Otherwise, the - // SDK sends them at some point in the future. You don't need to call this in production, - // because the SDK handles them automatically at an interval. The interval is customizable. - client.flush() - } + // Set BOOLEAN_FLAG_KEY to the feature flag key you want to evaluate. + val BOOLEAN_FLAG_KEY = "my-flag-key" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + val textView : TextView = findViewById(R.id.textview) + val fullView : View = window.decorView + + if (LAUNCHDARKLY_MOBILE_KEY == "example-mobile-key") { + val builder = AlertDialog.Builder(this) + builder.setMessage("LAUNCHDARKLY_MOBILE_KEY was not customized for this application.") + builder.create().show() + } + + val client = LDClient.get() + val flagValue = client.boolVariation(BOOLEAN_FLAG_KEY, false) + + // to get the variation the SDK has cached + textView.text = getString( + R.string.flag_evaluated, + BOOLEAN_FLAG_KEY, + flagValue.toString() + ) + + // Style the display + textView.setTextColor(resources.getColor(R.color.colorText)) + if(flagValue) { + fullView.setBackgroundColor(resources.getColor(R.color.colorBackgroundTrue)) + } else { + fullView.setBackgroundColor(resources.getColor(R.color.colorBackgroundFalse)) + } + + // to register a listener to get updates in real time + client.registerFeatureFlagListener(BOOLEAN_FLAG_KEY) { + val changedFlagValue = client.boolVariation(BOOLEAN_FLAG_KEY, false) + textView.text = getString( + R.string.flag_evaluated, + BOOLEAN_FLAG_KEY, + changedFlagValue.toString() + ) + if(changedFlagValue) { + fullView.setBackgroundColor(resources.getColor(R.color.colorBackgroundTrue)) + } else { + fullView.setBackgroundColor(resources.getColor(R.color.colorBackgroundFalse)) + } + } + } } ``` @@ -60,47 +85,54 @@ class MainActivity : AppCompatActivity() { 5. Create `MainApplication.kt` and add the following code: ```java +package com.launchdarkly.hello_android + +import android.app.Application import com.launchdarkly.sdk.ContextKind import com.launchdarkly.sdk.LDContext import com.launchdarkly.sdk.android.LDClient import com.launchdarkly.sdk.android.LDConfig +import com.launchdarkly.sdk.android.LDConfig.Builder.AutoEnvAttributes class MainApplication : Application() { - companion object { - - // Set LAUNCHDARKLY_MOBILE_KEY to your LaunchDarkly SDK mobile key. - const val LAUNCHDARKLY_MOBILE_KEY = "myMobileKey" - } + companion object { - override fun onCreate() { - super.onCreate() + // Set LAUNCHDARKLY_MOBILE_KEY to your LaunchDarkly SDK mobile key. + const val LAUNCHDARKLY_MOBILE_KEY = "myMobileKey" + } - val ldConfig = LDConfig.Builder(AutoEnvAttributes.Enabled) - .mobileKey(LAUNCHDARKLY_MOBILE_KEY) - .build() + override fun onCreate() { + super.onCreate() - // Set up the context properties. This context should appear on your LaunchDarkly contexts - // list soon after you run the demo. - val context = if (isUserLoggedIn()) { - LDContext.builder(ContextKind.DEFAULT, getUserKey()) - .name(getUserName()) - .build() - } else { - LDContext.builder(ContextKind.DEFAULT, "example-user-key") - .anonymous(true) - .build() - } + // Set LAUNCHDARKLY_MOBILE_KEY to your LaunchDarkly mobile key found on the LaunchDarkly + // dashboard in the start guide. + // If you want to disable the Auto EnvironmentAttributes functionality. + // Use AutoEnvAttributes.Disabled as the argument to the Builder + val ldConfig = LDConfig.Builder(AutoEnvAttributes.Enabled) + .mobileKey(LAUNCHDARKLY_MOBILE_KEY) + .build() - LDClient.init(this@MainApplication, ldConfig, context) - } + // Set up the context properties. This context should appear on your LaunchDarkly context + // dashboard soon after you run the demo. + val context = if (isUserLoggedIn()) { + LDContext.builder(ContextKind.DEFAULT, getUserKey()) + .name(getUserName()) + .build() + } else { + LDContext.builder(ContextKind.DEFAULT, "example-user-key") + .anonymous(true) + .build() + } - private fun isUserLoggedIn(): Boolean = false + LDClient.init(this@MainApplication, ldConfig, context) + } - private fun getUserKey(): String = "user-key-123abc" + private fun isUserLoggedIn(): Boolean = false - private fun getUserName(): String = "Sandy" + private fun getUserKey(): String = "example-user-key" + private fun getUserName(): String = "Sandy" } ``` From 924ad1f6ec338bc69397d3cd199a3ee8b67f768d Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 14:30:59 -0700 Subject: [PATCH 02/14] Update .net SDK instructions --- .../sdks/sdk_instructions/dotnet-server.md | 112 ++++++++++++------ 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/internal/sdks/sdk_instructions/dotnet-server.md b/internal/sdks/sdk_instructions/dotnet-server.md index 45a8efc5..ccf7b494 100644 --- a/internal/sdks/sdk_instructions/dotnet-server.md +++ b/internal/sdks/sdk_instructions/dotnet-server.md @@ -9,60 +9,98 @@ Install-Package LaunchDarkly.ServerSdk 3. Open the file `Program.cs` and add the following code: ```cs using System; - using LaunchDarkly.Sdk; - using LaunchDarkly.Sdk.Server; +using System.Threading.Tasks; +using LaunchDarkly.Sdk; +using LaunchDarkly.Sdk.Server; - namespace HelloDotNet +namespace HelloDotNet +{ + class Hello { - class Program + public static void ShowBanner(){ + Console.WriteLine( +@" ██ + ██ + ████████ + ███████ +██ LAUNCHDARKLY █ + ███████ + ████████ + ██ + ██ +"); + } + + static void Main(string[] args) { - // Set SdkKey to your LaunchDarkly SDK key. - public const string SdkKey = "1234567890abcdef"; + bool CI = Environment.GetEnvironmentVariable("CI") != null; + + string SdkKey = Environment.GetEnvironmentVariable("LAUNCHDARKLY_SDK_KEY"); // Set FeatureFlagKey to the feature flag key you want to evaluate. - public const string FeatureFlagKey = "my-flag-key"; + string FeatureFlagKey = "my-flag-key"; - private static void ShowMessage(string s) { - Console.WriteLine("*** " + s); - Console.WriteLine(); + if (string.IsNullOrEmpty(SdkKey)) + { + Console.WriteLine("*** Please set LAUNCHDARKLY_SDK_KEY environment variable to your LaunchDarkly SDK key first\n"); + Environment.Exit(1); } - static void Main(string[] args) - { - var ldConfig = Configuration.Default(SdkKey); + var ldConfig = Configuration.Default(SdkKey); - var client = new LdClient(ldConfig); + var client = new LdClient(ldConfig); - if (client.Initialized) - { - ShowMessage("SDK successfully initialized!"); - } - else - { - ShowMessage("SDK failed to initialize"); - Environment.Exit(1); - } + if (client.Initialized) + { + Console.WriteLine("*** SDK successfully initialized!\n"); + } + else + { + Console.WriteLine("*** SDK failed to initialize\n"); + Environment.Exit(1); + } - // Set up the evaluation context. This context should appear on your LaunchDarkly contexts - // dashboard soon after you run the demo. - var context = Context.Builder("example-user-key") - .Name("Sandy") - .Build(); + // Set up the evaluation context. This context should appear on your LaunchDarkly contexts + // dashboard soon after you run the demo. + var context = Context.Builder("example-user-key") + .Name("Sandy") + .Build(); - var flagValue = client.BoolVariation(FeatureFlagKey, context, false); + if (Environment.GetEnvironmentVariable("LAUNCHDARKLY_FLAG_KEY") != null) + { + FeatureFlagKey = Environment.GetEnvironmentVariable("LAUNCHDARKLY_FLAG_KEY"); + } + + var flagValue = client.BoolVariation(FeatureFlagKey, context, false); - ShowMessage(string.Format("Feature flag '{0}' is {1}", - FeatureFlagKey, flagValue)); + Console.WriteLine(string.Format("*** The {0} feature flag evaluates to {1}.\n", + FeatureFlagKey, flagValue)); - // Here we ensure that the SDK shuts down cleanly and has a chance to deliver analytics - // events to LaunchDarkly before the program exits. If analytics events are not delivered, - // the context attributes and flag usage statistics will not appear on your dashboard. In - // a normal long-running application, the SDK would continue running and events would be - // delivered automatically in the background. - client.Dispose(); + if (flagValue) + { + ShowBanner(); } + + client.FlagTracker.FlagChanged += client.FlagTracker.FlagValueChangeHandler( + FeatureFlagKey, + context, + (sender, changeArgs) => { + Console.WriteLine(string.Format("*** The {0} feature flag evaluates to {1}.\n", + FeatureFlagKey, changeArgs.NewValue)); + + if (changeArgs.NewValue.AsBool) ShowBanner(); + } + ); + + if(CI) Environment.Exit(0); + + Console.WriteLine("*** Waiting for changes \n"); + + Task waitForever = new Task(() => {}); + waitForever.Wait(); } } +} ``` Now that your application is ready, run the application to see what value we get. From dd529eed4b0c54c1e78925642bc874488b4c5e39 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 14:38:41 -0700 Subject: [PATCH 03/14] Update go SDK instructions --- internal/sdks/sdk_instructions/go.md | 102 ++++++++++++++++++--------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/internal/sdks/sdk_instructions/go.md b/internal/sdks/sdk_instructions/go.md index 1e515f55..2716588b 100644 --- a/internal/sdks/sdk_instructions/go.md +++ b/internal/sdks/sdk_instructions/go.md @@ -17,52 +17,84 @@ go get github.com/launchdarkly/go-server-sdk/v6 4. Create a file called main.go and add the following code: ```go package main - import ( - "fmt" - "os" - "time" - "github.com/launchdarkly/go-sdk-common/v3/ldcontext" - ld "github.com/launchdarkly/go-server-sdk/v6" - ) +import ( + "fmt" + "os" + "time" - // Set sdkKey to your LaunchDarkly SDK key. - const sdkKey = "1234567890abcdef" + "github.com/launchdarkly/go-sdk-common/v3/ldcontext" + "github.com/launchdarkly/go-sdk-common/v3/ldvalue" + ld "github.com/launchdarkly/go-server-sdk/v7" +) + +func showBanner() { + fmt.Print("\n ██ \n" + + " ██ \n" + + " ████████ \n" + + " ███████ \n" + + "██ LAUNCHDARKLY █\n" + + " ███████ \n" + + " ████████ \n" + + " ██ \n" + + " ██ \n") +} + +func showMessage(s string) { fmt.Printf("*** %s\n\n", s) } + +func main() { + var sdkKey = os.Getenv("LAUNCHDARKLY_SDK_KEY") + + if sdkKey == "" { + showMessage("LaunchDarkly SDK key is required: set the LAUNCHDARKLY_SDK_KEY environment variable and try again.") + os.Exit(1) + } + + ldClient, _ := ld.MakeClient(sdkKey, 5*time.Second) + if ldClient.Initialized() { + showMessage("SDK successfully initialized!") + } else { + showMessage("SDK failed to initialize") + os.Exit(1) + } + + // Set up the evaluation context. This context should appear on your LaunchDarkly contexts dashboard + // soon after you run the demo. + context := ldcontext.NewBuilder("example-user-key"). + Name("Sandy"). + Build() // Set featureFlagKey to the feature flag key you want to evaluate. - const featureFlagKey = "my-flag-key" + var featureFlagKey = "my-flag-key" - func showMessage(s string) { fmt.Printf("*** %%s\n\n", s) } + if os.Getenv("LAUNCHDARKLY_FLAG_KEY") != "" { + featureFlagKey = os.Getenv("LAUNCHDARKLY_FLAG_KEY") + } - func main() { - ldClient, _ := ld.MakeClient(sdkKey, 5*time.Second) - if ldClient.Initialized() { - showMessage("SDK successfully initialized!") - } else { - showMessage("SDK failed to initialize") - os.Exit(1) - } + flagValue, err := ldClient.BoolVariation(featureFlagKey, context, false) + if err != nil { + showMessage("error: " + err.Error()) + } - // Set up the evaluation context. This context should appear on your LaunchDarkly contexts dashboard - // soon after you run the demo. - context := ldcontext.NewBuilder("example-user-key"). - Name("Sandy"). - Build() + showMessage(fmt.Sprintf("The '%%s' feature flag evaluates to %%t.", featureFlagKey, flagValue)) - flagValue, err := ldClient.BoolVariation(featureFlagKey, context, false) - if err != nil { - showMessage("error: " + err.Error()) - } + if flagValue { + showBanner() + } + + if os.Getenv("CI") != "" { + os.Exit(0) + } - showMessage(fmt.Sprintf("Feature flag '%%s' is %%t ", featureFlagKey, flagValue)) + updateCh := ldClient.GetFlagTracker().AddFlagValueChangeListener(featureFlagKey, context, ldvalue.Null()) - // Here we ensure that the SDK shuts down cleanly and has a chance to deliver analytics - // events to LaunchDarkly before the program exits. If analytics events are not delivered, - // the context attributes and flag usage statistics will not appear on your dashboard. In - // a normal long-running application, the SDK would continue running and events would be - // delivered automatically in the background. - ldClient.Close() + for event := range updateCh { + showMessage(fmt.Sprintf("The '%%s' feature flag evaluates to %%t.", featureFlagKey, event.NewValue.BoolValue())) + if event.NewValue.BoolValue() { + showBanner() + } } +} ``` Now that your application is ready, run the application to see what value we get. From 8e4d532610ba4a6b5271c5e05e6bd9b03305764d Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 14:49:39 -0700 Subject: [PATCH 04/14] Update haskell SDK instructions --- .../sdks/sdk_instructions/haskell-server.md | 97 ++++++++++++++----- 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/internal/sdks/sdk_instructions/haskell-server.md b/internal/sdks/sdk_instructions/haskell-server.md index e64cefba..94b3982a 100644 --- a/internal/sdks/sdk_instructions/haskell-server.md +++ b/internal/sdks/sdk_instructions/haskell-server.md @@ -16,36 +16,87 @@ launchdarkly-server-sdk, text 4. Edit `app/Main.hs` by adding the following code: ```haskell -{-# LANGUAGE OverloadedStrings #-} - +{-# LANGUAGE OverloadedStrings, NumericUnderscores #-} module Main where - --- Import helper libraries. import Control.Concurrent (threadDelay) import Control.Monad (forever) +import Data.Text (Text, pack) +import Data.Function ((&)) +import qualified LaunchDarkly.Server as LD +import System.Timeout (timeout) +import Text.Printf (printf, hPrintf) +import System.Environment (lookupEnv) + +showEvaluationResult :: String -> Bool -> IO () +showEvaluationResult key value = do + printf "*** The %%s feature flag evaluates to %%s\n" key (show value) + +showBanner :: IO () +showBanner = putStr "\n\ +\ ██ \n\ +\ ██ \n\ +\ ████████ \n\ +\ ███████ \n\ +\██ LAUNCHDARKLY █\n\ +\ ███████ \n\ +\ ████████ \n\ +\ ██ \n\ +\ ██ \n\ +\\n\ +\" + +showMessage :: String -> Bool -> Maybe Bool -> Bool -> IO Bool +showMessage key True _ True = do + showBanner + showEvaluationResult key True + pure False +showMessage key value Nothing showBanner = do + showEvaluationResult key value + pure showBanner +showMessage key value (Just lastValue) showBanner + | value /= lastValue = do + showEvaluationResult key value + pure showBanner + | otherwise = pure showBanner + +waitForClient :: LD.Client -> IO Bool +waitForClient client = do + status <- LD.getStatus client + case status of + LD.Uninitialized -> threadDelay (1 * 1_000) >> waitForClient client + LD.Initialized -> return True + _anyOtherStatus -> return False + +evaluateLoop :: LD.Client -> String -> LD.Context -> Maybe Bool -> Bool -> IO () +evaluateLoop client featureFlagKey context lastValue showBanner = do + value <- LD.boolVariation client (pack featureFlagKey) context False + showBanner' <- showMessage featureFlagKey value lastValue showBanner + + threadDelay (1 * 1_000_000) >> evaluateLoop client featureFlagKey context (Just value) showBanner' + +evaluate :: Maybe String -> Maybe String -> IO () +evaluate (Just sdkKey) Nothing = do evaluate (Just sdkKey) (Just "sample-feature") +evaluate (Just sdkKey) (Just featureFlagKey) = do + -- Set up the evaluation context. This context should appear on your + -- LaunchDarkly contexts dashboard soon after you run the demo. + let context = LD.makeContext "example-user-key" "user" & LD.withName "Sandy" + client <- LD.makeClient $ LD.makeConfig (pack sdkKey) + initialized <- timeout (5_000 * 1_000) (waitForClient client) --- Import the LaunchDarkly SDK. -import LaunchDarkly.Server + case initialized of + Just True -> do + print "*** SDK successfully initialized!" + evaluateLoop client featureFlagKey context Nothing True + _notInitialized -> putStrLn "*** SDK failed to initialize. Please check your internet connection and SDK credential for any typo." +evaluate _ _ = putStrLn "*** You must define LAUNCHDARKLY_SDK_KEY and LAUNCHDARKLY_FLAG_KEY before running this script" --- Define main method main :: IO () main = do - -- Create a new LDClient instance with your environment-specific SDK Key. - client <- makeClient $ makeConfig "1234567890abcdef" - - -- Set up the context properties. This context should appear on your LaunchDarkly contexts dashboard - -- soon after you run the demo. - let context = makeContext "example-user-key" "user" - - forever $ do - -- Call LaunchDarkly with the feature flag key you want to evaluate. - launched <- boolVariation client "my-flag-key" context False - -- Ensure events are sent to LD immediately for fast completion of the Getting Started guide. - -- This line is not necessary here for production use. - flushEvents client - putStrLn $ "Feature Flag my-flag-key is" ++ show launched - -- one second - threadDelay $ 1 * 1000000 + -- Set sdkKey to your LaunchDarkly SDK key. + sdkKey <- lookupEnv "LAUNCHDARKLY_SDK_KEY" + -- Set featureFlagKey to the feature flag key you want to evaluate. + featureFlagKey <- lookupEnv "my-flag-key" + evaluate sdkKey featureFlagKey ``` Now that your application is ready, run the application to see what value we get. From 5e0fe2e381df3562b26f60204f7f63870c2cac52 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 14:49:51 -0700 Subject: [PATCH 05/14] Update ios SDK instructions --- internal/sdks/sdk_instructions/ios-swift.md | 91 ++++++++++----------- 1 file changed, 41 insertions(+), 50 deletions(-) diff --git a/internal/sdks/sdk_instructions/ios-swift.md b/internal/sdks/sdk_instructions/ios-swift.md index 7a222490..86988896 100644 --- a/internal/sdks/sdk_instructions/ios-swift.md +++ b/internal/sdks/sdk_instructions/ios-swift.md @@ -5,7 +5,7 @@ 2. Next, install the LaunchDarkly SDK using [CocoaPods](https://cocoapods.org/) by creating a `Podfile` and adding a dependency (you can also install with [Swift Package Manager](https://docs.launchdarkly.com/sdk/client-side/ios?site=launchDarkly#using-the-swift-package-manager), [Carthage](https://docs.launchdarkly.com/sdk/client-side/ios?site=launchDarkly#using-carthage), or [without a package manager](https://docs.launchdarkly.com/sdk/client-side/ios?site=launchDarkly#installing-the-sdk-manually)): ``` target 'hello-swift' do -pod 'LaunchDarkly', '9.6.2' + pod 'LaunchDarkly', '9.6.2' end ``` @@ -24,76 +24,67 @@ import LaunchDarkly @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? + var window: UIWindow? - // Declare a variable for your mobile-specific SDK key. - private let mobileKey = "myMobileKey" + // Set sdkKey to your LaunchDarkly mobile key. + private let sdkKey = "myMobileKey" - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - setUpLDClient() + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + setUpLDClient() - return true - } - - // Create a function to initialize the LDClient with your mobile-specific - // SDK key, context, and optional configurations. - private func setUpLDClient() { - var contextBuilder = LDContextBuilder(key: "test@email.com") - contextBuilder.trySetValue("firstName", .string("Bob")) - contextBuilder.trySetValue("lastName", .string("Loblaw")) - contextBuilder.trySetValue("groups", .array([.string("beta_testers")])) + return true + } - guard case .success(let context) = contextBuilder.build() - else { return } + private func setUpLDClient() { + // Set up the evaluation context. This context should appear on your + // LaunchDarkly contexts dashboard soon after you run the demo. + var contextBuilder = LDContextBuilder(key: "example-user-key") + contextBuilder.kind("user") + contextBuilder.name("Sandy") - var config = LDConfig(mobileKey: mobileKey, autoEnvAttributes: .enabled) - config.eventFlushInterval = 30.0 + guard case .success(let context) = contextBuilder.build() + else { return } - LDClient.start(config: config, context: context) - } + let config = LDConfig(mobileKey: sdkKey, autoEnvAttributes: .enabled) + LDClient.start(config: config, context: context, startWaitSeconds: 30) + } } ``` 5. Open ViewController.swift and add the following code: ```swift import UIKit -// Import the LaunchDarkly SDK. import LaunchDarkly class ViewController: UIViewController { - // Create a variable for your flag key. - fileprivate let featureFlagKey = "my-flag-key" +fo + @IBOutlet weak var featureFlagLabel: UILabel! - override func viewDidLoad() { - super.viewDidLoad() + // Set featureFlagKey to the feature flag key you want to evaluate. + fileprivate let featureFlagKey = "my-flag-key" - // Observe the LDClient for any feature flag updates. - LDClient.get()?.observe(key: featureFlagKey, owner: self) { [weak self] changedFlag in - self?.featureFlagDidUpdate(changedFlag.key) - } + override func viewDidLoad() { + super.viewDidLoad() + + if let ld = LDClient.get() { + ld.observe(key: featureFlagKey, owner: self) { [weak self] changedFlag in + guard let me = self else { return } + guard case .bool(let booleanValue) = changedFlag.newValue else { return } - checkFeatureValue() - } - - // Create a function to call LaunchDarkly with the feature flag key you want to evaluate and print its value. - fileprivate func checkFeatureValue() { - if let featureFlagValue = LDClient.get() { - let boolVal = featureFlagValue.boolVariation(forKey: featureFlagKey, defaultValue: false) - // Ensure events are sent to LD immediately for fast completion of the Getting Started guide. - // This line is not necessary here for production use. - LDClient.get()?.flush() - print("Feature flag (featureFlagKey) is (boolVal)") - } else { - print("failed to get flag, (featureFlagKey)") + me.updateUi(flagKey: changedFlag.key, result: booleanValue) + } + let result = ld.boolVariation(forKey: featureFlagKey, defaultValue: false) + updateUi(flagKey: featureFlagKey, result: result) + } } - } - // Create a function to respond to flag updates. - func featureFlagDidUpdate(_ key: LDFlagKey) { - if key == featureFlagKey { - checkFeatureValue() + func updateUi(flagKey: String, result: Bool) { + self.featureFlagLabel.text = "The (flagKey) feature flag evaluates to (result)" + + let toggleOn = UIColor(red: 0, green: 0.52, blue: 0.29, alpha: 1) + let toggleOff = UIColor(red: 0.22, green: 0.22, blue: 0.25, alpha: 1) + self.view.backgroundColor = result ? toggleOn : toggleOff } - } } ``` From 4fd795113861c256b113c746a3c6d8c1dc949a31 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 14:58:15 -0700 Subject: [PATCH 06/14] Update java SDK instructions --- internal/sdks/sdk_instructions/java.md | 159 +++++++++++++++---------- 1 file changed, 98 insertions(+), 61 deletions(-) diff --git a/internal/sdks/sdk_instructions/java.md b/internal/sdks/sdk_instructions/java.md index 769d4724..d4b045ba 100644 --- a/internal/sdks/sdk_instructions/java.md +++ b/internal/sdks/sdk_instructions/java.md @@ -4,7 +4,6 @@ mvn archetype:generate -DgroupId=com.launchdarkly.tutorial -DartifactId=hello-java ``` - 2. Change into the project directory: ```shell cd hello-java @@ -15,7 +14,7 @@ cd hello-java com.launchdarkly launchdarkly-java-server-sdk - 7.3.0 + 7.4.0 ``` @@ -48,84 +47,122 @@ cd hello-java 6. Add the following code to `App.java`: ```java +import java.io.IOException; + import com.launchdarkly.sdk.*; import com.launchdarkly.sdk.server.*; public class App { - // This value is already set to your LaunchDarkly SDK key for your Test environment in the Default project. - static final String SDK_KEY = "1234567890abcdef"; +// Set SDK_KEY to your LaunchDarkly SDK key. +static String SDK_KEY = "YOUR_SDK_KEY"; + +// Set FEATURE_FLAG_KEY to the feature flag key you want to evaluate. +static String FEATURE_FLAG_KEY = "my-flag-key"; + +private static void showMessage(String s) { + System.out.println("*** " + s); + System.out.println(); +} + +private static void showBanner() { + showMessage("\n ██ \n" + + " ██ \n" + + " ████████ \n" + + " ███████ \n" + + "██ LAUNCHDARKLY █\n" + + " ███████ \n" + + " ████████ \n" + + " ██ \n" + + " ██ \n"); +} - // This value is already set to flag you just created in the Default project. - static final String FEATURE_FLAG_KEY = "my-flag-key"; +public static void main(String... args) throws Exception { + boolean CIMode = System.getenv("CI") != null; - private static void showMessage(String s) { - System.out.println("*** " + s); - System.out.println(); + String envSDKKey = System.getenv("LAUNCHDARKLY_SDK_KEY"); + if(envSDKKey != null) { + SDK_KEY = envSDKKey; } - public static void main(String... args) throws Exception { - LDConfig config = new LDConfig.Builder().build(); - final LDClient client = new LDClient(SDK_KEY, config); - if (client.isInitialized()) { - showMessage("SDK successfully initialized!"); - } else { - showMessage("SDK failed to initialize"); - System.exit(1); - } + String envFlagKey = System.getenv("LAUNCHDARKLY_FLAG_KEY"); + if(envFlagKey != null) { + FEATURE_FLAG_KEY = envFlagKey; + } + + LDConfig config = new LDConfig.Builder().build(); + + if (SDK_KEY == null || SDK_KEY.equals("")) { + showMessage("Please set the LAUNCHDARKLY_SDK_KEY environment variable or edit Hello.java to set SDK_KEY to your LaunchDarkly SDK key first."); + System.exit(1); + } - // Set up the evaluation context. This context should appear on your - // LaunchDarkly contexts dashboard soon after you run the demo. - final LDContext context = LDContext.builder("example-user-key") - .name("Sandy") - .build(); - - // Evaluate the feature flag. - boolean flagValue = client.boolVariation(FEATURE_FLAG_KEY, context, false); - showMessage("Feature flag '" + FEATURE_FLAG_KEY + "' is " + flagValue); - - // Here we request that the SDK flush pending analytic events so that you see - // data for the above evaluation on the dashboard immediatelynow rather than - // at the next automatic flush interval. You don't need to do this under normal - // circumstances in your own application. - client.flush(); - - // We set up a flag change listener so you can see flag changes as you change - // the flag rules. - client.getFlagTracker().addFlagChangeListener(event -> { - showMessage("Feature flag '" + event.getKey() + "' has changed."); - if (event.getKey().equals(FEATURE_FLAG_KEY)) { - boolean value = client.boolVariation(FEATURE_FLAG_KEY, context, false); - showMessage("Feature flag '" + FEATURE_FLAG_KEY + "' is " + value ); + final LDClient client = new LDClient(SDK_KEY, config); + if (client.isInitialized()) { + showMessage("SDK successfully initialized!"); + } else { + showMessage("SDK failed to initialize. Please check your internet connection and SDK credential for any typo."); + System.exit(1); + } + + // Set up the evaluation context. This context should appear on your + // LaunchDarkly contexts dashboard soon after you run the demo. + final LDContext context = LDContext.builder("example-user-key") + .name("Sandy") + .build(); + + // Evaluate the feature flag for this context. + boolean flagValue = client.boolVariation(FEATURE_FLAG_KEY, context, false); + showMessage("The '" + FEATURE_FLAG_KEY + "' feature flag evaluates to " + flagValue + "."); + + if (flagValue) { + showBanner(); + } + + //If this is building for CI, we don't need to keep running the Hello App continously. + if(CIMode) { + System.exit(0); + } + + // We set up a flag change listener so you can see flag changes as you change + // the flag rules. + client.getFlagTracker().addFlagValueChangeListener(FEATURE_FLAG_KEY, context, event -> { + showMessage("The '" + FEATURE_FLAG_KEY + "' feature flag evaluates to " + event.getNewValue() + "."); + + if (event.getNewValue().booleanValue()) { + showBanner(); } - }); - showMessage("Listening for feature flag changes. Use Ctrl+C to terminate."); - - // Here we ensure that when the application terminates, the SDK shuts down - // cleanly and has a chance to deliver analytics events to LaunchDarkly. - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - public void run() { - try { - client.close(); - } catch (IOException e) { - // ignore - } + }); + showMessage("Listening for feature flag changes."); + + // Here we ensure that when the application terminates, the SDK shuts down + // cleanly and has a chance to deliver analytics events to LaunchDarkly. + // If analytics events are not delivered, the context attributes and flag usage + // statistics may not appear on your dashboard. In a normal long-running + // application, the SDK would continue running and events would be delivered + // automatically in the background. + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + public void run() { + try { + client.close(); + } catch (IOException e) { + // ignore } - }, "ldclient-cleanup-thread")); - - // Keeps example application alive. - Object mon = new Object(); - synchronized (mon) { - mon.wait(); } + }, "ldclient-cleanup-thread")); + + // Keeps example application alive. + Object mon = new Object(); + synchronized (mon) { + mon.wait(); } } +} ``` Now that your application is ready, run the application to see what value we get. ```shell -mvn clean compile assembly:single -java -jar target/hello-java-1.0-SNAPSHOT-jar-with-dependencies.jar +mvn clean compile assembly:single && LAUNCHDARKLY_SDK_KEY=YOUR_SDK_KEY java -jar target/hello-java-1.0-SNAPSHOT-jar-with-dependencies.jar ``` You should see: From 5ff203cc6401a5d38e5c5fa0b921a924cfd02cdd Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 15:03:50 -0700 Subject: [PATCH 07/14] Update js SDK instructions --- internal/sdks/sdk_instructions/js.md | 107 +++++++++++++++------------ 1 file changed, 58 insertions(+), 49 deletions(-) diff --git a/internal/sdks/sdk_instructions/js.md b/internal/sdks/sdk_instructions/js.md index 9bb87fbf..0c0d1ea5 100644 --- a/internal/sdks/sdk_instructions/js.md +++ b/internal/sdks/sdk_instructions/js.md @@ -2,57 +2,66 @@ 1. Create a file called `index.html` and add the following code: ```html - - - - - LaunchDarkly tutorial - - - - + + + - - + + ldclient.on('initialized', () => { + div.replaceChild(document.createTextNode('SDK successfully initialized!'), div.firstChild); + }); + ldclient.on('failed', () => { + div.replaceChild(document.createTextNode('SDK failed to initialize'), div.firstChild); + }); + ldclient.on('ready', render); + ldclient.on('change', render); + + } + main(); + + + ``` Now that your application is ready, run the application to see what value we get. From 5ee23d20aded3b273d8a7f09bcfd77e23e02382f Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 15:30:08 -0700 Subject: [PATCH 08/14] Update node SDK instructions --- internal/sdks/sdk_instructions/node-server.md | 79 ++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/internal/sdks/sdk_instructions/node-server.md b/internal/sdks/sdk_instructions/node-server.md index 30880bca..38f50559 100644 --- a/internal/sdks/sdk_instructions/node-server.md +++ b/internal/sdks/sdk_instructions/node-server.md @@ -6,23 +6,43 @@ mkdir hello-node-server && cd hello-node-server && npm init 2. Next, install the LaunchDarkly SDK: ```shell -npm install @launchdarkly/node-server-sdk@9.2.4 --save +npm install @launchdarkly/node-server-sdk@9.4.1 --save ``` 3. Create a file called `index.js` and add the following code: ```js -// Import the LaunchDarkly client. -var LaunchDarkly = require('@launchdarkly/node-server-sdk'); +const LaunchDarkly = require('@launchdarkly/node-server-sdk'); // Set sdkKey to your LaunchDarkly SDK key. -const sdkKey = '1234567890abcdef'; +const sdkKey = process.env.LAUNCHDARKLY_SDK_KEY ?? 'YOUR_SDK_KEY'; // Set featureFlagKey to the feature flag key you want to evaluate. const featureFlagKey = 'my-flag-key'; -function showMessage(s) { - console.log("*** " + s); - console.log(""); +function showBanner() { + console.log( + ` ██ + ██ + ████████ + ███████ +██ LAUNCHDARKLY █ + ███████ + ████████ + ██ + ██ +`, + ); +} + +function printValueAndBanner(flagValue) { + console.log(`*** The '${featureFlagKey}' feature flag evaluates to ${flagValue}.`); + + if (flagValue) showBanner(); +} + +if (!sdkKey) { + console.log('*** Please edit index.js to set sdkKey to your LaunchDarkly SDK key first.'); + process.exit(1); } const ldClient = LaunchDarkly.init(sdkKey); @@ -30,35 +50,38 @@ const ldClient = LaunchDarkly.init(sdkKey); // Set up the context properties. This context should appear on your LaunchDarkly contexts dashboard // soon after you run the demo. const context = { - kind: "user", - key: "example-context-key", - name: "Sandy" + kind: 'user', + key: 'example-user-key', + name: 'Sandy', }; -ldClient.waitForInitialization().then(function () { - showMessage("SDK successfully initialized!"); - ldClient.variation(featureFlagKey, context, false, function (err, flagValue) { - showMessage("Feature flag '" + featureFlagKey + "' is " + flagValue); - - // Here we ensure that the SDK shuts down cleanly and has a chance to deliver analytics - // events to LaunchDarkly before the program exits. If analytics events are not delivered, - // the context properties and flag usage statistics will not appear on your dashboard. In a - // normal long-running application, the SDK would continue running and events would be - // delivered automatically in the background. - ldClient.flush(function () { - ldClient.close(); - }); +ldClient + .waitForInitialization() + .then(() => { + console.log('*** SDK successfully initialized!'); + + const eventKey = `update:${featureFlagKey}`; + ldClient.on(eventKey, () => { + ldClient.variation(featureFlagKey, context, false).then(printValueAndBanner); }); -}).catch(function (error) { - showMessage("SDK failed to initialize: " + error); + + ldClient.variation(featureFlagKey, context, false).then((flagValue) => { + printValueAndBanner(flagValue); + + if(typeof process.env.CI !== "undefined") { + process.exit(0); + } + }); + }) + .catch((error) => { + console.log(`*** SDK failed to initialize: ${error}`); process.exit(1); -}); + }); ``` - Now that your application is ready, run the application to see what value we get. ```shell -node index.js +LAUNCHDARKLY_SDK_KEY=YOUR_SDK_KEY node index.js ``` You should see: From 66c46e8fdd21e470bc443d6f13e732a9beb13353 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 15:33:58 -0700 Subject: [PATCH 09/14] Update PHP SDK instructions --- internal/sdks/sdk_instructions/go.md | 2 +- internal/sdks/sdk_instructions/php.md | 69 +++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/internal/sdks/sdk_instructions/go.md b/internal/sdks/sdk_instructions/go.md index 2716588b..7a798052 100644 --- a/internal/sdks/sdk_instructions/go.md +++ b/internal/sdks/sdk_instructions/go.md @@ -40,7 +40,7 @@ func showBanner() { " ██ \n") } -func showMessage(s string) { fmt.Printf("*** %s\n\n", s) } +func showMessage(s string) { fmt.Printf("*** %%s\n\n", s) } func main() { var sdkKey = os.Getenv("LAUNCHDARKLY_SDK_KEY") diff --git a/internal/sdks/sdk_instructions/php.md b/internal/sdks/sdk_instructions/php.md index 770d0360..88257365 100644 --- a/internal/sdks/sdk_instructions/php.md +++ b/internal/sdks/sdk_instructions/php.md @@ -12,26 +12,75 @@ php composer.phar require launchdarkly/server-sdk:6.1.0 guzzlehttp/guzzle 3. Create a file called `index.php` and add the following code: ```php name("Sandy") - ->build(); +->kind("user") +->name("Sandy") +->build(); + + +$showBanner = true; +$lastValue = null; +do { + $flagValue = $client->variation($featureFlagKey, $context, false); + + if ($flagValue !== $lastValue) { + showEvaluationResult($featureFlagKey, $flagValue); + } -$flagValue = $client->variation("my-flag-key", $context, false); -$flagValueStr = $flagValue ? 'true' : 'false'; + if ($showBanner && $flagValue) { + showBanner(); + $showBanner = false; + } -echo "*** Feature flag 'my-flag-key' is {$flagValueStr}\\n\\n"; + $lastValue = $flagValue; + sleep(1); +} while(true); ``` Now that your application is ready, run the application to see what value we get. ```shell -php index.php +LAUNCHDARKLY_SDK_KEY=YOUR_SDK_KEY php index.php ``` You should see: From d1f3ea2996d0eaa1c550ff9b8d8b97ab574e6c25 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 15:36:19 -0700 Subject: [PATCH 10/14] Update python SDK instructions --- internal/sdks/sdk_instructions/python.md | 105 ++++++++++++++++------- 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/internal/sdks/sdk_instructions/python.md b/internal/sdks/sdk_instructions/python.md index 309f9c77..8d8f09f6 100644 --- a/internal/sdks/sdk_instructions/python.md +++ b/internal/sdks/sdk_instructions/python.md @@ -8,54 +8,97 @@ mkdir hello-python && cd hello-python 2. Next, create a file called `requirements.txt` with the SDK dependency and install it: ```bash -echo "launchdarkly-server-sdk==9.3.1" >> requirements.txt && pip install -r requirements.txt +echo "launchdarkly-server-sdk==9.4.0" >> requirements.txt && pip install -r requirements.txt ``` 3. Create a file called `test.py` and add the following code: ```python -# Import the LaunchDarkly client. +import os import ldclient from ldclient import Context from ldclient.config import Config +from threading import Lock, Event -# Create a helper function for rendering messages. -def show_message(s): - print("*** {}".format(s)) + +# Set sdk_key to your LaunchDarkly SDK key. +sdk_key = os.getenv("LAUNCHDARKLY_SDK_KEY") + +# Set feature_flag_key to the feature flag key you want to evaluate. +feature_flag_key = "my-flag-key" + + +def show_evaluation_result(key: str, value: bool): + print() + print(f"*** The {key} feature flag evaluates to {value}") + + +def show_banner(): + print() + print(" ██ ") + print(" ██ ") + print(" ████████ ") + print(" ███████ ") + print("██ LAUNCHDARKLY █") + print(" ███████ ") + print(" ████████ ") + print(" ██ ") + print(" ██ ") print() -# Initialize the ldclient with your environment-specific SDK key. + +class FlagValueChangeListener: + def __init__(self): + self.__show_banner = True + self.__lock = Lock() + + def flag_value_change_listener(self, flag_change): + with self.__lock: + if self.__show_banner and flag_change.new_value: + show_banner() + self.__show_banner = False + + show_evaluation_result(flag_change.key, flag_change.new_value) + + if __name__ == "__main__": - ldclient.set_config(Config("1234567890abcdef")) - -# The SDK starts up the first time ldclient.get() is called. -if ldclient.get().is_initialized(): - show_message("SDK successfully initialized!") -else: - show_message("SDK failed to initialize") - exit() - -# Set up the evaluation context. This context should appear on your LaunchDarkly contexts -# dashboard soon after you run the demo. -context = Context.builder('example-user-key').name('Sandy').build() - -# Call LaunchDarkly with the feature flag key you want to evaluate. -flag_value = ldclient.get().variation("my-flag-key", context, False) - -show_message("Feature flag 'my-flag-key' is %%s" %% (flag_value)) - -# Here we ensure that the SDK shuts down cleanly and has a chance to deliver analytics -# events to LaunchDarkly before the program exits. If analytics events are not delivered, -# the user properties and flag usage statistics will not appear on your dashboard. In a -# normal long-running application, the SDK would continue running and events would be -# delivered automatically in the background. -ldclient.get().close() + if not sdk_key: + print("*** Please set the LAUNCHDARKLY_SDK_KEY env first") + exit() + if not feature_flag_key: + print("*** Please set the LAUNCHDARKLY_FLAG_KEY env first") + exit() + + ldclient.set_config(Config(sdk_key)) + + if not ldclient.get().is_initialized(): + print("*** SDK failed to initialize. Please check your internet connection and SDK credential for any typo.") + exit() + + print("*** SDK successfully initialized") + + # Set up the evaluation context. This context should appear on your + # LaunchDarkly contexts dashboard soon after you run the demo. + context = \ + Context.builder('example-user-key').kind('user').name('Sandy').build() + + flag_value = ldclient.get().variation(feature_flag_key, context, False) + show_evaluation_result(feature_flag_key, flag_value) + + change_listener = FlagValueChangeListener() + listener = ldclient.get().flag_tracker \ + .add_flag_value_change_listener(feature_flag_key, context, change_listener.flag_value_change_listener) + + try: + Event().wait() + except KeyboardInterrupt: + pass ``` Now that your application is ready, run the application to see what value we get. ```shell -python test.py +LAUNCHDARKLY_SDK_KEY=YOUR_SDK_KEY python test.py ``` You should see: From 75224d0a92308192af1cf1089fcdf590ec8a2def Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Wed, 1 May 2024 15:39:09 -0700 Subject: [PATCH 11/14] Update roku SDK instructions --- internal/sdks/sdk_instructions/roku.md | 98 ++++++++++++++++++-------- 1 file changed, 69 insertions(+), 29 deletions(-) diff --git a/internal/sdks/sdk_instructions/roku.md b/internal/sdks/sdk_instructions/roku.md index 28571aed..4bada58c 100644 --- a/internal/sdks/sdk_instructions/roku.md +++ b/internal/sdks/sdk_instructions/roku.md @@ -41,49 +41,89 @@ end sub 5. In `components/AppScene.xml` create a basic scene by adding the following code: ```xml - - - - - - -