From 83bf449b364835a4c89560c2c4ee826a0b2b7bba Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Mon, 13 Nov 2023 14:01:32 -0800 Subject: [PATCH 1/4] chore: set OpenSSL root dir properly in CI (#286) Updates install-openssl step to properly set OPENSSL_ROOT_DIR variable, which is passed into CMake build step. Additionally, updates install-boost with correct brew command. --- .github/actions/install-boost/action.yml | 2 +- .github/actions/install-openssl/action.yml | 32 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/.github/actions/install-boost/action.yml b/.github/actions/install-boost/action.yml index b7d05d852..b39568e48 100644 --- a/.github/actions/install-boost/action.yml +++ b/.github/actions/install-boost/action.yml @@ -40,7 +40,7 @@ runs: shell: bash run: | brew install boost - echo "BOOST_ROOT=$(boost --prefix boost@1.82)" >> $GITHUB_OUTPUT + echo "BOOST_ROOT=$(brew --prefix boost)" >> $GITHUB_OUTPUT - name: Determine root id: determine-root diff --git a/.github/actions/install-openssl/action.yml b/.github/actions/install-openssl/action.yml index a6db037f5..b2e4ff4b2 100644 --- a/.github/actions/install-openssl/action.yml +++ b/.github/actions/install-openssl/action.yml @@ -1,6 +1,10 @@ name: Install OpenSSL description: 'Install OpenSSL >= 3 if not already present.' +outputs: + OPENSSL_ROOT_DIR: + description: The location of the installed OpenSSL. + value: ${{ steps.determine-root.outputs.OPENSSL_ROOT_DIR }} runs: using: composite steps: @@ -13,20 +17,40 @@ runs: # The Mac runner already has OpenSSL > 3 via brew, but we need to expose its # install path to CMake. - name: Install for Mac + id: brew-action if: runner.os == 'macOS' shell: bash - run: echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> "$GITHUB_ENV" + run: | + echo "OpenSSL Prefix: $(brew --prefix openssl@3)" + echo "OPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" >> $GITHUB_OUTPUT # The Windows runner has an older version of OpenSSL and needs to be upgraded. - # Additionally it seems to randomly be installed in OpenSSL-Win64 or OpenSSL depending on + # Additionally, it seems to randomly be installed in OpenSSL-Win64 or OpenSSL depending on # the runner Github gives us. - name: Install for Windows + id: choco-action if: runner.os == 'Windows' shell: bash run: | choco upgrade openssl --no-progress if [ -d "C:\Program Files\OpenSSL-Win64" ]; then - echo "OPENSSL_ROOT_DIR=C:\Program Files\OpenSSL-Win64" >> "$GITHUB_ENV" + echo "OPENSSL_ROOT_DIR=C:\Program Files\OpenSSL-Win64" >> $GITHUB_OUTPUT else - echo "OPENSSL_ROOT_DIR=C:\Program Files\OpenSSL" >> "$GITHUB_ENV" + echo "OPENSSL_ROOT_DIR=C:\Program Files\OpenSSL" >> $GITHUB_OUTPUT + fi + + - name: Determine root + id: determine-root + shell: bash + run: | + if [ ! -z "$ROOT_CHOCO" ]; then + echo "OPENSSL_ROOT_DIR=$ROOT_CHOCO" >> $GITHUB_OUTPUT + echo Setting OPENSSL_ROOT_DIR to "$ROOT_CHOCO" + elif [ ! -z "$ROOT_BREW" ]; then + echo "OPENSSL_ROOT_DIR=$ROOT_BREW" >> $GITHUB_OUTPUT + echo Setting OPENSSL_ROOT_DIR to "$ROOT_BREW" fi + + env: + ROOT_CHOCO: ${{ steps.choco-action.outputs.OPENSSL_ROOT_DIR }} + ROOT_BREW: ${{ steps.brew-action.outputs.OPENSSL_ROOT_DIR }} From a608fe4567dd26fa0c2a9e60e14836b9863b317c Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Wed, 15 Nov 2023 17:01:49 -0800 Subject: [PATCH 2/4] chore: add matrix build for hello apps (#281) This introduces a matrix build for running all hello-apps on windows, mac, and linux. The apps are tested in two configurations: static link vs dynamic. Hello apps now receive SDK credentials via environment variable and source, to support injection via CI. Right now a bogus key is injected, and the SDKs are expected to fail. --- .github/workflows/hello-apps.yml | 45 ++++++++++++++++++++++++++++++ examples/hello-c-client/main.c | 34 +++++++++++++++++----- examples/hello-c-server/main.c | 35 ++++++++++++++++++----- examples/hello-cpp-client/main.cpp | 34 +++++++++++++++++----- examples/hello-cpp-server/main.cpp | 34 +++++++++++++++++----- scripts/run-hello-apps.sh | 45 ++++++++++++++++++++++++++++++ 6 files changed, 199 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/hello-apps.yml create mode 100755 scripts/run-hello-apps.sh diff --git a/.github/workflows/hello-apps.yml b/.github/workflows/hello-apps.yml new file mode 100644 index 000000000..07ed35b8c --- /dev/null +++ b/.github/workflows/hello-apps.yml @@ -0,0 +1,45 @@ +name: hello-apps + +on: + push: + branches: [ main ] + paths-ignore: + - '**.md' #Do not need to run CI for markdown changes. + pull_request: + branches: [ main, "feat/**" ] + paths-ignore: + - '**.md' + + +jobs: + smoketest: + strategy: + matrix: + os: [ "ubuntu-22.04", "macos-12", "windows-2022" ] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: ilammy/msvc-dev-cmd@v1 + - name: Install Ninja + uses: ./.github/actions/install-ninja + - name: Install boost + uses: ./.github/actions/install-boost + id: install-boost + - name: Install OpenSSL + uses: ./.github/actions/install-openssl + id: install-openssl + - name: Statically Linked Hello Apps + shell: bash + run: ./scripts/run-hello-apps.sh static hello-c-client hello-cpp-client hello-c-server hello-cpp-server + env: + BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} + OPENSSL_ROOT_DIR: ${{ steps.install-openssl.outputs.OPENSSL_ROOT_DIR }} + - name: Dynamically Linked Hello Apps + shell: bash + continue-on-error: true # TODO(SC-223804) + # Only the C bindings work with dynamic linking because C++ symbols are hidden + run: ./scripts/run-hello-apps.sh dynamic hello-c-client hello-c-server + env: + BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} + OPENSSL_ROOT_DIR: ${{ steps.install-openssl.outputs.OPENSSL_ROOT_DIR }} diff --git a/examples/hello-c-client/main.c b/examples/hello-c-client/main.c index 4915ff191..fd8e48f63 100644 --- a/examples/hello-c-client/main.c +++ b/examples/hello-c-client/main.c @@ -18,16 +18,20 @@ // the client to become initialized. #define INIT_TIMEOUT_MILLISECONDS 3000 +char const* get_with_env_fallback(char const* source_val, + char const* env_variable, + char const* error_msg); + int main() { - if (!strlen(MOBILE_KEY)) { - printf( - "*** Please edit main.c to set MOBILE_KEY to your LaunchDarkly " - "mobile key first\n\n"); - return 1; - } + char const* mobile_key = get_with_env_fallback( + MOBILE_KEY, "LD_MOBILE_KEY", + "Please edit main.c to set MOBILE_KEY to your LaunchDarkly mobile key " + "first.\n\nAlternatively, set the LD_MOBILE_KEY environment " + "variable.\n" + "The value of MOBILE_KEY in main.c takes priority over LD_MOBILE_KEY."); LDClientConfigBuilder config_builder = - LDClientConfigBuilder_New(MOBILE_KEY); + LDClientConfigBuilder_New(mobile_key); LDClientConfig config = NULL; LDStatus config_status = @@ -76,3 +80,19 @@ int main() { return 0; } + +char const* get_with_env_fallback(char const* source_val, + 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); +} diff --git a/examples/hello-c-server/main.c b/examples/hello-c-server/main.c index 4b3f68fcd..5c1acc69a 100644 --- a/examples/hello-c-server/main.c +++ b/examples/hello-c-server/main.c @@ -18,15 +18,20 @@ // the client to become initialized. #define INIT_TIMEOUT_MILLISECONDS 3000 +char const* get_with_env_fallback(char const* source_val, + char const* env_variable, + char const* error_msg); + int main() { - if (!strlen(SDK_KEY)) { - printf( - "*** Please edit main.c to set SDK_KEY to your LaunchDarkly " - "SDK key first\n\n"); - return 1; - } + 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."); - LDServerConfigBuilder config_builder = LDServerConfigBuilder_New(SDK_KEY); + LDServerConfigBuilder config_builder = LDServerConfigBuilder_New(sdk_key); LDServerConfig config = NULL; LDStatus config_status = @@ -76,3 +81,19 @@ int main() { return 0; } + +char const* get_with_env_fallback(char const* source_val, + 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); +} diff --git a/examples/hello-cpp-client/main.cpp b/examples/hello-cpp-client/main.cpp index 99ed3c849..ab7d3d232 100644 --- a/examples/hello-cpp-client/main.cpp +++ b/examples/hello-cpp-client/main.cpp @@ -14,16 +14,20 @@ // the client to become initialized. #define INIT_TIMEOUT_MILLISECONDS 3000 +char const* get_with_env_fallback(char const* source_val, + char const* env_variable, + char const* error_msg); + using namespace launchdarkly; int main() { - if (!strlen(MOBILE_KEY)) { - printf( - "*** Please edit main.cpp to set MOBILE_KEY to your LaunchDarkly " - "mobile key first\n\n"); - return 1; - } + char const* mobile_key = get_with_env_fallback( + MOBILE_KEY, "LD_MOBILE_KEY", + "Please edit main.c to set MOBILE_KEY to your LaunchDarkly mobile key " + "first.\n\nAlternatively, set the LD_MOBILE_KEY environment " + "variable.\n" + "The value of MOBILE_KEY in main.c takes priority over LD_MOBILE_KEY."); - auto config = client_side::ConfigBuilder(MOBILE_KEY).Build(); + auto config = client_side::ConfigBuilder(mobile_key).Build(); if (!config) { std::cout << "error: config is invalid: " << config.error() << '\n'; return 1; @@ -57,3 +61,19 @@ int main() { return 0; } + +char const* get_with_env_fallback(char const* source_val, + char const* env_variable, + char const* error_msg) { + if (strlen(source_val)) { + return source_val; + } + + char const* from_env = std::getenv(env_variable); + if (from_env && strlen(from_env)) { + return from_env; + } + + std::cout << "*** " << error_msg << std::endl; + std::exit(1); +} diff --git a/examples/hello-cpp-server/main.cpp b/examples/hello-cpp-server/main.cpp index 1f975ded5..1f4f9b31d 100644 --- a/examples/hello-cpp-server/main.cpp +++ b/examples/hello-cpp-server/main.cpp @@ -14,16 +14,20 @@ // the client to become initialized. #define INIT_TIMEOUT_MILLISECONDS 3000 +char const* get_with_env_fallback(char const* source_val, + char const* env_variable, + char const* error_msg); using namespace launchdarkly; int main() { - if (!strlen(SDK_KEY)) { - printf( - "*** Please edit main.cpp to set SDK_KEY to your LaunchDarkly " - "SDK key first\n\n"); - return 1; - } + 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."); - auto config = server_side::ConfigBuilder(SDK_KEY).Build(); + auto config = server_side::ConfigBuilder(sdk_key).Build(); if (!config) { std::cout << "error: config is invalid: " << config.error() << '\n'; return 1; @@ -57,3 +61,19 @@ int main() { return 0; } + +char const* get_with_env_fallback(char const* source_val, + char const* env_variable, + char const* error_msg) { + if (strlen(source_val)) { + return source_val; + } + + char const* from_env = std::getenv(env_variable); + if (from_env && strlen(from_env)) { + return from_env; + } + + std::cout << "*** " << error_msg << std::endl; + std::exit(1); +} diff --git a/scripts/run-hello-apps.sh b/scripts/run-hello-apps.sh new file mode 100755 index 000000000..9b87fed5d --- /dev/null +++ b/scripts/run-hello-apps.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# This script builds a set of hello-app cmake targets +# using static or dynamic linkage. +# This script should be ran from the root directory of the project. +# Example: +# ./scripts/run-hello-apps.sh dynamic hello-cpp-client hello-c-client +# +# $1 is the linkage, either 'static' or 'dynamic'. +# Subsequent arguments are cmake target names. + +if [ "$1" != "static" ] && [ "$1" != "dynamic" ] +then + echo "Linkage must be specified ('static' or 'dynamic')" + exit 1 +fi + +dynamic_linkage="Off" +if [ "$1" == "dynamic" ]; then + dynamic_linkage="On" +fi + +shift + +function cleanup { + cd .. +} + +mkdir -p build-"$1" +cd build-"$1" || exit + +# After we enter the directory we want to make sure we always exit it when the +# script ends. +trap cleanup EXIT + +cmake -G Ninja -D CMAKE_BUILD_TYPE=Release -D BUILD_TESTING=OFF -D LD_BUILD_SHARED_LIBS=$dynamic_linkage .. + +export LD_MOBILE_KEY="bogus" +export LD_SDK_KEY="bogus" + +for target in "$@" +do + cmake --build . --target "$target" + ./examples/"$target"/"$target" | tee "$target"_output.txt + grep "failed to initialize" "$target"_output.txt || (echo "$target: expected connection to LD to fail" && exit 1) +done From c58de8f3914bf83fa8662cccf5b284de3179852d Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Thu, 16 Nov 2023 10:01:13 -0800 Subject: [PATCH 3/4] refactor!: move server side config into lib/server (#283) Our current config system utilizes templated builders for different components, but then has a single top-level `Config` shared between client and server. This served us well during development of the server alpha, but it's now time to split the configs. This change moves the serve config into `lib/server`, in preparation for adding new configuration builders/structs. --- .../src/entity_manager.cpp | 2 +- examples/hello-cpp-server/main.cpp | 1 + .../{server.hpp => server_builders.hpp} | 5 +- libs/common/src/config/config_builder.cpp | 1 - libs/common/tests/config_builder_test.cpp | 48 ---------- .../common/tests/data_source_builder_test.cpp | 2 +- libs/common/tests/service_endpoints_test.cpp | 2 +- .../launchdarkly/server_side/client.hpp | 2 +- .../server_side/config/config.hpp | 57 ++++++++++++ .../server_side/config/config_builder.hpp | 93 +++++++++++++++++++ libs/server-sdk/src/CMakeLists.txt | 2 + libs/server-sdk/src/bindings/c/builder.cpp | 2 +- libs/server-sdk/src/bindings/c/config.cpp | 2 +- libs/server-sdk/src/client_impl.cpp | 4 +- libs/server-sdk/src/config/config.cpp | 56 +++++++++++ libs/server-sdk/src/config/config_builder.cpp | 72 ++++++++++++++ libs/server-sdk/tests/client_test.cpp | 11 ++- libs/server-sdk/tests/config_builder_test.cpp | 65 +++++++++++++ 18 files changed, 361 insertions(+), 66 deletions(-) rename libs/common/include/launchdarkly/config/{server.hpp => server_builders.hpp} (84%) create mode 100644 libs/server-sdk/include/launchdarkly/server_side/config/config.hpp create mode 100644 libs/server-sdk/include/launchdarkly/server_side/config/config_builder.hpp create mode 100644 libs/server-sdk/src/config/config.cpp create mode 100644 libs/server-sdk/src/config/config_builder.cpp create mode 100644 libs/server-sdk/tests/config_builder_test.cpp diff --git a/contract-tests/server-contract-tests/src/entity_manager.cpp b/contract-tests/server-contract-tests/src/entity_manager.cpp index 8e8235036..c7db150fb 100644 --- a/contract-tests/server-contract-tests/src/entity_manager.cpp +++ b/contract-tests/server-contract-tests/src/entity_manager.cpp @@ -1,9 +1,9 @@ #include "entity_manager.hpp" #include -#include #include #include +#include using launchdarkly::LogLevel; using namespace launchdarkly::server_side; diff --git a/examples/hello-cpp-server/main.cpp b/examples/hello-cpp-server/main.cpp index 1f4f9b31d..2583ede84 100644 --- a/examples/hello-cpp-server/main.cpp +++ b/examples/hello-cpp-server/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include diff --git a/libs/common/include/launchdarkly/config/server.hpp b/libs/common/include/launchdarkly/config/server_builders.hpp similarity index 84% rename from libs/common/include/launchdarkly/config/server.hpp rename to libs/common/include/launchdarkly/config/server_builders.hpp index d284983f1..453199bf8 100644 --- a/libs/common/include/launchdarkly/config/server.hpp +++ b/libs/common/include/launchdarkly/config/server_builders.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -14,14 +15,10 @@ using SDK = config::shared::ServerSDK; using Defaults = config::shared::Defaults; using AppInfoBuilder = config::shared::builders::AppInfoBuilder; using EndpointsBuilder = config::shared::builders::EndpointsBuilder; -using ConfigBuilder = config::shared::builders::ConfigBuilder; using EventsBuilder = config::shared::builders::EventsBuilder; using HttpPropertiesBuilder = config::shared::builders::HttpPropertiesBuilder; using DataSourceBuilder = config::shared::builders::DataSourceBuilder; using LoggingBuilder = config::shared::builders::LoggingBuilder; -using PersistenceBuilder = config::shared::builders::PersistenceBuilder; - -using Config = config::Config; } // namespace launchdarkly::server_side diff --git a/libs/common/src/config/config_builder.cpp b/libs/common/src/config/config_builder.cpp index 5e58e3b89..0c306cb1f 100644 --- a/libs/common/src/config/config_builder.cpp +++ b/libs/common/src/config/config_builder.cpp @@ -91,6 +91,5 @@ ConfigBuilder::Build() const { } template class ConfigBuilder; -template class ConfigBuilder; } // namespace launchdarkly::config::shared::builders diff --git a/libs/common/tests/config_builder_test.cpp b/libs/common/tests/config_builder_test.cpp index 3f4adeafb..19cf7129d 100644 --- a/libs/common/tests/config_builder_test.cpp +++ b/libs/common/tests/config_builder_test.cpp @@ -1,7 +1,6 @@ #include #include -#include #include class ConfigBuilderTest @@ -20,14 +19,6 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_ClientConfig) { ASSERT_EQ(cfg->SdkKey(), "sdk-123"); } -TEST_F(ConfigBuilderTest, DefaultConstruction_ServerConfig) { - using namespace launchdarkly::server_side; - ConfigBuilder builder("sdk-123"); - auto cfg = builder.Build(); - ASSERT_TRUE(cfg); - ASSERT_EQ(cfg->SdkKey(), "sdk-123"); -} - TEST_F(ConfigBuilderTest, DefaultConstruction_UsesDefaultEndpointsIfNotSupplied) { using namespace launchdarkly::client_side; @@ -78,37 +69,6 @@ TEST_F(ConfigBuilderTest, .initial_reconnect_delay); } -TEST_F(ConfigBuilderTest, - DefaultConstruction_ServerConfig_UsesDefaulDataSourceConfig) { - using namespace launchdarkly::server_side; - ConfigBuilder builder("sdk-123"); - auto cfg = builder.Build(); - - // Should be streaming with a 1 second initial retry; - EXPECT_EQ(std::chrono::milliseconds{1000}, - std::get>( - cfg->DataSourceConfig().method) - .initial_reconnect_delay); -} - -TEST_F(ConfigBuilderTest, ServerConfig_CanSetDataSource) { - using namespace launchdarkly::server_side; - ConfigBuilder builder("sdk-123"); - - builder.DataSource().Method( - ConfigBuilder::DataSourceBuilder::Streaming().InitialReconnectDelay( - std::chrono::milliseconds{5000})); - - auto cfg = builder.Build(); - - EXPECT_EQ(std::chrono::milliseconds{5000}, - std::get>( - cfg->DataSourceConfig().method) - .initial_reconnect_delay); -} - TEST_F(ConfigBuilderTest, ClientConfig_CanSetDataSource) { using namespace launchdarkly::client_side; ConfigBuilder builder("sdk-123"); @@ -141,14 +101,6 @@ TEST_F(ConfigBuilderTest, ASSERT_EQ(cfg->HttpProperties(), Defaults::HttpProperties()); } -TEST_F(ConfigBuilderTest, - DefaultConstruction_ServerConfig_UsesDefaultHttpProperties) { - using namespace launchdarkly::server_side; - ConfigBuilder builder("sdk-123"); - auto cfg = builder.Build(); - ASSERT_EQ(cfg->HttpProperties(), Defaults::HttpProperties()); -} - TEST_F(ConfigBuilderTest, DefaultConstruction_CanSetHttpProperties) { using namespace launchdarkly::client_side; ConfigBuilder builder("sdk-123"); diff --git a/libs/common/tests/data_source_builder_test.cpp b/libs/common/tests/data_source_builder_test.cpp index 1c5f896c9..795a6f937 100644 --- a/libs/common/tests/data_source_builder_test.cpp +++ b/libs/common/tests/data_source_builder_test.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include diff --git a/libs/common/tests/service_endpoints_test.cpp b/libs/common/tests/service_endpoints_test.cpp index 080070c0a..bd593352e 100644 --- a/libs/common/tests/service_endpoints_test.cpp +++ b/libs/common/tests/service_endpoints_test.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include class ServiceEndpointTest : public testing::Test {}; diff --git a/libs/server-sdk/include/launchdarkly/server_side/client.hpp b/libs/server-sdk/include/launchdarkly/server_side/client.hpp index e7f1b8056..2dfcaaf2e 100644 --- a/libs/server-sdk/include/launchdarkly/server_side/client.hpp +++ b/libs/server-sdk/include/launchdarkly/server_side/client.hpp @@ -1,8 +1,8 @@ #pragma once -#include #include #include +#include #include #include diff --git a/libs/server-sdk/include/launchdarkly/server_side/config/config.hpp b/libs/server-sdk/include/launchdarkly/server_side/config/config.hpp new file mode 100644 index 000000000..70f19b19a --- /dev/null +++ b/libs/server-sdk/include/launchdarkly/server_side/config/config.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace launchdarkly::server_side { + +using SDK = config::shared::ServerSDK; + +struct Config { + public: + Config(std::string sdk_key, + bool offline, + config::shared::built::Logging logging, + config::shared::built::ServiceEndpoints endpoints, + config::shared::built::Events events, + std::optional application_tag, + config::shared::built::DataSourceConfig data_source_config, + config::shared::built::HttpProperties http_properties); + + [[nodiscard]] std::string const& SdkKey() const; + + [[nodiscard]] config::shared::built::ServiceEndpoints const& + ServiceEndpoints() const; + + [[nodiscard]] config::shared::built::Events const& Events() const; + + [[nodiscard]] std::optional const& ApplicationTag() const; + + config::shared::built::DataSourceConfig const& DataSourceConfig() + const; + + [[nodiscard]] config::shared::built::HttpProperties const& HttpProperties() + const; + + [[nodiscard]] bool Offline() const; + + [[nodiscard]] config::shared::built::Logging const& Logging() const; + + private: + std::string sdk_key_; + bool offline_; + config::shared::built::Logging logging_; + config::shared::built::ServiceEndpoints service_endpoints_; + std::optional application_tag_; + config::shared::built::Events events_; + config::shared::built::DataSourceConfig data_source_config_; + config::shared::built::HttpProperties http_properties_; +}; +} // namespace launchdarkly::server_side diff --git a/libs/server-sdk/include/launchdarkly/server_side/config/config_builder.hpp b/libs/server-sdk/include/launchdarkly/server_side/config/config_builder.hpp new file mode 100644 index 000000000..8ed1d66d2 --- /dev/null +++ b/libs/server-sdk/include/launchdarkly/server_side/config/config_builder.hpp @@ -0,0 +1,93 @@ +#pragma once +#include +#include +#include "launchdarkly/config/shared/builders/app_info_builder.hpp" +#include "launchdarkly/config/shared/builders/data_source_builder.hpp" +#include "launchdarkly/config/shared/builders/http_properties_builder.hpp" +#include "launchdarkly/config/shared/builders/logging_builder.hpp" +#include "launchdarkly/config/shared/defaults.hpp" + +namespace launchdarkly::server_side { + +class ConfigBuilder { + public: + using Result = Config; + /** + * A minimal configuration consists of a LaunchDarkly SDK Key. + * @param sdk_key SDK Key. + */ + explicit ConfigBuilder(std::string sdk_key); + + /** + * To customize the ServiceEndpoints the SDK uses for streaming, + * polling, and events, pass in an EndpointsBuilder. + * @param builder An EndpointsBuilder. + * @return Reference to an EndpointsBuilder. + */ + EndpointsBuilder& ServiceEndpoints(); + + /** + * To include metadata about the application that is utilizing the SDK, + * pass in an AppInfoBuilder. + * @param builder An AppInfoBuilder. + * @return Reference to an AppInfoBuilder. + */ + AppInfoBuilder& AppInfo(); + + /** + * Enables or disables "Offline" mode. True means + * Offline mode is enabled. + * @param offline True if the SDK should operate in Offline mode. + * @return Reference to this builder. + */ + ConfigBuilder& Offline(bool offline); + + /** + * To tune settings related to event generation and delivery, pass an + * EventsBuilder. + * @param builder An EventsBuilder. + * @return Reference to an EventsBuilder. + */ + EventsBuilder& Events(); + + /** + * Sets the configuration of the component that receives feature flag data + * from LaunchDarkly. + * @param builder A DataSourceConfig builder. + * @return Reference to a DataSourceBuilder. + */ + DataSourceBuilder& DataSource(); + + /** + * Sets the SDK's networking configuration, using an HttpPropertiesBuilder. + * The builder has methods for setting individual HTTP-related properties. + * @param builder A HttpPropertiesBuilder builder. + * @return Reference to an HttpPropertiesBuilder. + */ + HttpPropertiesBuilder& HttpProperties(); + + /** + * Sets the logging configuration for the SDK. + * @param builder A Logging builder. + * @return Reference to a LoggingBuilder. + */ + LoggingBuilder& Logging(); + + /** + * Builds a Configuration, suitable for passing into an instance of Client. + * @return + */ + tl::expected Build() const; + + private: + std::string sdk_key_; + std::optional offline_; + + EndpointsBuilder service_endpoints_builder_; + AppInfoBuilder app_info_builder_; + EventsBuilder events_builder_; + DataSourceBuilder data_source_builder_; + HttpPropertiesBuilder http_properties_builder_; + LoggingBuilder logging_config_builder_; +}; +} // namespace launchdarkly::server_side diff --git a/libs/server-sdk/src/CMakeLists.txt b/libs/server-sdk/src/CMakeLists.txt index 00569be24..5f234d148 100644 --- a/libs/server-sdk/src/CMakeLists.txt +++ b/libs/server-sdk/src/CMakeLists.txt @@ -19,6 +19,8 @@ target_sources(${LIBNAME} boost.cpp client.cpp client_impl.cpp + config/config.cpp + config/config_builder.cpp all_flags_state/all_flags_state.cpp all_flags_state/json_all_flags_state.cpp all_flags_state/all_flags_state_builder.cpp diff --git a/libs/server-sdk/src/bindings/c/builder.cpp b/libs/server-sdk/src/bindings/c/builder.cpp index 461bcf204..fe8037413 100644 --- a/libs/server-sdk/src/bindings/c/builder.cpp +++ b/libs/server-sdk/src/bindings/c/builder.cpp @@ -3,8 +3,8 @@ #include -#include #include +#include using namespace launchdarkly::server_side; diff --git a/libs/server-sdk/src/bindings/c/config.cpp b/libs/server-sdk/src/bindings/c/config.cpp index 72b23bace..27618baa6 100644 --- a/libs/server-sdk/src/bindings/c/config.cpp +++ b/libs/server-sdk/src/bindings/c/config.cpp @@ -2,7 +2,7 @@ // NOLINTBEGIN OCInconsistentNamingInspection #include -#include +#include #define TO_CONFIG(ptr) (reinterpret_cast(ptr)) diff --git a/libs/server-sdk/src/client_impl.cpp b/libs/server-sdk/src/client_impl.cpp index 5dfc5b56b..0845f4f6b 100644 --- a/libs/server-sdk/src/client_impl.cpp +++ b/libs/server-sdk/src/client_impl.cpp @@ -4,9 +4,8 @@ #include #include -#include "client_impl.hpp" - #include "all_flags_state/all_flags_state_builder.hpp" +#include "client_impl.hpp" #include "data_sources/null_data_source.hpp" #include "data_sources/polling_data_source.hpp" #include "data_sources/streaming_data_source.hpp" @@ -17,6 +16,7 @@ #include #include #include +#include namespace launchdarkly::server_side { diff --git a/libs/server-sdk/src/config/config.cpp b/libs/server-sdk/src/config/config.cpp new file mode 100644 index 000000000..0427d39b3 --- /dev/null +++ b/libs/server-sdk/src/config/config.cpp @@ -0,0 +1,56 @@ +#include + +namespace launchdarkly::server_side { + +Config::Config(std::string sdk_key, + bool offline, + config::shared::built::Logging logging, + config::shared::built::ServiceEndpoints service_endpoints, + config::shared::built::Events events, + std::optional application_tag, + config::shared::built::DataSourceConfig data_source_config, + config::shared::built::HttpProperties http_properties) + : sdk_key_(std::move(sdk_key)), + logging_(std::move(logging)), + offline_(offline), + service_endpoints_(std::move(service_endpoints)), + events_(std::move(events)), + application_tag_(std::move(application_tag)), + data_source_config_(std::move(data_source_config)), + http_properties_(std::move(http_properties)) {} + +std::string const& Config::SdkKey() const { + return sdk_key_; +} + +config::shared::built::ServiceEndpoints const& Config::ServiceEndpoints() + const { + return service_endpoints_; +} + +config::shared::built::Events const& Config::Events() const { + return events_; +} + +std::optional const& Config::ApplicationTag() const { + return application_tag_; +} + +config::shared::built::DataSourceConfig const& +Config::DataSourceConfig() const { + return data_source_config_; +} + +config::shared::built::HttpProperties const& Config::HttpProperties() const { + return http_properties_; +} + +bool Config::Offline() const { + return offline_; +} + +config::shared::built::Logging const& Config::Logging() const { + return logging_; +} + +} // namespace launchdarkly::server_side diff --git a/libs/server-sdk/src/config/config_builder.cpp b/libs/server-sdk/src/config/config_builder.cpp new file mode 100644 index 000000000..ad6603b94 --- /dev/null +++ b/libs/server-sdk/src/config/config_builder.cpp @@ -0,0 +1,72 @@ +#include +#include "launchdarkly/config/shared/defaults.hpp" + +namespace launchdarkly::server_side { + +ConfigBuilder::ConfigBuilder(std::string sdk_key) + : sdk_key_(std::move(sdk_key)) {} + +EndpointsBuilder& ConfigBuilder::ServiceEndpoints() { + return service_endpoints_builder_; +} + +EventsBuilder& ConfigBuilder::Events() { + return events_builder_; +} + +AppInfoBuilder& ConfigBuilder::AppInfo() { + return app_info_builder_; +} + +ConfigBuilder& ConfigBuilder::Offline(bool offline) { + offline_ = offline; + return *this; +} + +DataSourceBuilder& ConfigBuilder::DataSource() { + return data_source_builder_; +} + +HttpPropertiesBuilder& ConfigBuilder::HttpProperties() { + return http_properties_builder_; +} + +LoggingBuilder& ConfigBuilder::Logging() { + return logging_config_builder_; +} + +tl::expected ConfigBuilder::Build() const { + auto sdk_key = sdk_key_; + if (sdk_key.empty()) { + return tl::make_unexpected(Error::kConfig_SDKKey_Empty); + } + auto offline = offline_.value_or(Defaults::Offline()); + auto endpoints_config = service_endpoints_builder_.Build(); + if (!endpoints_config) { + return tl::make_unexpected(endpoints_config.error()); + } + auto events_config = events_builder_.Build(); + if (!events_config) { + return tl::make_unexpected(events_config.error()); + } + + std::optional app_tag = app_info_builder_.Build(); + + auto data_source_config = data_source_builder_.Build(); + + auto http_properties = http_properties_builder_.Build(); + + auto logging = logging_config_builder_.Build(); + + return {tl::in_place, + sdk_key, + offline, + logging, + *endpoints_config, + *events_config, + app_tag, + std::move(data_source_config), + std::move(http_properties)}; +} + +} // namespace launchdarkly::server_side diff --git a/libs/server-sdk/tests/client_test.cpp b/libs/server-sdk/tests/client_test.cpp index 73d0c0dd7..28d4b82da 100644 --- a/libs/server-sdk/tests/client_test.cpp +++ b/libs/server-sdk/tests/client_test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include using namespace launchdarkly; @@ -23,7 +24,7 @@ TEST_F(ClientTest, ClientConstructedWithMinimalConfigAndContextT) { } TEST_F(ClientTest, BoolVariationDefaultPassesThrough) { - const std::string flag = "extra-cat-food"; + std::string const flag = "extra-cat-food"; std::vector values = {true, false}; for (auto const& v : values) { ASSERT_EQ(client_.BoolVariation(context_, flag, v), v); @@ -32,7 +33,7 @@ TEST_F(ClientTest, BoolVariationDefaultPassesThrough) { } TEST_F(ClientTest, StringVariationDefaultPassesThrough) { - const std::string flag = "treat"; + std::string const flag = "treat"; std::vector values = {"chicken", "fish", "cat-grass"}; for (auto const& v : values) { ASSERT_EQ(client_.StringVariation(context_, flag, v), v); @@ -41,7 +42,7 @@ TEST_F(ClientTest, StringVariationDefaultPassesThrough) { } TEST_F(ClientTest, IntVariationDefaultPassesThrough) { - const std::string flag = "weight"; + std::string const flag = "weight"; std::vector values = {0, 12, 13, 24, 1000}; for (auto const& v : values) { ASSERT_EQ(client_.IntVariation(context_, flag, v), v); @@ -50,7 +51,7 @@ TEST_F(ClientTest, IntVariationDefaultPassesThrough) { } TEST_F(ClientTest, DoubleVariationDefaultPassesThrough) { - const std::string flag = "weight"; + std::string const flag = "weight"; std::vector values = {0.0, 12.0, 13.0, 24.0, 1000.0}; for (auto const& v : values) { ASSERT_EQ(client_.DoubleVariation(context_, flag, v), v); @@ -59,7 +60,7 @@ TEST_F(ClientTest, DoubleVariationDefaultPassesThrough) { } TEST_F(ClientTest, JsonVariationDefaultPassesThrough) { - const std::string flag = "assorted-values"; + std::string const flag = "assorted-values"; std::vector values = { Value({"running", "jumping"}), Value(3), Value(1.0), Value(true), Value(std::map{{"weight", 20}})}; diff --git a/libs/server-sdk/tests/config_builder_test.cpp b/libs/server-sdk/tests/config_builder_test.cpp new file mode 100644 index 000000000..c88990163 --- /dev/null +++ b/libs/server-sdk/tests/config_builder_test.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include + +using namespace launchdarkly; +using namespace launchdarkly::server_side; + +class ConfigBuilderTest + : public ::testing:: + Test { // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + protected: + launchdarkly::Logger logger; + ConfigBuilderTest() : logger(launchdarkly::logging::NullLogger()) {} +}; + +TEST_F(ConfigBuilderTest, DefaultConstruction_ServerConfig) { + using namespace launchdarkly::server_side; + ConfigBuilder builder("sdk-123"); + auto cfg = builder.Build(); + ASSERT_TRUE(cfg); + ASSERT_EQ(cfg->SdkKey(), "sdk-123"); +} + +TEST_F(ConfigBuilderTest, + DefaultConstruction_ServerConfig_UsesDefaulDataSourceConfig) { + using namespace launchdarkly::server_side; + ConfigBuilder builder("sdk-123"); + auto cfg = builder.Build(); + + // Should be streaming with a 1 second initial retry; + EXPECT_EQ(std::chrono::milliseconds{1000}, + std::get>( + cfg->DataSourceConfig().method) + .initial_reconnect_delay); +} + +TEST_F(ConfigBuilderTest, ServerConfig_CanSetDataSource) { + using namespace launchdarkly::server_side; + ConfigBuilder builder("sdk-123"); + + builder.DataSource().Method( + DataSourceBuilder::Streaming().InitialReconnectDelay( + std::chrono::milliseconds{5000})); + + auto cfg = builder.Build(); + + EXPECT_EQ(std::chrono::milliseconds{5000}, + std::get>( + cfg->DataSourceConfig().method) + .initial_reconnect_delay); +} + +TEST_F(ConfigBuilderTest, + DefaultConstruction_ServerConfig_UsesDefaultHttpProperties) { + using namespace launchdarkly::server_side; + ConfigBuilder builder("sdk-123"); + auto cfg = builder.Build(); + ASSERT_EQ(cfg->HttpProperties(), Defaults::HttpProperties()); +} From f1e93cd9087ed723756438997700e155970ba771 Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Thu, 16 Nov 2023 10:56:20 -0800 Subject: [PATCH 4/4] chore: remove 'release-as' from server sdk (#288) --- release-please-config.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/release-please-config.json b/release-please-config.json index 1a5d6687b..b9b9fd9b5 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -19,8 +19,7 @@ "CMakeLists.txt" ], "prerelease": true, - "bump-minor-pre-major": true, - "release-as": "0.1.0" + "bump-minor-pre-major": true }, "libs/server-sent-events": { "initial-version": "0.1.0"