Skip to content

Commit

Permalink
chore: add C binding example for server-side Redis Source (#470)
Browse files Browse the repository at this point in the history
We had a ready-to-go example for C++, but the only good example for C
was buried in a unit test. I've extracted that out into a buildable
hello-example.
  • Loading branch information
cwaldren-ld authored Dec 12, 2024
1 parent b7ccd28 commit b978ea4
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ add_subdirectory(client-and-server-coexistence)

if (LD_BUILD_REDIS_SUPPORT)
add_subdirectory(hello-cpp-server-redis)
add_subdirectory(hello-c-server-redis)
endif ()
15 changes: 15 additions & 0 deletions examples/hello-c-server-redis/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Required for Apple Silicon support.
cmake_minimum_required(VERSION 3.19)

project(
LaunchDarklyHelloCServerRedis
VERSION 0.1
DESCRIPTION "LaunchDarkly Hello C Server-side SDK with Redis source"
LANGUAGES C
)

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

add_executable(hello-c-server-redis-source main.c)
target_link_libraries(hello-c-server-redis-source PRIVATE launchdarkly::server_redis_source Threads::Threads)
119 changes: 119 additions & 0 deletions examples/hello-c-server-redis/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include <launchdarkly/server_side/bindings/c/sdk.h>

#include <launchdarkly/bindings/c/context_builder.h>

#include <launchdarkly/server_side/bindings/c/integrations/redis/redis_source.h>

Check failure on line 5 in examples/hello-c-server-redis/main.c

View workflow job for this annotation

GitHub Actions / cpp-linter

/examples/hello-c-server-redis/main.c:5:10 [clang-diagnostic-error]

'launchdarkly/server_side/bindings/c/integrations/redis/redis_source.h' file not found

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Set SDK_KEY to your LaunchDarkly SKD key.
#define SDK_KEY ""

// Set FEATURE_FLAG_KEY to the feature flag key you want to evaluate.
#define FEATURE_FLAG_KEY "my-boolean-flag"

// Set INIT_TIMEOUT_MILLISECONDS to the amount of time you will wait for
// the client to become initialized.
#define INIT_TIMEOUT_MILLISECONDS 3000

Check warning on line 20 in examples/hello-c-server-redis/main.c

View workflow job for this annotation

GitHub Actions / cpp-linter

/examples/hello-c-server-redis/main.c:20:1 [modernize-macro-to-enum]

replace macro with enum

Check warning on line 20 in examples/hello-c-server-redis/main.c

View workflow job for this annotation

GitHub Actions / cpp-linter

/examples/hello-c-server-redis/main.c:20:9 [modernize-macro-to-enum]

macro 'INIT_TIMEOUT_MILLISECONDS' defines an integral constant; prefer an enum instead

// Set REDIS_URI to your own Redis instance's URI.
#define REDIS_URI "redis://localhost:6379"

// Set REDIS_PREFIX to the prefix containing the launchdarkly
// environment data in Redis.
#define REDIS_PREFIX "launchdarkly"

char const* get_with_env_fallback(char const* source_val,
char const* env_variable,
char const* error_msg);

int main() {
char const* sdk_key = get_with_env_fallback(
SDK_KEY, "LD_SDK_KEY",
"Please edit main.c to set SDK_KEY to your LaunchDarkly server-side "
"SDK key "
"first.\n\nAlternatively, set the LD_SDK_KEY environment "
"variable.\n"
"The value of SDK_KEY in main.c takes priority over LD_SDK_KEY.");

struct LDServerLazyLoadRedisResult result;
if (!LDServerLazyLoadRedisSource_New(REDIS_URI, REDIS_PREFIX, &result)) {
printf("error: couldn't instantiate Redis source: %s\n",
result.error_message);
return 1;
}

LDServerConfigBuilder config_builder = LDServerConfigBuilder_New(sdk_key);

LDServerLazyLoadBuilder lazy_builder = LDServerLazyLoadBuilder_New();
LDServerLazyLoadBuilder_SourcePtr(lazy_builder,
(LDServerLazyLoadSourcePtr)result.source);
LDServerConfigBuilder_DataSystem_LazyLoad(config_builder, lazy_builder);

LDServerConfig config = NULL;
LDStatus config_status =
LDServerConfigBuilder_Build(config_builder, &config);
if (!LDStatus_Ok(config_status)) {
printf("error: config is invalid: %s\n", LDStatus_Error(config_status));
return 1;
}

LDServerSDK client = LDServerSDK_New(config);

bool initialized_successfully = false;
if (LDServerSDK_Start(client, INIT_TIMEOUT_MILLISECONDS,
&initialized_successfully)) {
if (initialized_successfully) {
printf("*** SDK successfully initialized!\n\n");
} else {
printf("*** SDK failed to initialize\n");
return 1;
}
} else {
printf("*** SDK initialization didn't complete in %dms\n",
INIT_TIMEOUT_MILLISECONDS);
return 1;
}

LDContextBuilder context_builder = LDContextBuilder_New();
LDContextBuilder_AddKind(context_builder, "user", "example-user-key");
LDContextBuilder_Attributes_SetName(context_builder, "user", "Sandy");
LDContext context = LDContextBuilder_Build(context_builder);

bool flag_value =
LDServerSDK_BoolVariation(client, context, FEATURE_FLAG_KEY, false);

printf("*** Feature flag '%s' is %s for this user\n\n", FEATURE_FLAG_KEY,
flag_value ? "true" : "false");

// 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.

LDContext_Free(context);
LDServerSDK_Free(client);

return 0;
}

char const* get_with_env_fallback(char const* source_val,

Check warning on line 105 in examples/hello-c-server-redis/main.c

View workflow job for this annotation

GitHub Actions / cpp-linter

/examples/hello-c-server-redis/main.c:105:35 [bugprone-easily-swappable-parameters]

3 adjacent parameters of 'get_with_env_fallback' of similar type ('const char *') are easily swapped by mistake
char const* env_variable,
char const* error_msg) {
if (strlen(source_val)) {
return source_val;
}

char const* from_env = getenv(env_variable);
if (from_env && strlen(from_env)) {
return from_env;
}

printf("*** %s\n\n", error_msg);
exit(1);
}

0 comments on commit b978ea4

Please sign in to comment.