From 637be53bb1321ddd488d8882e9d1bc7c89442555 Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 11:08:43 -0700 Subject: [PATCH 1/9] Add declaration of StreamingBuilder::Filter to common lib --- .../shared/builders/data_source_builder.hpp | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp index e3275e0be..52d0face3 100644 --- a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp +++ b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp @@ -12,7 +12,6 @@ #include namespace launchdarkly::config::shared::builders { - /** * Used to construct a DataSourceConfiguration for the specified SDK type. * @tparam SDK ClientSDK or ServerSDK. @@ -25,7 +24,7 @@ class DataSourceBuilder; */ template class StreamingBuilder { - public: +public: StreamingBuilder(); /** @@ -43,13 +42,32 @@ class StreamingBuilder { StreamingBuilder& InitialReconnectDelay( std::chrono::milliseconds initial_reconnect_delay); + /** + * Filter sets the filter key for the streaming connection. + * + * By default, the SDK is able to evaluate all flags in an environment. + * + * If this is undesirable - for example, because the environment contains + * thousands of flags, but this application only needs to evaluate + * a smaller, known subset - then a filter may be setup in LaunchDarkly, + * and the filter's key specified here. + * + * Evaluations for flags that aren't part of the filtered environment will + * return default values. + * @param filter_key The filter key. If the key is malformed or nonexistent, + * then a full LaunchDarkly environment will be fetched. In the case of a + * malformed key, the SDK will additionally log a runtime error. + * @return Reference to this builder. + */ + StreamingBuilder& Filter(std::string filter_key); + /** * Build the streaming config. Used internal to the SDK. * @return The built config. */ [[nodiscard]] built::StreamingConfig Build() const; - private: +private: built::StreamingConfig config_; }; @@ -58,7 +76,7 @@ class StreamingBuilder { */ template class PollingBuilder { - public: +public: PollingBuilder(); /** @@ -74,13 +92,13 @@ class PollingBuilder { */ [[nodiscard]] built::PollingConfig Build() const; - private: +private: built::PollingConfig config_; }; template <> class DataSourceBuilder { - public: +public: using Streaming = StreamingBuilder; using Polling = PollingBuilder; @@ -145,10 +163,9 @@ class DataSourceBuilder { */ [[nodiscard]] built::DataSourceConfig Build() const; - private: +private: std::variant method_; bool with_reasons_; bool use_report_; }; - -} // namespace launchdarkly::config::shared::builders +} // namespace launchdarkly::config::shared::builders From c54c8fedc9a6ee01e3e9c78bb9eb4cfa36070c78 Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 11:10:01 -0700 Subject: [PATCH 2/9] Add declaration to PollingBuilder --- .../shared/builders/data_source_builder.hpp | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp index 52d0face3..a0cf3fa46 100644 --- a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp +++ b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp @@ -43,7 +43,7 @@ class StreamingBuilder { std::chrono::milliseconds initial_reconnect_delay); /** - * Filter sets the filter key for the streaming connection. + * Filter sets the filter key for the streaming connection. * * By default, the SDK is able to evaluate all flags in an environment. * @@ -86,6 +86,26 @@ class PollingBuilder { */ PollingBuilder& PollInterval(std::chrono::seconds poll_interval); + /** + * Filter sets the filter key for the polling connection. + * + * By default, the SDK is able to evaluate all flags in an environment. + * + * If this is undesirable - for example, because the environment contains + * thousands of flags, but this application only needs to evaluate + * a smaller, known subset - then a filter may be setup in LaunchDarkly, + * and the filter's key specified here. + * + * Evaluations for flags that aren't part of the filtered environment will + * return default values. + * + * @param filter_key The filter key. If the key is malformed or nonexistent, + * then a full LaunchDarkly environment will be fetched. In the case of a + * malformed key, the SDK will additionally log a runtime error. + * @return Reference to this builder. + */ + PollingBuilder& Filter(std::string filter_key); + /** * Build the polling config. Used internal to the SDK. * @return The built config. From f9c6d41ef48c4f0a2485a53f3b9c7917fbfe8c97 Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 11:11:35 -0700 Subject: [PATCH 3/9] add impls: --- .../common/src/config/data_source_builder.cpp | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/libs/common/src/config/data_source_builder.cpp b/libs/common/src/config/data_source_builder.cpp index e19a9417e..e3f20c1b2 100644 --- a/libs/common/src/config/data_source_builder.cpp +++ b/libs/common/src/config/data_source_builder.cpp @@ -1,15 +1,15 @@ #include namespace launchdarkly::config::shared::builders { - template -struct MethodVisitor {}; +struct MethodVisitor { +}; template <> struct MethodVisitor { using SDK = ClientSDK; using Result = - std::variant, built::PollingConfig>; + std::variant, built::PollingConfig>; Result operator()(StreamingBuilder const& streaming) const { return streaming.Build(); @@ -22,7 +22,8 @@ struct MethodVisitor { template StreamingBuilder::StreamingBuilder() - : config_(Defaults::StreamingConfig()) {} + : config_(Defaults::StreamingConfig()) { +} template StreamingBuilder& StreamingBuilder::InitialReconnectDelay( @@ -31,6 +32,12 @@ StreamingBuilder& StreamingBuilder::InitialReconnectDelay( return *this; } +template +StreamingBuilder& StreamingBuilder::Filter(std::string filter_key) { + config_.filter_key = std::move(filter_key); + return *this; +} + template built::StreamingConfig StreamingBuilder::Build() const { return config_; @@ -38,7 +45,8 @@ built::StreamingConfig StreamingBuilder::Build() const { template PollingBuilder::PollingBuilder() - : config_(Defaults::PollingConfig()) {} + : config_(Defaults::PollingConfig()) { +} template PollingBuilder& PollingBuilder::PollInterval( @@ -47,13 +55,20 @@ PollingBuilder& PollingBuilder::PollInterval( return *this; } +template +PollingBuilder& PollingBuilder::Filter(std::string filter_key) { + config_.filter_key = std::move(filter_key); + return *this; +} + template built::PollingConfig PollingBuilder::Build() const { return config_; } DataSourceBuilder::DataSourceBuilder() - : with_reasons_(false), use_report_(false), method_(Streaming()) {} + : with_reasons_(false), use_report_(false), method_(Streaming()) { +} DataSourceBuilder& DataSourceBuilder::WithReasons( bool value) { @@ -89,5 +104,4 @@ template class PollingBuilder; template class StreamingBuilder; template class StreamingBuilder; - -} // namespace launchdarkly::config::shared::builders +} // namespace launchdarkly::config::shared::builders From 0292ed4f615df440402069c862143bd9b62ad5d9 Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 11:54:49 -0700 Subject: [PATCH 4/9] c bindings --- .../shared/builders/data_source_builder.hpp | 34 +++++-- .../shared/built/data_source_config.hpp | 11 ++- .../common/src/config/data_source_builder.cpp | 12 --- .../common/tests/data_source_builder_test.cpp | 28 +++--- .../server_side/bindings/c/config/builder.h | 89 ++++++++++++++----- libs/server-sdk/src/bindings/c/builder.cpp | 24 ++++- libs/server-sdk/tests/config_builder_test.cpp | 71 +++++++++++++-- .../tests/server_c_bindings_test.cpp | 47 ++++++++-- 8 files changed, 240 insertions(+), 76 deletions(-) diff --git a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp index a0cf3fa46..08e262083 100644 --- a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp +++ b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp @@ -4,12 +4,9 @@ #include #include -#include - -#include - #include #include +#include namespace launchdarkly::config::shared::builders { /** @@ -19,6 +16,14 @@ namespace launchdarkly::config::shared::builders { template class DataSourceBuilder; +template +struct is_server_sdk : std::false_type { +}; + +template <> +struct is_server_sdk : std::true_type { +}; + /** * Builds a configuration for a streaming data source. */ @@ -43,7 +48,7 @@ class StreamingBuilder { std::chrono::milliseconds initial_reconnect_delay); /** - * Filter sets the filter key for the streaming connection. + * Sets the filter key for the streaming connection. * * By default, the SDK is able to evaluate all flags in an environment. * @@ -59,7 +64,12 @@ class StreamingBuilder { * malformed key, the SDK will additionally log a runtime error. * @return Reference to this builder. */ - StreamingBuilder& Filter(std::string filter_key); + template + std::enable_if_t::value, StreamingBuilder&> Filter( + std::string filter_key) { + config_.filter_key = std::move(filter_key); + return *this; + } /** * Build the streaming config. Used internal to the SDK. @@ -87,7 +97,7 @@ class PollingBuilder { PollingBuilder& PollInterval(std::chrono::seconds poll_interval); /** - * Filter sets the filter key for the polling connection. + * Sets the filter key for the polling connection. * * By default, the SDK is able to evaluate all flags in an environment. * @@ -104,7 +114,12 @@ class PollingBuilder { * malformed key, the SDK will additionally log a runtime error. * @return Reference to this builder. */ - PollingBuilder& Filter(std::string filter_key); + template + std::enable_if_t::value, PollingBuilder&> Filter( + std::string filter_key) { + config_.filter_key = std::move(filter_key); + return *this; + } /** * Build the polling config. Used internal to the SDK. @@ -116,6 +131,7 @@ class PollingBuilder { built::PollingConfig config_; }; + template <> class DataSourceBuilder { public: @@ -138,7 +154,7 @@ class DataSourceBuilder { DataSourceBuilder& WithReasons(bool value); /** - * Whether or not to use the REPORT verb to fetch flag settings. + * Whether to use the REPORT verb to fetch flag settings. * * If this is true, flag settings will be fetched with a REPORT request * including a JSON entity body with the context object. diff --git a/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp b/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp index 8b6a13055..45742e3c4 100644 --- a/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp +++ b/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp @@ -8,7 +8,6 @@ #include namespace launchdarkly::config::shared::built { - template struct StreamingConfig; @@ -22,12 +21,14 @@ template <> struct StreamingConfig { std::chrono::milliseconds initial_reconnect_delay; std::string streaming_path; + std::optional filter_key; }; inline bool operator==(StreamingConfig const& lhs, StreamingConfig const& rhs) { return lhs.initial_reconnect_delay == rhs.initial_reconnect_delay && - lhs.streaming_path == rhs.streaming_path; + lhs.streaming_path == rhs.streaming_path && lhs.filter_key == rhs. + filter_key; } template @@ -46,6 +47,7 @@ struct PollingConfig { std::chrono::seconds poll_interval; std::string polling_get_path; std::chrono::seconds min_polling_interval; + std::optional filter_key; }; template @@ -60,5 +62,6 @@ struct DataSourceConfig { }; template <> -struct DataSourceConfig {}; -} // namespace launchdarkly::config::shared::built +struct DataSourceConfig { +}; +} // namespace launchdarkly::config::shared::built diff --git a/libs/common/src/config/data_source_builder.cpp b/libs/common/src/config/data_source_builder.cpp index e3f20c1b2..f5252c061 100644 --- a/libs/common/src/config/data_source_builder.cpp +++ b/libs/common/src/config/data_source_builder.cpp @@ -32,12 +32,6 @@ StreamingBuilder& StreamingBuilder::InitialReconnectDelay( return *this; } -template -StreamingBuilder& StreamingBuilder::Filter(std::string filter_key) { - config_.filter_key = std::move(filter_key); - return *this; -} - template built::StreamingConfig StreamingBuilder::Build() const { return config_; @@ -55,12 +49,6 @@ PollingBuilder& PollingBuilder::PollInterval( return *this; } -template -PollingBuilder& PollingBuilder::Filter(std::string filter_key) { - config_.filter_key = std::move(filter_key); - return *this; -} - template built::PollingConfig PollingBuilder::Build() const { return config_; diff --git a/libs/common/tests/data_source_builder_test.cpp b/libs/common/tests/data_source_builder_test.cpp index 8f16edc94..6e66d954d 100644 --- a/libs/common/tests/data_source_builder_test.cpp +++ b/libs/common/tests/data_source_builder_test.cpp @@ -11,37 +11,37 @@ using namespace launchdarkly; TEST(DataSourceBuilderTests, CanCreateStreamingClientConfig) { auto client_config = client_side::DataSourceBuilder() - .WithReasons(true) - .UseReport(true) - .Method(client_side::DataSourceBuilder::Streaming() - .InitialReconnectDelay(std::chrono::milliseconds{1500})) - .Build(); + .WithReasons(true) + .UseReport(true) + .Method(client_side::DataSourceBuilder::Streaming() + .InitialReconnectDelay(std::chrono::milliseconds{1500})) + .Build(); EXPECT_TRUE(client_config.use_report); EXPECT_TRUE(client_config.with_reasons); EXPECT_EQ( std::chrono::milliseconds{1500}, std::get< - config::shared::built::StreamingConfig>( + config::shared::built::StreamingConfig>( client_config.method) - .initial_reconnect_delay); + .initial_reconnect_delay); } TEST(DataSourceBuilderTests, CanCreatePollingClientConfig) { auto client_config = client_side::DataSourceBuilder() - .WithReasons(false) - .UseReport(false) - .Method(client_side::DataSourceBuilder::Polling().PollInterval( - std::chrono::seconds{88000})) - .Build(); + .WithReasons(false) + .UseReport(false) + .Method(client_side::DataSourceBuilder::Polling().PollInterval( + std::chrono::seconds{88000})) + .Build(); EXPECT_FALSE(client_config.use_report); EXPECT_FALSE(client_config.with_reasons); EXPECT_EQ( std::chrono::seconds{88000}, std::get< - config::shared::built::PollingConfig>( + config::shared::built::PollingConfig>( client_config.method) - .poll_interval); + .poll_interval); } diff --git a/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h b/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h index 11ecfb786..6f45c963c 100644 --- a/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h +++ b/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h @@ -14,16 +14,17 @@ #include #ifdef __cplusplus -extern "C" { // only need to export C interface if +extern "C" { +// only need to export C interface if // used by C++ source code #endif typedef struct _LDServerConfigBuilder* LDServerConfigBuilder; typedef struct _LDServerDataSourceStreamBuilder* - LDServerDataSourceStreamBuilder; +LDServerDataSourceStreamBuilder; typedef struct _LDServerDataSourcePollBuilder* LDServerDataSourcePollBuilder; typedef struct _LDServerHttpPropertiesTlsBuilder* - LDServerHttpPropertiesTlsBuilder; +LDServerHttpPropertiesTlsBuilder; /** * Constructs a client-side config builder. @@ -63,8 +64,8 @@ LDServerConfigBuilder_ServiceEndpoints_EventsBaseURL(LDServerConfigBuilder b, */ LD_EXPORT(void) LDServerConfigBuilder_ServiceEndpoints_RelayProxyBaseURL( - LDServerConfigBuilder b, - char const* url); + LDServerConfigBuilder b, + char const* url); /** * Sets an identifier for the application. @@ -201,8 +202,8 @@ LDServerConfigBuilder_Events_PrivateAttribute(LDServerConfigBuilder b, */ LD_EXPORT(void) LDServerConfigBuilder_DataSystem_BackgroundSync_Streaming( - LDServerConfigBuilder b, - LDServerDataSourceStreamBuilder stream_builder); + LDServerConfigBuilder b, + LDServerDataSourceStreamBuilder stream_builder); /** * Configures the Background Sync data system with a Polling synchronizer. @@ -219,8 +220,8 @@ LDServerConfigBuilder_DataSystem_BackgroundSync_Streaming( */ LD_EXPORT(void) LDServerConfigBuilder_DataSystem_BackgroundSync_Polling( - LDServerConfigBuilder b, - LDServerDataSourcePollBuilder poll_builder); + LDServerConfigBuilder b, + LDServerDataSourcePollBuilder poll_builder); /** * Configures the Lazy Load data system. This method is mutually exclusive with @@ -235,8 +236,8 @@ LDServerConfigBuilder_DataSystem_BackgroundSync_Polling( */ LD_EXPORT(void) LDServerConfigBuilder_DataSystem_LazyLoad( - LDServerConfigBuilder b, - LDServerLazyLoadBuilder lazy_load_builder); + LDServerConfigBuilder b, + LDServerLazyLoadBuilder lazy_load_builder); /** * Specify if the SDK's data system should be enabled or not. @@ -274,8 +275,31 @@ LDServerDataSourceStreamBuilder_New(); */ LD_EXPORT(void) LDServerDataSourceStreamBuilder_InitialReconnectDelayMs( - LDServerDataSourceStreamBuilder b, - unsigned int milliseconds); + LDServerDataSourceStreamBuilder b, + unsigned int milliseconds); + +/** +* Sets the filter key for the streaming connection. +* +* By default, the SDK is able to evaluate all flags in an environment. +* +* If this is undesirable - for example, because the environment contains +* thousands of flags, but this application only needs to evaluate +* a smaller, known subset - then a filter may be setup in LaunchDarkly, +* and the filter's key specified here. +* +* Evaluations for flags that aren't part of the filtered environment will +* return default values. +* +* @param b Streaming method builder. Must not be NULL. + * @param filter_key The filter key. Must not be NULL. If the key is malformed or + * nonexistent, then a full LaunchDarkly environment will be fetched. In the case + * of a malformed key, the SDK will additionally log a runtime error. +*/ +LD_EXPORT(void) +LDServerDataSourceStreamBuilder_Filter( + LDServerDataSourceStreamBuilder b, + char const* filter_key); /** * Frees a Streaming method builder. Do not call if the builder was consumed by @@ -307,6 +331,29 @@ LD_EXPORT(void) LDServerDataSourcePollBuilder_IntervalS(LDServerDataSourcePollBuilder b, unsigned int seconds); +/** +* Sets the filter key for the polling connection. +* +* By default, the SDK is able to evaluate all flags in an environment. +* +* If this is undesirable - for example, because the environment contains +* thousands of flags, but this application only needs to evaluate +* a smaller, known subset - then a filter may be setup in LaunchDarkly, +* and the filter's key specified here. +* +* Evaluations for flags that aren't part of the filtered environment will +* return default values. +* +* @param b Polling method builder. Must not be NULL. +* @param filter_key The filter key. Must not be NULL. If the key is malformed or +* nonexistent, then a full LaunchDarkly environment will be fetched. In the case +* of a malformed key, the SDK will additionally log a runtime error. +*/ +LD_EXPORT(void) +LDServerDataSourcePollBuilder_Filter( + LDServerDataSourcePollBuilder b, + char const* filter_key); + /** * Frees a Polling method builder. Do not call if the builder was consumed by * the config builder. @@ -336,8 +383,8 @@ LDServerConfigBuilder_HttpProperties_WrapperName(LDServerConfigBuilder b, */ LD_EXPORT(void) LDServerConfigBuilder_HttpProperties_WrapperVersion( - LDServerConfigBuilder b, - char const* wrapper_version); + LDServerConfigBuilder b, + char const* wrapper_version); /** * Set a custom header value. May be called more than once with additional @@ -359,8 +406,8 @@ LDServerConfigBuilder_HttpProperties_Header(LDServerConfigBuilder b, */ LD_EXPORT(void) LDServerConfigBuilder_HttpProperties_Tls( - LDServerConfigBuilder b, - LDServerHttpPropertiesTlsBuilder tls_builder); + LDServerConfigBuilder b, + LDServerHttpPropertiesTlsBuilder tls_builder); /** * Creates a new TLS options builder for the HttpProperties builder. @@ -394,8 +441,8 @@ LDServerHttpPropertiesTlsBuilder_Free(LDServerHttpPropertiesTlsBuilder b); */ LD_EXPORT(void) LDServerHttpPropertiesTlsBuilder_SkipVerifyPeer( - LDServerHttpPropertiesTlsBuilder b, - bool skip_verify_peer); + LDServerHttpPropertiesTlsBuilder b, + bool skip_verify_peer); /** * Configures TLS peer certificate verification to use a custom @@ -414,8 +461,8 @@ LDServerHttpPropertiesTlsBuilder_SkipVerifyPeer( */ LD_EXPORT(void) LDServerHttpPropertiesTlsBuilder_CustomCAFile( - LDServerHttpPropertiesTlsBuilder b, - char const* custom_ca_file); + LDServerHttpPropertiesTlsBuilder b, + char const* custom_ca_file); /** * Disables the default SDK logging. diff --git a/libs/server-sdk/src/bindings/c/builder.cpp b/libs/server-sdk/src/bindings/c/builder.cpp index 8bdc43b15..90a0f355b 100644 --- a/libs/server-sdk/src/bindings/c/builder.cpp +++ b/libs/server-sdk/src/bindings/c/builder.cpp @@ -76,7 +76,7 @@ LDServerConfigBuilder_Free(LDServerConfigBuilder builder) { LD_EXPORT(void) LDServerConfigBuilder_ServiceEndpoints_PollingBaseURL(LDServerConfigBuilder b, - char const* url) { + char const* url) { LD_ASSERT_NOT_NULL(b); LD_ASSERT_NOT_NULL(url); @@ -85,7 +85,7 @@ LDServerConfigBuilder_ServiceEndpoints_PollingBaseURL(LDServerConfigBuilder b, LD_EXPORT(void) LDServerConfigBuilder_ServiceEndpoints_StreamingBaseURL(LDServerConfigBuilder b, - char const* url) { + char const* url) { LD_ASSERT_NOT_NULL(b); LD_ASSERT_NOT_NULL(url); @@ -94,7 +94,7 @@ LDServerConfigBuilder_ServiceEndpoints_StreamingBaseURL(LDServerConfigBuilder b, LD_EXPORT(void) LDServerConfigBuilder_ServiceEndpoints_EventsBaseURL(LDServerConfigBuilder b, - char const* url) { + char const* url) { LD_ASSERT_NOT_NULL(b); LD_ASSERT_NOT_NULL(url); @@ -246,6 +246,15 @@ LDServerDataSourceStreamBuilder_InitialReconnectDelayMs( std::chrono::milliseconds{milliseconds}); } +LD_EXPORT(void) +LDServerDataSourceStreamBuilder_Filter(LDServerDataSourceStreamBuilder b, + char const* filter_key) { + LD_ASSERT_NOT_NULL(b); + LD_ASSERT_NOT_NULL(filter_key); + + TO_STREAM_BUILDER(b)->Filter(filter_key); +} + LD_EXPORT(void) LDServerDataSourceStreamBuilder_Free(LDServerDataSourceStreamBuilder b) { delete TO_STREAM_BUILDER(b); @@ -263,6 +272,15 @@ LDServerDataSourcePollBuilder_IntervalS(LDServerDataSourcePollBuilder b, TO_POLL_BUILDER(b)->PollInterval(std::chrono::seconds{seconds}); } +LD_EXPORT(void) +LDServerDataSourcePollBuilder_Filter(LDServerDataSourcePollBuilder b, + char const* filter_key) { + LD_ASSERT_NOT_NULL(b); + LD_ASSERT_NOT_NULL(filter_key); + + TO_POLL_BUILDER(b)->Filter(filter_key); +} + LD_EXPORT(void) LDServerDataSourcePollBuilder_Free(LDServerDataSourcePollBuilder b) { delete TO_POLL_BUILDER(b); diff --git a/libs/server-sdk/tests/config_builder_test.cpp b/libs/server-sdk/tests/config_builder_test.cpp index b231ecf5a..10b232dcd 100644 --- a/libs/server-sdk/tests/config_builder_test.cpp +++ b/libs/server-sdk/tests/config_builder_test.cpp @@ -13,10 +13,13 @@ using namespace launchdarkly::server_side::config; class ConfigBuilderTest : public ::testing:: - Test { // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) - protected: + Test { + // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) +protected: Logger logger; - ConfigBuilderTest() : logger(logging::NullLogger()) {} + + ConfigBuilderTest() : logger(logging::NullLogger()) { + } }; TEST_F(ConfigBuilderTest, DefaultConstruction_Succeeds) { @@ -52,13 +55,65 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_StreamingDefaultsAreUsed) { EXPECT_EQ(streaming_config, Defaults::SynchronizerConfig()); } +TEST_F(ConfigBuilderTest, CanSetStreamingPayloadFilterKey) { + ConfigBuilder builder("sdk-123"); + builder.DataSystem().Method( + builders::DataSystemBuilder::BackgroundSync().Synchronizer( + builders::BackgroundSyncBuilder::Streaming().Filter( + "foo"))); + + auto cfg = builder.Build(); + + ASSERT_TRUE(std::holds_alternative( + cfg->DataSystemConfig().system_)); + + auto const bg_sync_config = + std::get(cfg->DataSystemConfig().system_); + + ASSERT_TRUE( + std::holds_alternative( + bg_sync_config.synchronizer_)); + + auto const streaming_config = + std::get( + bg_sync_config.synchronizer_); + + EXPECT_EQ(streaming_config.filter_key, "foo"); +} + +TEST_F(ConfigBuilderTest, CanSetPollingPayloadFilterKey) { + ConfigBuilder builder("sdk-123"); + builder.DataSystem().Method( + builders::DataSystemBuilder::BackgroundSync().Synchronizer( + builders::BackgroundSyncBuilder::Polling().Filter( + "foo"))); + + auto cfg = builder.Build(); + + ASSERT_TRUE(std::holds_alternative( + cfg->DataSystemConfig().system_)); + + auto const bg_sync_config = + std::get(cfg->DataSystemConfig().system_); + + ASSERT_TRUE( + std::holds_alternative( + bg_sync_config.synchronizer_)); + + auto const polling_config = + std::get( + bg_sync_config.synchronizer_); + + EXPECT_EQ(polling_config.filter_key, "foo"); +} + TEST_F(ConfigBuilderTest, DefaultConstruction_HttpPropertyDefaultsAreUsed) { ConfigBuilder builder("sdk-123"); auto cfg = builder.Build(); ASSERT_EQ(cfg->HttpProperties(), ::launchdarkly::config::shared::Defaults< - launchdarkly::config::shared::ServerSDK>::Defaults:: - HttpProperties()); + launchdarkly::config::shared::ServerSDK>::Defaults:: + HttpProperties()); } TEST_F(ConfigBuilderTest, DefaultConstruction_ServiceEndpointDefaultsAreUsed) { @@ -66,8 +121,8 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_ServiceEndpointDefaultsAreUsed) { auto cfg = builder.Build(); ASSERT_EQ(cfg->ServiceEndpoints(), ::launchdarkly::config::shared::Defaults< - launchdarkly::config::shared::ServerSDK>::Defaults:: - ServiceEndpoints()); + launchdarkly::config::shared::ServerSDK>::Defaults:: + ServiceEndpoints()); } TEST_F(ConfigBuilderTest, DefaultConstruction_EventDefaultsAreUsed) { @@ -75,7 +130,7 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_EventDefaultsAreUsed) { auto cfg = builder.Build(); ASSERT_EQ(cfg->Events(), ::launchdarkly::config::shared::Defaults< - launchdarkly::config::shared::ServerSDK>::Defaults::Events()); + launchdarkly::config::shared::ServerSDK>::Defaults::Events()); } TEST_F(ConfigBuilderTest, CanDisableDataSystem) { diff --git a/libs/server-sdk/tests/server_c_bindings_test.cpp b/libs/server-sdk/tests/server_c_bindings_test.cpp index fe470c157..4b7845836 100644 --- a/libs/server-sdk/tests/server_c_bindings_test.cpp +++ b/libs/server-sdk/tests/server_c_bindings_test.cpp @@ -24,7 +24,7 @@ TEST(ClientBindings, MinimalInstantiation) { char const* version = LDServerSDK_Version(); ASSERT_TRUE(version); - ASSERT_STREQ(version, "3.5.3"); // {x-release-please-version} + ASSERT_STREQ(version, "3.5.3"); // {x-release-please-version} LDServerSDK_Free(sdk); } @@ -46,7 +46,7 @@ TEST(ClientBindings, RegisterDataSourceStatusChangeListener) { LDServerSDK sdk = LDServerSDK_New(config); - struct LDServerDataSourceStatusListener listener {}; + struct LDServerDataSourceStatusListener listener{}; LDServerDataSourceStatusListener_Init(&listener); listener.UserData = const_cast("Potato"); @@ -115,7 +115,7 @@ TEST(ClientBindings, ComplexDataSourceStatus) { reinterpret_cast(&status))); EXPECT_EQ(200, LDServerDataSourceStatus_StateSince( - reinterpret_cast(&status))); + reinterpret_cast(&status))); LDDataSourceStatus_ErrorInfo info = LDServerDataSourceStatus_GetLastError( reinterpret_cast(&status)); @@ -188,12 +188,12 @@ TEST(ClientBindings, DoubleVariationPassesThroughDefault) { LDContext context = LDContextBuilder_Build(ctx_builder); std::string const flag = "weight"; - std::vector values = {0.0, 0.0001, 0.5, 1.234, + std::vector values = {0.0, 0.0001, 0.5, 1.234, 12.9, 13.211, 24.0, 1000.0}; for (auto const& v : values) { ASSERT_EQ(LDServerSDK_DoubleVariation(sdk, context, "weight", v), v); ASSERT_EQ(LDServerSDK_DoubleVariationDetail(sdk, context, "weight", v, - LD_DISCARD_DETAIL), + LD_DISCARD_DETAIL), v); } @@ -301,3 +301,40 @@ TEST(ClientBindings, TlsConfigurationSystemCAFile) { LDServerConfig_Free(config); } + + +TEST(ClientBindings, StreamingPayloadFilters) { + LDServerConfigBuilder cfg_builder = LDServerConfigBuilder_New("sdk-123"); + + LDServerDataSourceStreamBuilder stream_builder = + LDServerDataSourceStreamBuilder_New(); + + LDServerDataSourceStreamBuilder_Filter(stream_builder, "foo"); + + LDServerConfigBuilder_DataSystem_BackgroundSync_Streaming( + cfg_builder, stream_builder); + + LDServerConfig config; + LDStatus status = LDServerConfigBuilder_Build(cfg_builder, &config); + ASSERT_TRUE(LDStatus_Ok(status)); + + LDServerConfig_Free(config); +} + +TEST(ClientBindings, PollingPayloadFilters) { + LDServerConfigBuilder cfg_builder = LDServerConfigBuilder_New("sdk-123"); + + LDServerDataSourcePollBuilder poll_builder = + LDServerDataSourcePollBuilder_New(); + + LDServerDataSourcePollBuilder_Filter(poll_builder, "foo"); + + LDServerConfigBuilder_DataSystem_BackgroundSync_Polling( + cfg_builder, poll_builder); + + LDServerConfig config; + LDStatus status = LDServerConfigBuilder_Build(cfg_builder, &config); + ASSERT_TRUE(LDStatus_Ok(status)); + + LDServerConfig_Free(config); +} From 5ef05d37a09184a15b9e6d4c43e454ad9a2381ae Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 12:02:57 -0700 Subject: [PATCH 5/9] use filter key in polling/streaming data source implementations --- .../sources/polling/polling_data_source.cpp | 14 ++++++++------ .../sources/streaming/streaming_data_source.cpp | 12 ++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp index a0d830843..30fc6d252 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp @@ -28,6 +28,10 @@ static network::HttpRequest MakeRequest( url = network::AppendUrl(url, polling_config.polling_get_path); + if (polling_config.filter_key && url) { + url->append("?filter=" + *polling_config.filter_key); + } + network::HttpRequest::BodyType body; network::HttpMethod method = network::HttpMethod::kGet; @@ -48,7 +52,7 @@ PollingDataSource::PollingDataSource( data_components::DataSourceStatusManager& status_manager, config::built::ServiceEndpoints const& endpoints, config::built::BackgroundSyncConfig::PollingConfig const& - data_source_config, + data_source_config, config::built::HttpProperties const& http_properties) : logger_(logger), status_manager_(status_manager), @@ -61,8 +65,8 @@ PollingDataSource::PollingDataSource( LD_LOG(logger_, LogLevel::kWarn) << "Polling interval too frequent, defaulting to " << std::chrono::duration_cast( - data_source_config.min_polling_interval) - .count() + data_source_config.min_polling_interval) + .count() << " seconds"; polling_interval_ = data_source_config.min_polling_interval; @@ -144,7 +148,6 @@ void PollingDataSource::HandlePollResult(network::HttpResult const& res) { DataSourceStatus::DataSourceState::kInterrupted, DataSourceStatus::ErrorInfo::ErrorKind::kUnknown, "polling response contained no body."); - } else if (res.Status() == 304) { // This should be handled ahead of here, but if we get a 304, // and it didn't have an etag, we still don't want to try to @@ -233,5 +236,4 @@ void PollingDataSource::ShutdownAsync(std::function completion) { boost::asio::post(timer_.get_executor(), completion); } } - -} // namespace launchdarkly::server_side::data_systems +} // namespace launchdarkly::server_side::data_systems diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp index 68ddd3a0b..a77505da4 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp @@ -11,7 +11,6 @@ #include namespace launchdarkly::server_side::data_systems { - static char const* const kCouldNotParseEndpoint = "Could not parse streaming endpoint URL"; @@ -32,7 +31,8 @@ StreamingDataSource::StreamingDataSource( status_manager_(status_manager), http_config_(http_properties), streaming_config_(streaming), - streaming_endpoint_(endpoints.StreamingBaseUrl()) {} + streaming_endpoint_(endpoints.StreamingBaseUrl()) { +} void StreamingDataSource::StartAsync( data_interfaces::IDestination* dest, @@ -46,6 +46,10 @@ void StreamingDataSource::StartAsync( auto updated_url = network::AppendUrl(streaming_endpoint_, streaming_config_.streaming_path); + if (streaming_config_.filter_key && updated_url) { + updated_url->append("?filter=" + *streaming_config_.filter_key); + } + // Bad URL, don't set the client. Start will then report the bad status. if (!updated_url) { LD_LOG(logger_, LogLevel::kError) << kCouldNotParseEndpoint; @@ -92,7 +96,7 @@ void StreamingDataSource::StartAsync( if (http_config_.Tls().PeerVerifyMode() == launchdarkly::config::shared::built::TlsOptions::VerifyMode:: - kVerifyNone) { + kVerifyNone) { client_builder.skip_verify_peer(true); } @@ -150,4 +154,4 @@ void StreamingDataSource::ShutdownAsync(std::function completion) { boost::asio::post(io_, completion); } } -} // namespace launchdarkly::server_side::data_systems +} // namespace launchdarkly::server_side::data_systems From 35b503522d3310465c3b6d6085ec12267287fa62 Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 13:28:57 -0700 Subject: [PATCH 6/9] contract tests + validation --- .../include/data_model/data_model.hpp | 172 ++++++++++++------ .../src/entity_manager.cpp | 20 +- .../server-contract-tests/src/main.cpp | 8 +- libs/server-sdk/src/CMakeLists.txt | 2 + .../payload_filter_validation.cpp | 24 +++ .../payload_filter_validation.hpp | 7 + .../sources/polling/polling_data_source.cpp | 17 +- .../streaming/streaming_data_source.cpp | 14 +- 8 files changed, 191 insertions(+), 73 deletions(-) create mode 100644 libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp create mode 100644 libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.hpp diff --git a/contract-tests/data-model/include/data_model/data_model.hpp b/contract-tests/data-model/include/data_model/data_model.hpp index 51ee897ca..a752ccd7d 100644 --- a/contract-tests/data-model/include/data_model/data_model.hpp +++ b/contract-tests/data-model/include/data_model/data_model.hpp @@ -5,31 +5,41 @@ #include #include "nlohmann/json.hpp" -namespace nlohmann { - -template -struct adl_serializer> { - static void to_json(json& j, std::optional const& opt) { - if (opt == std::nullopt) { - j = nullptr; - } else { - j = *opt; // this will call adl_serializer::to_json which will - // find the free function to_json in T's namespace! +namespace nlohmann +{ + template + struct adl_serializer> + { + static void to_json(json& j, std::optional const& opt) + { + if (opt == std::nullopt) + { + j = nullptr; + } + else + { + j = *opt; // this will call adl_serializer::to_json which will + // find the free function to_json in T's namespace! + } } - } - - static void from_json(json const& j, std::optional& opt) { - if (j.is_null()) { - opt = std::nullopt; - } else { - opt = j.get(); // same as above, but with - // adl_serializer::from_json + + static void from_json(json const& j, std::optional& opt) + { + if (j.is_null()) + { + opt = std::nullopt; + } + else + { + opt = j.get(); // same as above, but with + // adl_serializer::from_json + } } - } -}; -} // namespace nlohmann + }; +} // namespace nlohmann -struct ConfigTLSParams { +struct ConfigTLSParams +{ std::optional skipVerifyPeer; std::optional customCAFile; }; @@ -38,23 +48,30 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigTLSParams, skipVerifyPeer, customCAFile); -struct ConfigStreamingParams { +struct ConfigStreamingParams +{ std::optional baseUri; std::optional initialRetryDelayMs; + std::optional filter; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigStreamingParams, baseUri, - initialRetryDelayMs); + initialRetryDelayMs, filter); -struct ConfigPollingParams { +struct ConfigPollingParams +{ std::optional baseUri; std::optional pollIntervalMs; + std::optional filter; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigPollingParams, baseUri, - pollIntervalMs); + pollIntervalMs, filter); -struct ConfigEventParams { +struct ConfigEventParams +{ std::optional baseUri; std::optional capacity; std::optional enableDiagnostics; @@ -62,6 +79,7 @@ struct ConfigEventParams { std::vector globalPrivateAttributes; std::optional flushIntervalMs; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigEventParams, baseUri, capacity, @@ -69,35 +87,43 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigEventParams, allAttributesPrivate, globalPrivateAttributes, flushIntervalMs); -struct ConfigServiceEndpointsParams { + +struct ConfigServiceEndpointsParams +{ std::optional streaming; std::optional polling; std::optional events; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigServiceEndpointsParams, streaming, polling, events); -struct ConfigClientSideParams { +struct ConfigClientSideParams +{ nlohmann::json initialContext; std::optional evaluationReasons; std::optional useReport; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigClientSideParams, initialContext, evaluationReasons, useReport); -struct ConfigTags { +struct ConfigTags +{ std::optional applicationId; std::optional applicationVersion; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigTags, applicationId, applicationVersion); -struct ConfigParams { +struct ConfigParams +{ std::string credential; std::optional startWaitTimeMs; std::optional initCanFail; @@ -109,6 +135,7 @@ struct ConfigParams { std::optional tags; std::optional tls; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigParams, credential, startWaitTimeMs, @@ -121,7 +148,8 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigParams, tags, tls); -struct ContextSingleParams { +struct ContextSingleParams +{ std::optional kind; std::string key; std::optional name; @@ -133,7 +161,8 @@ struct ContextSingleParams { // These are defined manually because of the 'private' field, which is a // reserved keyword in C++. inline void to_json(nlohmann::json& nlohmann_json_j, - ContextSingleParams const& nlohmann_json_t) { + ContextSingleParams const& nlohmann_json_t) +{ nlohmann_json_j["kind"] = nlohmann_json_t.kind; nlohmann_json_j["key"] = nlohmann_json_t.key; nlohmann_json_j["name"] = nlohmann_json_t.name; @@ -141,8 +170,10 @@ inline void to_json(nlohmann::json& nlohmann_json_j, nlohmann_json_j["private"] = nlohmann_json_t._private; nlohmann_json_j["custom"] = nlohmann_json_t.custom; } + inline void from_json(nlohmann::json const& nlohmann_json_j, - ContextSingleParams& nlohmann_json_t) { + ContextSingleParams& nlohmann_json_t) +{ ContextSingleParams nlohmann_json_default_obj; nlohmann_json_t.kind = nlohmann_json_j.value("kind", nlohmann_json_default_obj.kind); @@ -158,7 +189,8 @@ inline void from_json(nlohmann::json const& nlohmann_json_j, nlohmann_json_j.value("custom", nlohmann_json_default_obj.custom); } -struct ContextBuildParams { +struct ContextBuildParams +{ std::optional single; std::optional> multi; }; @@ -167,37 +199,43 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ContextBuildParams, single, multi); -struct ContextConvertParams { +struct ContextConvertParams +{ std::string input; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ContextConvertParams, input); -struct ContextResponse { +struct ContextResponse +{ std::optional output; std::optional error; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ContextResponse, output, error); -struct CreateInstanceParams { +struct CreateInstanceParams +{ ConfigParams configuration; std::string tag; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(CreateInstanceParams, configuration, tag); enum class ValueType { Bool = 1, Int, Double, String, Any, Unspecified }; + NLOHMANN_JSON_SERIALIZE_ENUM(ValueType, {{ValueType::Bool, "bool"}, - {ValueType::Int, "int"}, - {ValueType::Double, "double"}, - {ValueType::String, "string"}, - {ValueType::Any, "any"}, - {ValueType::Unspecified, ""}}) + {ValueType::Int, "int"}, + {ValueType::Double, "double"}, + {ValueType::String, "string"}, + {ValueType::Any, "any"}, + {ValueType::Unspecified, ""}}) -struct EvaluateFlagParams { +struct EvaluateFlagParams +{ std::string flagKey; std::optional context; ValueType valueType; @@ -205,6 +243,7 @@ struct EvaluateFlagParams { bool detail; EvaluateFlagParams(); }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateFlagParams, flagKey, context, @@ -212,40 +251,49 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateFlagParams, defaultValue, detail); -struct EvaluateFlagResponse { +struct EvaluateFlagResponse +{ nlohmann::json value; std::optional variationIndex; std::optional reason; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateFlagResponse, value, variationIndex, reason); -struct EvaluateAllFlagParams { +struct EvaluateAllFlagParams +{ std::optional context; std::optional withReasons; std::optional clientSideOnly; std::optional detailsOnlyForTrackedFlags; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateAllFlagParams, context, withReasons, clientSideOnly, detailsOnlyForTrackedFlags); -struct EvaluateAllFlagsResponse { + +struct EvaluateAllFlagsResponse +{ nlohmann::json state; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateAllFlagsResponse, state); -struct CustomEventParams { +struct CustomEventParams +{ std::string eventKey; std::optional context; std::optional data; std::optional omitNullData; std::optional metricValue; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(CustomEventParams, eventKey, context, @@ -253,12 +301,15 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(CustomEventParams, omitNullData, metricValue); -struct IdentifyEventParams { +struct IdentifyEventParams +{ nlohmann::json context; }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(IdentifyEventParams, context); -enum class Command { +enum class Command +{ Unknown = -1, EvaluateFlag, EvaluateAllFlags, @@ -268,17 +319,19 @@ enum class Command { ContextBuild, ContextConvert }; + NLOHMANN_JSON_SERIALIZE_ENUM(Command, {{Command::Unknown, nullptr}, - {Command::EvaluateFlag, "evaluate"}, - {Command::EvaluateAllFlags, "evaluateAll"}, - {Command::IdentifyEvent, "identifyEvent"}, - {Command::CustomEvent, "customEvent"}, - {Command::FlushEvents, "flushEvents"}, - {Command::ContextBuild, "contextBuild"}, - {Command::ContextConvert, "contextConvert"}}); - -struct CommandParams { + {Command::EvaluateFlag, "evaluate"}, + {Command::EvaluateAllFlags, "evaluateAll"}, + {Command::IdentifyEvent, "identifyEvent"}, + {Command::CustomEvent, "customEvent"}, + {Command::FlushEvents, "flushEvents"}, + {Command::ContextBuild, "contextBuild"}, + {Command::ContextConvert, "contextConvert"}}); + +struct CommandParams +{ Command command; std::optional evaluate; std::optional evaluateAll; @@ -288,6 +341,7 @@ struct CommandParams { std::optional contextConvert; CommandParams(); }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(CommandParams, command, evaluate, diff --git a/contract-tests/server-contract-tests/src/entity_manager.cpp b/contract-tests/server-contract-tests/src/entity_manager.cpp index 68d75d642..c92093d0e 100644 --- a/contract-tests/server-contract-tests/src/entity_manager.cpp +++ b/contract-tests/server-contract-tests/src/entity_manager.cpp @@ -11,7 +11,8 @@ using namespace launchdarkly::server_side; EntityManager::EntityManager(boost::asio::any_io_executor executor, launchdarkly::Logger& logger) - : counter_{0}, executor_{std::move(executor)}, logger_{logger} {} + : counter_{0}, executor_{std::move(executor)}, logger_{logger} { +} std::optional EntityManager::create(ConfigParams const& in) { std::string id = std::to_string(counter_++); @@ -31,9 +32,9 @@ std::optional EntityManager::create(ConfigParams const& in) { auto& endpoints = config_builder.ServiceEndpoints() - .EventsBaseUrl(default_endpoints.EventsBaseUrl()) - .PollingBaseUrl(default_endpoints.PollingBaseUrl()) - .StreamingBaseUrl(default_endpoints.StreamingBaseUrl()); + .EventsBaseUrl(default_endpoints.EventsBaseUrl()) + .PollingBaseUrl(default_endpoints.PollingBaseUrl()) + .StreamingBaseUrl(default_endpoints.StreamingBaseUrl()); if (in.serviceEndpoints) { if (in.serviceEndpoints->streaming) { @@ -52,12 +53,15 @@ std::optional EntityManager::create(ConfigParams const& in) { if (in.streaming->baseUri) { endpoints.StreamingBaseUrl(*in.streaming->baseUri); } + auto streaming = decltype(datasystem)::Streaming(); if (in.streaming->initialRetryDelayMs) { - auto streaming = decltype(datasystem)::Streaming(); streaming.InitialReconnectDelay( std::chrono::milliseconds(*in.streaming->initialRetryDelayMs)); - datasystem.Synchronizer(std::move(streaming)); } + if (in.streaming->filter) { + streaming.Filter(*in.streaming->filter); + } + datasystem.Synchronizer(std::move(streaming)); } if (in.polling) { @@ -72,6 +76,9 @@ std::optional EntityManager::create(ConfigParams const& in) { std::chrono::milliseconds( *in.polling->pollIntervalMs))); } + if (in.polling->filter) { + method.Filter(*in.polling->filter); + } datasystem.Synchronizer(std::move(method)); } } @@ -106,7 +113,6 @@ std::optional EntityManager::create(ConfigParams const& in) { event_config.FlushInterval( std::chrono::milliseconds(*events.flushIntervalMs)); } - } else { event_config.Disable(); } diff --git a/contract-tests/server-contract-tests/src/main.cpp b/contract-tests/server-contract-tests/src/main.cpp index e492b4ab6..c4084d0a6 100644 --- a/contract-tests/server-contract-tests/src/main.cpp +++ b/contract-tests/server-contract-tests/src/main.cpp @@ -25,7 +25,7 @@ int main(int argc, char* argv[]) { std::string port = default_port; if (argc == 2) { port = - argv[1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + argv[1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) } try { @@ -45,7 +45,8 @@ int main(int argc, char* argv[]) { srv.add_capability("tls:verify-peer"); srv.add_capability("tls:skip-verify-peer"); srv.add_capability("tls:custom-ca"); - + srv.add_capability("filtering"); + srv.add_capability("filtering-strict"); net::signal_set signals{ioc, SIGINT, SIGTERM}; boost::asio::spawn(ioc.get_executor(), [&](auto yield) mutable { @@ -56,12 +57,11 @@ int main(int argc, char* argv[]) { ioc.run(); LD_LOG(logger, LogLevel::kInfo) << "bye!"; - } catch (boost::bad_lexical_cast&) { LD_LOG(logger, LogLevel::kError) << "invalid port (" << port << "), provide a number (no arguments defaults " - "to port " + "to port " << default_port << ")"; return EXIT_FAILURE; } catch (std::exception const& e) { diff --git a/libs/server-sdk/src/CMakeLists.txt b/libs/server-sdk/src/CMakeLists.txt index 692e4af38..a440b79ed 100644 --- a/libs/server-sdk/src/CMakeLists.txt +++ b/libs/server-sdk/src/CMakeLists.txt @@ -42,6 +42,8 @@ target_sources(${LIBNAME} data_components/serialization_adapters/json_deserializer.cpp data_components/serialization_adapters/json_destination.hpp data_components/serialization_adapters/json_destination.cpp + data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.hpp + data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp data_systems/background_sync/sources/polling/polling_data_source.hpp data_systems/background_sync/sources/polling/polling_data_source.cpp data_systems/background_sync/sources/streaming/streaming_data_source.hpp diff --git a/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp b/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp new file mode 100644 index 000000000..24df165b5 --- /dev/null +++ b/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp @@ -0,0 +1,24 @@ +#include "payload_filter_validation.hpp" + +#include + +namespace launchdarkly::server_side::data_systems::detail { +bool ValidateFilterKey(std::string const& filter_key) { + if (filter_key.empty()) { + return false; + } + try { + return regex_search(filter_key, + boost::regex( + "^[a-zA-Z0-9][._\\-a-zA-Z0-9]*$")); + } catch (boost::bad_expression) { + // boost::bad_expression can be thrown by basic_regex when compiling a + // regular expression. + return false; + } catch (std::runtime_error) { + // std::runtime_error can be thrown when a call + // to regex_search results in an "everlasting" search + return false; + } +} +} // namespace launchdarkly::server_side::data_systems::detail diff --git a/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.hpp b/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.hpp new file mode 100644 index 000000000..dcbad651b --- /dev/null +++ b/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace launchdarkly::server_side::data_systems::detail { +bool ValidateFilterKey(std::string const& filter_key); +} diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp index 30fc6d252..0f2b5c5cb 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp @@ -10,6 +10,8 @@ #include +#include "../../detail/payload_filter_validation/payload_filter_validation.hpp" + #include namespace launchdarkly::server_side::data_systems { @@ -20,7 +22,13 @@ static char const* const kErrorPutInvalid = static char const* const kCouldNotParseEndpoint = "Could not parse polling endpoint URL"; +static char const* const kInvalidFilterKey = + "Invalid payload filter configured on polling data source, full environment " + "will be fetched.\nEnsure the filter key is not empty and was copied " + "correctly from LaunchDarkly settings"; + static network::HttpRequest MakeRequest( + Logger const& logger, config::built::BackgroundSyncConfig::PollingConfig const& polling_config, config::built::ServiceEndpoints const& endpoints, config::built::HttpProperties const& http_properties) { @@ -29,7 +37,11 @@ static network::HttpRequest MakeRequest( url = network::AppendUrl(url, polling_config.polling_get_path); if (polling_config.filter_key && url) { - url->append("?filter=" + *polling_config.filter_key); + if (detail::ValidateFilterKey(*polling_config.filter_key)) { + url->append("?filter=" + *polling_config.filter_key); + } else { + LD_LOG(logger, LogLevel::kError) << kInvalidFilterKey; + } } network::HttpRequest::BodyType body; @@ -58,7 +70,8 @@ PollingDataSource::PollingDataSource( status_manager_(status_manager), requester_(ioc, http_properties.Tls()), polling_interval_(data_source_config.poll_interval), - request_(MakeRequest(data_source_config, endpoints, http_properties)), + request_(MakeRequest(logger_, data_source_config, endpoints, + http_properties)), timer_(ioc), sink_(nullptr) { if (polling_interval_ < data_source_config.min_polling_interval) { diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp index a77505da4..6ba637364 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp @@ -2,6 +2,8 @@ #include +#include "../../detail/payload_filter_validation/payload_filter_validation.hpp" + #include #include #include @@ -14,6 +16,12 @@ namespace launchdarkly::server_side::data_systems { static char const* const kCouldNotParseEndpoint = "Could not parse streaming endpoint URL"; +static char const* const kInvalidFilterKey = + "Invalid payload filter configured on polling data source, full environment " + "will be fetched.\nEnsure the filter key is not empty and was copied " + "correctly from LaunchDarkly settings"; + + std::string const& StreamingDataSource::Identity() const { static std::string const identity = "streaming data source"; return identity; @@ -47,7 +55,11 @@ void StreamingDataSource::StartAsync( streaming_config_.streaming_path); if (streaming_config_.filter_key && updated_url) { - updated_url->append("?filter=" + *streaming_config_.filter_key); + if (detail::ValidateFilterKey(*streaming_config_.filter_key)) { + updated_url->append("?filter=" + *streaming_config_.filter_key); + } else { + LD_LOG(logger_, LogLevel::kError) << kInvalidFilterKey; + } } // Bad URL, don't set the client. Start will then report the bad status. From 65b3fa978370fc1e788bd1a46ee50e12a17d609e Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 13:40:18 -0700 Subject: [PATCH 7/9] clang-fmt --- .../include/data_model/data_model.hpp | 153 +++++++----------- .../src/entity_manager.cpp | 9 +- .../shared/builders/data_source_builder.hpp | 57 ++++--- .../shared/built/data_source_config.hpp | 9 +- .../common/src/config/data_source_builder.cpp | 18 +-- .../common/tests/data_source_builder_test.cpp | 28 ++-- .../server_side/bindings/c/config/builder.h | 126 +++++++-------- libs/server-sdk/src/bindings/c/builder.cpp | 6 +- .../sources/polling/polling_data_source.cpp | 15 +- .../streaming/streaming_data_source.cpp | 13 +- libs/server-sdk/tests/config_builder_test.cpp | 25 ++- .../tests/server_c_bindings_test.cpp | 19 ++- 12 files changed, 216 insertions(+), 262 deletions(-) diff --git a/contract-tests/data-model/include/data_model/data_model.hpp b/contract-tests/data-model/include/data_model/data_model.hpp index a752ccd7d..12ca2e28d 100644 --- a/contract-tests/data-model/include/data_model/data_model.hpp +++ b/contract-tests/data-model/include/data_model/data_model.hpp @@ -5,41 +5,30 @@ #include #include "nlohmann/json.hpp" -namespace nlohmann -{ - template - struct adl_serializer> - { - static void to_json(json& j, std::optional const& opt) - { - if (opt == std::nullopt) - { - j = nullptr; - } - else - { - j = *opt; // this will call adl_serializer::to_json which will - // find the free function to_json in T's namespace! - } +namespace nlohmann { +template +struct adl_serializer> { + static void to_json(json& j, std::optional const& opt) { + if (opt == std::nullopt) { + j = nullptr; + } else { + j = *opt; // this will call adl_serializer::to_json which will + // find the free function to_json in T's namespace! } - - static void from_json(json const& j, std::optional& opt) - { - if (j.is_null()) - { - opt = std::nullopt; - } - else - { - opt = j.get(); // same as above, but with - // adl_serializer::from_json - } + } + + static void from_json(json const& j, std::optional& opt) { + if (j.is_null()) { + opt = std::nullopt; + } else { + opt = j.get(); // same as above, but with + // adl_serializer::from_json } - }; -} // namespace nlohmann + } +}; +} // namespace nlohmann -struct ConfigTLSParams -{ +struct ConfigTLSParams { std::optional skipVerifyPeer; std::optional customCAFile; }; @@ -48,8 +37,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigTLSParams, skipVerifyPeer, customCAFile); -struct ConfigStreamingParams -{ +struct ConfigStreamingParams { std::optional baseUri; std::optional initialRetryDelayMs; std::optional filter; @@ -57,10 +45,10 @@ struct ConfigStreamingParams NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigStreamingParams, baseUri, - initialRetryDelayMs, filter); + initialRetryDelayMs, + filter); -struct ConfigPollingParams -{ +struct ConfigPollingParams { std::optional baseUri; std::optional pollIntervalMs; std::optional filter; @@ -68,10 +56,10 @@ struct ConfigPollingParams NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigPollingParams, baseUri, - pollIntervalMs, filter); + pollIntervalMs, + filter); -struct ConfigEventParams -{ +struct ConfigEventParams { std::optional baseUri; std::optional capacity; std::optional enableDiagnostics; @@ -88,8 +76,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigEventParams, globalPrivateAttributes, flushIntervalMs); -struct ConfigServiceEndpointsParams -{ +struct ConfigServiceEndpointsParams { std::optional streaming; std::optional polling; std::optional events; @@ -100,8 +87,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigServiceEndpointsParams, polling, events); -struct ConfigClientSideParams -{ +struct ConfigClientSideParams { nlohmann::json initialContext; std::optional evaluationReasons; std::optional useReport; @@ -112,8 +98,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigClientSideParams, evaluationReasons, useReport); -struct ConfigTags -{ +struct ConfigTags { std::optional applicationId; std::optional applicationVersion; }; @@ -122,8 +107,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigTags, applicationId, applicationVersion); -struct ConfigParams -{ +struct ConfigParams { std::string credential; std::optional startWaitTimeMs; std::optional initCanFail; @@ -148,8 +132,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigParams, tags, tls); -struct ContextSingleParams -{ +struct ContextSingleParams { std::optional kind; std::string key; std::optional name; @@ -161,8 +144,7 @@ struct ContextSingleParams // These are defined manually because of the 'private' field, which is a // reserved keyword in C++. inline void to_json(nlohmann::json& nlohmann_json_j, - ContextSingleParams const& nlohmann_json_t) -{ + ContextSingleParams const& nlohmann_json_t) { nlohmann_json_j["kind"] = nlohmann_json_t.kind; nlohmann_json_j["key"] = nlohmann_json_t.key; nlohmann_json_j["name"] = nlohmann_json_t.name; @@ -172,8 +154,7 @@ inline void to_json(nlohmann::json& nlohmann_json_j, } inline void from_json(nlohmann::json const& nlohmann_json_j, - ContextSingleParams& nlohmann_json_t) -{ + ContextSingleParams& nlohmann_json_t) { ContextSingleParams nlohmann_json_default_obj; nlohmann_json_t.kind = nlohmann_json_j.value("kind", nlohmann_json_default_obj.kind); @@ -189,8 +170,7 @@ inline void from_json(nlohmann::json const& nlohmann_json_j, nlohmann_json_j.value("custom", nlohmann_json_default_obj.custom); } -struct ContextBuildParams -{ +struct ContextBuildParams { std::optional single; std::optional> multi; }; @@ -199,23 +179,20 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ContextBuildParams, single, multi); -struct ContextConvertParams -{ +struct ContextConvertParams { std::string input; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ContextConvertParams, input); -struct ContextResponse -{ +struct ContextResponse { std::optional output; std::optional error; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ContextResponse, output, error); -struct CreateInstanceParams -{ +struct CreateInstanceParams { ConfigParams configuration; std::string tag; }; @@ -228,14 +205,13 @@ enum class ValueType { Bool = 1, Int, Double, String, Any, Unspecified }; NLOHMANN_JSON_SERIALIZE_ENUM(ValueType, {{ValueType::Bool, "bool"}, - {ValueType::Int, "int"}, - {ValueType::Double, "double"}, - {ValueType::String, "string"}, - {ValueType::Any, "any"}, - {ValueType::Unspecified, ""}}) - -struct EvaluateFlagParams -{ + {ValueType::Int, "int"}, + {ValueType::Double, "double"}, + {ValueType::String, "string"}, + {ValueType::Any, "any"}, + {ValueType::Unspecified, ""}}) + +struct EvaluateFlagParams { std::string flagKey; std::optional context; ValueType valueType; @@ -251,8 +227,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateFlagParams, defaultValue, detail); -struct EvaluateFlagResponse -{ +struct EvaluateFlagResponse { nlohmann::json value; std::optional variationIndex; std::optional reason; @@ -263,8 +238,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateFlagResponse, variationIndex, reason); -struct EvaluateAllFlagParams -{ +struct EvaluateAllFlagParams { std::optional context; std::optional withReasons; std::optional clientSideOnly; @@ -277,16 +251,14 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateAllFlagParams, clientSideOnly, detailsOnlyForTrackedFlags); -struct EvaluateAllFlagsResponse -{ +struct EvaluateAllFlagsResponse { nlohmann::json state; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(EvaluateAllFlagsResponse, state); -struct CustomEventParams -{ +struct CustomEventParams { std::string eventKey; std::optional context; std::optional data; @@ -301,15 +273,13 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(CustomEventParams, omitNullData, metricValue); -struct IdentifyEventParams -{ +struct IdentifyEventParams { nlohmann::json context; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(IdentifyEventParams, context); -enum class Command -{ +enum class Command { Unknown = -1, EvaluateFlag, EvaluateAllFlags, @@ -322,16 +292,15 @@ enum class Command NLOHMANN_JSON_SERIALIZE_ENUM(Command, {{Command::Unknown, nullptr}, - {Command::EvaluateFlag, "evaluate"}, - {Command::EvaluateAllFlags, "evaluateAll"}, - {Command::IdentifyEvent, "identifyEvent"}, - {Command::CustomEvent, "customEvent"}, - {Command::FlushEvents, "flushEvents"}, - {Command::ContextBuild, "contextBuild"}, - {Command::ContextConvert, "contextConvert"}}); - -struct CommandParams -{ + {Command::EvaluateFlag, "evaluate"}, + {Command::EvaluateAllFlags, "evaluateAll"}, + {Command::IdentifyEvent, "identifyEvent"}, + {Command::CustomEvent, "customEvent"}, + {Command::FlushEvents, "flushEvents"}, + {Command::ContextBuild, "contextBuild"}, + {Command::ContextConvert, "contextConvert"}}); + +struct CommandParams { Command command; std::optional evaluate; std::optional evaluateAll; diff --git a/contract-tests/server-contract-tests/src/entity_manager.cpp b/contract-tests/server-contract-tests/src/entity_manager.cpp index c92093d0e..71e2d479e 100644 --- a/contract-tests/server-contract-tests/src/entity_manager.cpp +++ b/contract-tests/server-contract-tests/src/entity_manager.cpp @@ -11,8 +11,7 @@ using namespace launchdarkly::server_side; EntityManager::EntityManager(boost::asio::any_io_executor executor, launchdarkly::Logger& logger) - : counter_{0}, executor_{std::move(executor)}, logger_{logger} { -} + : counter_{0}, executor_{std::move(executor)}, logger_{logger} {} std::optional EntityManager::create(ConfigParams const& in) { std::string id = std::to_string(counter_++); @@ -32,9 +31,9 @@ std::optional EntityManager::create(ConfigParams const& in) { auto& endpoints = config_builder.ServiceEndpoints() - .EventsBaseUrl(default_endpoints.EventsBaseUrl()) - .PollingBaseUrl(default_endpoints.PollingBaseUrl()) - .StreamingBaseUrl(default_endpoints.StreamingBaseUrl()); + .EventsBaseUrl(default_endpoints.EventsBaseUrl()) + .PollingBaseUrl(default_endpoints.PollingBaseUrl()) + .StreamingBaseUrl(default_endpoints.StreamingBaseUrl()); if (in.serviceEndpoints) { if (in.serviceEndpoints->streaming) { diff --git a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp index 08e262083..9fffa74ce 100644 --- a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp +++ b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp @@ -5,8 +5,8 @@ #include #include -#include #include +#include namespace launchdarkly::config::shared::builders { /** @@ -17,19 +17,17 @@ template class DataSourceBuilder; template -struct is_server_sdk : std::false_type { -}; +struct is_server_sdk : std::false_type {}; template <> -struct is_server_sdk : std::true_type { -}; +struct is_server_sdk : std::true_type {}; /** * Builds a configuration for a streaming data source. */ template class StreamingBuilder { -public: + public: StreamingBuilder(); /** @@ -77,7 +75,7 @@ class StreamingBuilder { */ [[nodiscard]] built::StreamingConfig Build() const; -private: + private: built::StreamingConfig config_; }; @@ -86,7 +84,7 @@ class StreamingBuilder { */ template class PollingBuilder { -public: + public: PollingBuilder(); /** @@ -97,23 +95,23 @@ class PollingBuilder { PollingBuilder& PollInterval(std::chrono::seconds poll_interval); /** - * Sets the filter key for the polling connection. - * - * By default, the SDK is able to evaluate all flags in an environment. - * - * If this is undesirable - for example, because the environment contains - * thousands of flags, but this application only needs to evaluate - * a smaller, known subset - then a filter may be setup in LaunchDarkly, - * and the filter's key specified here. - * - * Evaluations for flags that aren't part of the filtered environment will - * return default values. - * - * @param filter_key The filter key. If the key is malformed or nonexistent, - * then a full LaunchDarkly environment will be fetched. In the case of a - * malformed key, the SDK will additionally log a runtime error. - * @return Reference to this builder. - */ + * Sets the filter key for the polling connection. + * + * By default, the SDK is able to evaluate all flags in an environment. + * + * If this is undesirable - for example, because the environment contains + * thousands of flags, but this application only needs to evaluate + * a smaller, known subset - then a filter may be setup in LaunchDarkly, + * and the filter's key specified here. + * + * Evaluations for flags that aren't part of the filtered environment will + * return default values. + * + * @param filter_key The filter key. If the key is malformed or nonexistent, + * then a full LaunchDarkly environment will be fetched. In the case of a + * malformed key, the SDK will additionally log a runtime error. + * @return Reference to this builder. + */ template std::enable_if_t::value, PollingBuilder&> Filter( std::string filter_key) { @@ -127,14 +125,13 @@ class PollingBuilder { */ [[nodiscard]] built::PollingConfig Build() const; -private: + private: built::PollingConfig config_; }; - template <> class DataSourceBuilder { -public: + public: using Streaming = StreamingBuilder; using Polling = PollingBuilder; @@ -199,9 +196,9 @@ class DataSourceBuilder { */ [[nodiscard]] built::DataSourceConfig Build() const; -private: + private: std::variant method_; bool with_reasons_; bool use_report_; }; -} // namespace launchdarkly::config::shared::builders +} // namespace launchdarkly::config::shared::builders diff --git a/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp b/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp index 45742e3c4..035e29c1d 100644 --- a/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp +++ b/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp @@ -27,8 +27,8 @@ struct StreamingConfig { inline bool operator==(StreamingConfig const& lhs, StreamingConfig const& rhs) { return lhs.initial_reconnect_delay == rhs.initial_reconnect_delay && - lhs.streaming_path == rhs.streaming_path && lhs.filter_key == rhs. - filter_key; + lhs.streaming_path == rhs.streaming_path && + lhs.filter_key == rhs.filter_key; } template @@ -62,6 +62,5 @@ struct DataSourceConfig { }; template <> -struct DataSourceConfig { -}; -} // namespace launchdarkly::config::shared::built +struct DataSourceConfig {}; +} // namespace launchdarkly::config::shared::built diff --git a/libs/common/src/config/data_source_builder.cpp b/libs/common/src/config/data_source_builder.cpp index f5252c061..e19a9417e 100644 --- a/libs/common/src/config/data_source_builder.cpp +++ b/libs/common/src/config/data_source_builder.cpp @@ -1,15 +1,15 @@ #include namespace launchdarkly::config::shared::builders { + template -struct MethodVisitor { -}; +struct MethodVisitor {}; template <> struct MethodVisitor { using SDK = ClientSDK; using Result = - std::variant, built::PollingConfig>; + std::variant, built::PollingConfig>; Result operator()(StreamingBuilder const& streaming) const { return streaming.Build(); @@ -22,8 +22,7 @@ struct MethodVisitor { template StreamingBuilder::StreamingBuilder() - : config_(Defaults::StreamingConfig()) { -} + : config_(Defaults::StreamingConfig()) {} template StreamingBuilder& StreamingBuilder::InitialReconnectDelay( @@ -39,8 +38,7 @@ built::StreamingConfig StreamingBuilder::Build() const { template PollingBuilder::PollingBuilder() - : config_(Defaults::PollingConfig()) { -} + : config_(Defaults::PollingConfig()) {} template PollingBuilder& PollingBuilder::PollInterval( @@ -55,8 +53,7 @@ built::PollingConfig PollingBuilder::Build() const { } DataSourceBuilder::DataSourceBuilder() - : with_reasons_(false), use_report_(false), method_(Streaming()) { -} + : with_reasons_(false), use_report_(false), method_(Streaming()) {} DataSourceBuilder& DataSourceBuilder::WithReasons( bool value) { @@ -92,4 +89,5 @@ template class PollingBuilder; template class StreamingBuilder; template class StreamingBuilder; -} // namespace launchdarkly::config::shared::builders + +} // namespace launchdarkly::config::shared::builders diff --git a/libs/common/tests/data_source_builder_test.cpp b/libs/common/tests/data_source_builder_test.cpp index 6e66d954d..8f16edc94 100644 --- a/libs/common/tests/data_source_builder_test.cpp +++ b/libs/common/tests/data_source_builder_test.cpp @@ -11,37 +11,37 @@ using namespace launchdarkly; TEST(DataSourceBuilderTests, CanCreateStreamingClientConfig) { auto client_config = client_side::DataSourceBuilder() - .WithReasons(true) - .UseReport(true) - .Method(client_side::DataSourceBuilder::Streaming() - .InitialReconnectDelay(std::chrono::milliseconds{1500})) - .Build(); + .WithReasons(true) + .UseReport(true) + .Method(client_side::DataSourceBuilder::Streaming() + .InitialReconnectDelay(std::chrono::milliseconds{1500})) + .Build(); EXPECT_TRUE(client_config.use_report); EXPECT_TRUE(client_config.with_reasons); EXPECT_EQ( std::chrono::milliseconds{1500}, std::get< - config::shared::built::StreamingConfig>( + config::shared::built::StreamingConfig>( client_config.method) - .initial_reconnect_delay); + .initial_reconnect_delay); } TEST(DataSourceBuilderTests, CanCreatePollingClientConfig) { auto client_config = client_side::DataSourceBuilder() - .WithReasons(false) - .UseReport(false) - .Method(client_side::DataSourceBuilder::Polling().PollInterval( - std::chrono::seconds{88000})) - .Build(); + .WithReasons(false) + .UseReport(false) + .Method(client_side::DataSourceBuilder::Polling().PollInterval( + std::chrono::seconds{88000})) + .Build(); EXPECT_FALSE(client_config.use_report); EXPECT_FALSE(client_config.with_reasons); EXPECT_EQ( std::chrono::seconds{88000}, std::get< - config::shared::built::PollingConfig>( + config::shared::built::PollingConfig>( client_config.method) - .poll_interval); + .poll_interval); } diff --git a/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h b/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h index 6f45c963c..9ac1a8cf1 100644 --- a/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h +++ b/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h @@ -21,10 +21,10 @@ extern "C" { typedef struct _LDServerConfigBuilder* LDServerConfigBuilder; typedef struct _LDServerDataSourceStreamBuilder* -LDServerDataSourceStreamBuilder; + LDServerDataSourceStreamBuilder; typedef struct _LDServerDataSourcePollBuilder* LDServerDataSourcePollBuilder; typedef struct _LDServerHttpPropertiesTlsBuilder* -LDServerHttpPropertiesTlsBuilder; + LDServerHttpPropertiesTlsBuilder; /** * Constructs a client-side config builder. @@ -64,8 +64,8 @@ LDServerConfigBuilder_ServiceEndpoints_EventsBaseURL(LDServerConfigBuilder b, */ LD_EXPORT(void) LDServerConfigBuilder_ServiceEndpoints_RelayProxyBaseURL( - LDServerConfigBuilder b, - char const* url); + LDServerConfigBuilder b, + char const* url); /** * Sets an identifier for the application. @@ -202,8 +202,8 @@ LDServerConfigBuilder_Events_PrivateAttribute(LDServerConfigBuilder b, */ LD_EXPORT(void) LDServerConfigBuilder_DataSystem_BackgroundSync_Streaming( - LDServerConfigBuilder b, - LDServerDataSourceStreamBuilder stream_builder); + LDServerConfigBuilder b, + LDServerDataSourceStreamBuilder stream_builder); /** * Configures the Background Sync data system with a Polling synchronizer. @@ -220,8 +220,8 @@ LDServerConfigBuilder_DataSystem_BackgroundSync_Streaming( */ LD_EXPORT(void) LDServerConfigBuilder_DataSystem_BackgroundSync_Polling( - LDServerConfigBuilder b, - LDServerDataSourcePollBuilder poll_builder); + LDServerConfigBuilder b, + LDServerDataSourcePollBuilder poll_builder); /** * Configures the Lazy Load data system. This method is mutually exclusive with @@ -236,8 +236,8 @@ LDServerConfigBuilder_DataSystem_BackgroundSync_Polling( */ LD_EXPORT(void) LDServerConfigBuilder_DataSystem_LazyLoad( - LDServerConfigBuilder b, - LDServerLazyLoadBuilder lazy_load_builder); + LDServerConfigBuilder b, + LDServerLazyLoadBuilder lazy_load_builder); /** * Specify if the SDK's data system should be enabled or not. @@ -275,31 +275,30 @@ LDServerDataSourceStreamBuilder_New(); */ LD_EXPORT(void) LDServerDataSourceStreamBuilder_InitialReconnectDelayMs( - LDServerDataSourceStreamBuilder b, - unsigned int milliseconds); - -/** -* Sets the filter key for the streaming connection. -* -* By default, the SDK is able to evaluate all flags in an environment. -* -* If this is undesirable - for example, because the environment contains -* thousands of flags, but this application only needs to evaluate -* a smaller, known subset - then a filter may be setup in LaunchDarkly, -* and the filter's key specified here. -* -* Evaluations for flags that aren't part of the filtered environment will -* return default values. -* -* @param b Streaming method builder. Must not be NULL. - * @param filter_key The filter key. Must not be NULL. If the key is malformed or - * nonexistent, then a full LaunchDarkly environment will be fetched. In the case - * of a malformed key, the SDK will additionally log a runtime error. -*/ -LD_EXPORT(void) -LDServerDataSourceStreamBuilder_Filter( - LDServerDataSourceStreamBuilder b, - char const* filter_key); + LDServerDataSourceStreamBuilder b, + unsigned int milliseconds); + +/** + * Sets the filter key for the streaming connection. + * + * By default, the SDK is able to evaluate all flags in an environment. + * + * If this is undesirable - for example, because the environment contains + * thousands of flags, but this application only needs to evaluate + * a smaller, known subset - then a filter may be setup in LaunchDarkly, + * and the filter's key specified here. + * + * Evaluations for flags that aren't part of the filtered environment will + * return default values. + * + * @param b Streaming method builder. Must not be NULL. + * @param filter_key The filter key. Must not be NULL. If the key is malformed + * or nonexistent, then a full LaunchDarkly environment will be fetched. In the + * case of a malformed key, the SDK will additionally log a runtime error. + */ +LD_EXPORT(void) +LDServerDataSourceStreamBuilder_Filter(LDServerDataSourceStreamBuilder b, + char const* filter_key); /** * Frees a Streaming method builder. Do not call if the builder was consumed by @@ -332,27 +331,26 @@ LDServerDataSourcePollBuilder_IntervalS(LDServerDataSourcePollBuilder b, unsigned int seconds); /** -* Sets the filter key for the polling connection. -* -* By default, the SDK is able to evaluate all flags in an environment. -* -* If this is undesirable - for example, because the environment contains -* thousands of flags, but this application only needs to evaluate -* a smaller, known subset - then a filter may be setup in LaunchDarkly, -* and the filter's key specified here. -* -* Evaluations for flags that aren't part of the filtered environment will -* return default values. -* -* @param b Polling method builder. Must not be NULL. -* @param filter_key The filter key. Must not be NULL. If the key is malformed or -* nonexistent, then a full LaunchDarkly environment will be fetched. In the case -* of a malformed key, the SDK will additionally log a runtime error. -*/ -LD_EXPORT(void) -LDServerDataSourcePollBuilder_Filter( - LDServerDataSourcePollBuilder b, - char const* filter_key); + * Sets the filter key for the polling connection. + * + * By default, the SDK is able to evaluate all flags in an environment. + * + * If this is undesirable - for example, because the environment contains + * thousands of flags, but this application only needs to evaluate + * a smaller, known subset - then a filter may be setup in LaunchDarkly, + * and the filter's key specified here. + * + * Evaluations for flags that aren't part of the filtered environment will + * return default values. + * + * @param b Polling method builder. Must not be NULL. + * @param filter_key The filter key. Must not be NULL. If the key is malformed + * or nonexistent, then a full LaunchDarkly environment will be fetched. In the + * case of a malformed key, the SDK will additionally log a runtime error. + */ +LD_EXPORT(void) +LDServerDataSourcePollBuilder_Filter(LDServerDataSourcePollBuilder b, + char const* filter_key); /** * Frees a Polling method builder. Do not call if the builder was consumed by @@ -383,8 +381,8 @@ LDServerConfigBuilder_HttpProperties_WrapperName(LDServerConfigBuilder b, */ LD_EXPORT(void) LDServerConfigBuilder_HttpProperties_WrapperVersion( - LDServerConfigBuilder b, - char const* wrapper_version); + LDServerConfigBuilder b, + char const* wrapper_version); /** * Set a custom header value. May be called more than once with additional @@ -406,8 +404,8 @@ LDServerConfigBuilder_HttpProperties_Header(LDServerConfigBuilder b, */ LD_EXPORT(void) LDServerConfigBuilder_HttpProperties_Tls( - LDServerConfigBuilder b, - LDServerHttpPropertiesTlsBuilder tls_builder); + LDServerConfigBuilder b, + LDServerHttpPropertiesTlsBuilder tls_builder); /** * Creates a new TLS options builder for the HttpProperties builder. @@ -441,8 +439,8 @@ LDServerHttpPropertiesTlsBuilder_Free(LDServerHttpPropertiesTlsBuilder b); */ LD_EXPORT(void) LDServerHttpPropertiesTlsBuilder_SkipVerifyPeer( - LDServerHttpPropertiesTlsBuilder b, - bool skip_verify_peer); + LDServerHttpPropertiesTlsBuilder b, + bool skip_verify_peer); /** * Configures TLS peer certificate verification to use a custom @@ -461,8 +459,8 @@ LDServerHttpPropertiesTlsBuilder_SkipVerifyPeer( */ LD_EXPORT(void) LDServerHttpPropertiesTlsBuilder_CustomCAFile( - LDServerHttpPropertiesTlsBuilder b, - char const* custom_ca_file); + LDServerHttpPropertiesTlsBuilder b, + char const* custom_ca_file); /** * Disables the default SDK logging. diff --git a/libs/server-sdk/src/bindings/c/builder.cpp b/libs/server-sdk/src/bindings/c/builder.cpp index 90a0f355b..655113720 100644 --- a/libs/server-sdk/src/bindings/c/builder.cpp +++ b/libs/server-sdk/src/bindings/c/builder.cpp @@ -76,7 +76,7 @@ LDServerConfigBuilder_Free(LDServerConfigBuilder builder) { LD_EXPORT(void) LDServerConfigBuilder_ServiceEndpoints_PollingBaseURL(LDServerConfigBuilder b, - char const* url) { + char const* url) { LD_ASSERT_NOT_NULL(b); LD_ASSERT_NOT_NULL(url); @@ -85,7 +85,7 @@ LDServerConfigBuilder_ServiceEndpoints_PollingBaseURL(LDServerConfigBuilder b, LD_EXPORT(void) LDServerConfigBuilder_ServiceEndpoints_StreamingBaseURL(LDServerConfigBuilder b, - char const* url) { + char const* url) { LD_ASSERT_NOT_NULL(b); LD_ASSERT_NOT_NULL(url); @@ -94,7 +94,7 @@ LDServerConfigBuilder_ServiceEndpoints_StreamingBaseURL(LDServerConfigBuilder b, LD_EXPORT(void) LDServerConfigBuilder_ServiceEndpoints_EventsBaseURL(LDServerConfigBuilder b, - char const* url) { + char const* url) { LD_ASSERT_NOT_NULL(b); LD_ASSERT_NOT_NULL(url); diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp index 0f2b5c5cb..fad6beb94 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp @@ -23,7 +23,8 @@ static char const* const kCouldNotParseEndpoint = "Could not parse polling endpoint URL"; static char const* const kInvalidFilterKey = - "Invalid payload filter configured on polling data source, full environment " + "Invalid payload filter configured on polling data source, full " + "environment " "will be fetched.\nEnsure the filter key is not empty and was copied " "correctly from LaunchDarkly settings"; @@ -64,22 +65,22 @@ PollingDataSource::PollingDataSource( data_components::DataSourceStatusManager& status_manager, config::built::ServiceEndpoints const& endpoints, config::built::BackgroundSyncConfig::PollingConfig const& - data_source_config, + data_source_config, config::built::HttpProperties const& http_properties) : logger_(logger), status_manager_(status_manager), requester_(ioc, http_properties.Tls()), polling_interval_(data_source_config.poll_interval), - request_(MakeRequest(logger_, data_source_config, endpoints, - http_properties)), + request_( + MakeRequest(logger_, data_source_config, endpoints, http_properties)), timer_(ioc), sink_(nullptr) { if (polling_interval_ < data_source_config.min_polling_interval) { LD_LOG(logger_, LogLevel::kWarn) << "Polling interval too frequent, defaulting to " << std::chrono::duration_cast( - data_source_config.min_polling_interval) - .count() + data_source_config.min_polling_interval) + .count() << " seconds"; polling_interval_ = data_source_config.min_polling_interval; @@ -249,4 +250,4 @@ void PollingDataSource::ShutdownAsync(std::function completion) { boost::asio::post(timer_.get_executor(), completion); } } -} // namespace launchdarkly::server_side::data_systems +} // namespace launchdarkly::server_side::data_systems diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp index 6ba637364..76a606ed4 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp @@ -17,11 +17,11 @@ static char const* const kCouldNotParseEndpoint = "Could not parse streaming endpoint URL"; static char const* const kInvalidFilterKey = - "Invalid payload filter configured on polling data source, full environment " + "Invalid payload filter configured on polling data source, full " + "environment " "will be fetched.\nEnsure the filter key is not empty and was copied " "correctly from LaunchDarkly settings"; - std::string const& StreamingDataSource::Identity() const { static std::string const identity = "streaming data source"; return identity; @@ -38,9 +38,8 @@ StreamingDataSource::StreamingDataSource( logger_(logger), status_manager_(status_manager), http_config_(http_properties), - streaming_config_(streaming), - streaming_endpoint_(endpoints.StreamingBaseUrl()) { -} + streaming_endpoint_(endpoints.StreamingBaseUrl()), + streaming_config_(streaming) {} void StreamingDataSource::StartAsync( data_interfaces::IDestination* dest, @@ -108,7 +107,7 @@ void StreamingDataSource::StartAsync( if (http_config_.Tls().PeerVerifyMode() == launchdarkly::config::shared::built::TlsOptions::VerifyMode:: - kVerifyNone) { + kVerifyNone) { client_builder.skip_verify_peer(true); } @@ -166,4 +165,4 @@ void StreamingDataSource::ShutdownAsync(std::function completion) { boost::asio::post(io_, completion); } } -} // namespace launchdarkly::server_side::data_systems +} // namespace launchdarkly::server_side::data_systems diff --git a/libs/server-sdk/tests/config_builder_test.cpp b/libs/server-sdk/tests/config_builder_test.cpp index 10b232dcd..7e4bb0631 100644 --- a/libs/server-sdk/tests/config_builder_test.cpp +++ b/libs/server-sdk/tests/config_builder_test.cpp @@ -11,15 +11,12 @@ using namespace launchdarkly; using namespace launchdarkly::server_side; using namespace launchdarkly::server_side::config; -class ConfigBuilderTest - : public ::testing:: - Test { +class ConfigBuilderTest : public ::testing::Test { // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) -protected: + protected: Logger logger; - ConfigBuilderTest() : logger(logging::NullLogger()) { - } + ConfigBuilderTest() : logger(logging::NullLogger()) {} }; TEST_F(ConfigBuilderTest, DefaultConstruction_Succeeds) { @@ -59,8 +56,7 @@ TEST_F(ConfigBuilderTest, CanSetStreamingPayloadFilterKey) { ConfigBuilder builder("sdk-123"); builder.DataSystem().Method( builders::DataSystemBuilder::BackgroundSync().Synchronizer( - builders::BackgroundSyncBuilder::Streaming().Filter( - "foo"))); + builders::BackgroundSyncBuilder::Streaming().Filter("foo"))); auto cfg = builder.Build(); @@ -85,8 +81,7 @@ TEST_F(ConfigBuilderTest, CanSetPollingPayloadFilterKey) { ConfigBuilder builder("sdk-123"); builder.DataSystem().Method( builders::DataSystemBuilder::BackgroundSync().Synchronizer( - builders::BackgroundSyncBuilder::Polling().Filter( - "foo"))); + builders::BackgroundSyncBuilder::Polling().Filter("foo"))); auto cfg = builder.Build(); @@ -112,8 +107,8 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_HttpPropertyDefaultsAreUsed) { auto cfg = builder.Build(); ASSERT_EQ(cfg->HttpProperties(), ::launchdarkly::config::shared::Defaults< - launchdarkly::config::shared::ServerSDK>::Defaults:: - HttpProperties()); + launchdarkly::config::shared::ServerSDK>::Defaults:: + HttpProperties()); } TEST_F(ConfigBuilderTest, DefaultConstruction_ServiceEndpointDefaultsAreUsed) { @@ -121,8 +116,8 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_ServiceEndpointDefaultsAreUsed) { auto cfg = builder.Build(); ASSERT_EQ(cfg->ServiceEndpoints(), ::launchdarkly::config::shared::Defaults< - launchdarkly::config::shared::ServerSDK>::Defaults:: - ServiceEndpoints()); + launchdarkly::config::shared::ServerSDK>::Defaults:: + ServiceEndpoints()); } TEST_F(ConfigBuilderTest, DefaultConstruction_EventDefaultsAreUsed) { @@ -130,7 +125,7 @@ TEST_F(ConfigBuilderTest, DefaultConstruction_EventDefaultsAreUsed) { auto cfg = builder.Build(); ASSERT_EQ(cfg->Events(), ::launchdarkly::config::shared::Defaults< - launchdarkly::config::shared::ServerSDK>::Defaults::Events()); + launchdarkly::config::shared::ServerSDK>::Defaults::Events()); } TEST_F(ConfigBuilderTest, CanDisableDataSystem) { diff --git a/libs/server-sdk/tests/server_c_bindings_test.cpp b/libs/server-sdk/tests/server_c_bindings_test.cpp index 4b7845836..54508ba20 100644 --- a/libs/server-sdk/tests/server_c_bindings_test.cpp +++ b/libs/server-sdk/tests/server_c_bindings_test.cpp @@ -24,7 +24,7 @@ TEST(ClientBindings, MinimalInstantiation) { char const* version = LDServerSDK_Version(); ASSERT_TRUE(version); - ASSERT_STREQ(version, "3.5.3"); // {x-release-please-version} + ASSERT_STREQ(version, "3.5.3"); // {x-release-please-version} LDServerSDK_Free(sdk); } @@ -46,7 +46,7 @@ TEST(ClientBindings, RegisterDataSourceStatusChangeListener) { LDServerSDK sdk = LDServerSDK_New(config); - struct LDServerDataSourceStatusListener listener{}; + struct LDServerDataSourceStatusListener listener {}; LDServerDataSourceStatusListener_Init(&listener); listener.UserData = const_cast("Potato"); @@ -115,7 +115,7 @@ TEST(ClientBindings, ComplexDataSourceStatus) { reinterpret_cast(&status))); EXPECT_EQ(200, LDServerDataSourceStatus_StateSince( - reinterpret_cast(&status))); + reinterpret_cast(&status))); LDDataSourceStatus_ErrorInfo info = LDServerDataSourceStatus_GetLastError( reinterpret_cast(&status)); @@ -188,12 +188,12 @@ TEST(ClientBindings, DoubleVariationPassesThroughDefault) { LDContext context = LDContextBuilder_Build(ctx_builder); std::string const flag = "weight"; - std::vector values = {0.0, 0.0001, 0.5, 1.234, + std::vector values = {0.0, 0.0001, 0.5, 1.234, 12.9, 13.211, 24.0, 1000.0}; for (auto const& v : values) { ASSERT_EQ(LDServerSDK_DoubleVariation(sdk, context, "weight", v), v); ASSERT_EQ(LDServerSDK_DoubleVariationDetail(sdk, context, "weight", v, - LD_DISCARD_DETAIL), + LD_DISCARD_DETAIL), v); } @@ -302,7 +302,6 @@ TEST(ClientBindings, TlsConfigurationSystemCAFile) { LDServerConfig_Free(config); } - TEST(ClientBindings, StreamingPayloadFilters) { LDServerConfigBuilder cfg_builder = LDServerConfigBuilder_New("sdk-123"); @@ -311,8 +310,8 @@ TEST(ClientBindings, StreamingPayloadFilters) { LDServerDataSourceStreamBuilder_Filter(stream_builder, "foo"); - LDServerConfigBuilder_DataSystem_BackgroundSync_Streaming( - cfg_builder, stream_builder); + LDServerConfigBuilder_DataSystem_BackgroundSync_Streaming(cfg_builder, + stream_builder); LDServerConfig config; LDStatus status = LDServerConfigBuilder_Build(cfg_builder, &config); @@ -329,8 +328,8 @@ TEST(ClientBindings, PollingPayloadFilters) { LDServerDataSourcePollBuilder_Filter(poll_builder, "foo"); - LDServerConfigBuilder_DataSystem_BackgroundSync_Polling( - cfg_builder, poll_builder); + LDServerConfigBuilder_DataSystem_BackgroundSync_Polling(cfg_builder, + poll_builder); LDServerConfig config; LDStatus status = LDServerConfigBuilder_Build(cfg_builder, &config); From c7c254cc6b1e60e3e733037d41947495ed3ab459 Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 14:06:07 -0700 Subject: [PATCH 8/9] linter noise --- .../server-contract-tests/src/entity_manager.cpp | 1 + contract-tests/server-contract-tests/src/main.cpp | 9 +++++---- .../config/shared/builders/data_source_builder.hpp | 1 + .../config/shared/built/data_source_config.hpp | 1 + .../launchdarkly/server_side/bindings/c/config/builder.h | 4 +--- .../payload_filter_validation.cpp | 5 ++--- .../sources/polling/polling_data_source.cpp | 2 ++ .../sources/streaming/streaming_data_source.cpp | 1 + 8 files changed, 14 insertions(+), 10 deletions(-) diff --git a/contract-tests/server-contract-tests/src/entity_manager.cpp b/contract-tests/server-contract-tests/src/entity_manager.cpp index 71e2d479e..6a9548a3b 100644 --- a/contract-tests/server-contract-tests/src/entity_manager.cpp +++ b/contract-tests/server-contract-tests/src/entity_manager.cpp @@ -112,6 +112,7 @@ std::optional EntityManager::create(ConfigParams const& in) { event_config.FlushInterval( std::chrono::milliseconds(*events.flushIntervalMs)); } + } else { event_config.Disable(); } diff --git a/contract-tests/server-contract-tests/src/main.cpp b/contract-tests/server-contract-tests/src/main.cpp index c4084d0a6..9ec539c7b 100644 --- a/contract-tests/server-contract-tests/src/main.cpp +++ b/contract-tests/server-contract-tests/src/main.cpp @@ -25,14 +25,14 @@ int main(int argc, char* argv[]) { std::string port = default_port; if (argc == 2) { port = - argv[1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + argv[1]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) } try { net::io_context ioc{1}; - auto p = boost::lexical_cast(port); - server srv(ioc, "0.0.0.0", p, logger); + auto const p = boost::lexical_cast(port); + server srv{ioc, "0.0.0.0", p, logger}; srv.add_capability("server-side"); srv.add_capability("strongly-typed"); @@ -57,11 +57,12 @@ int main(int argc, char* argv[]) { ioc.run(); LD_LOG(logger, LogLevel::kInfo) << "bye!"; + } catch (boost::bad_lexical_cast&) { LD_LOG(logger, LogLevel::kError) << "invalid port (" << port << "), provide a number (no arguments defaults " - "to port " + "to port " << default_port << ")"; return EXIT_FAILURE; } catch (std::exception const& e) { diff --git a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp index 9fffa74ce..a69bac6e1 100644 --- a/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp +++ b/libs/common/include/launchdarkly/config/shared/builders/data_source_builder.hpp @@ -201,4 +201,5 @@ class DataSourceBuilder { bool with_reasons_; bool use_report_; }; + } // namespace launchdarkly::config::shared::builders diff --git a/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp b/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp index 035e29c1d..32b9cc5b2 100644 --- a/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp +++ b/libs/common/include/launchdarkly/config/shared/built/data_source_config.hpp @@ -8,6 +8,7 @@ #include namespace launchdarkly::config::shared::built { + template struct StreamingConfig; diff --git a/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h b/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h index 9ac1a8cf1..3129a9f6f 100644 --- a/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h +++ b/libs/server-sdk/include/launchdarkly/server_side/bindings/c/config/builder.h @@ -14,9 +14,7 @@ #include #ifdef __cplusplus -extern "C" { -// only need to export C interface if -// used by C++ source code +extern "C" { // only need to export C interface if used by C++ source code #endif typedef struct _LDServerConfigBuilder* LDServerConfigBuilder; diff --git a/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp b/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp index 24df165b5..5d148b967 100644 --- a/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/detail/payload_filter_validation/payload_filter_validation.cpp @@ -9,8 +9,7 @@ bool ValidateFilterKey(std::string const& filter_key) { } try { return regex_search(filter_key, - boost::regex( - "^[a-zA-Z0-9][._\\-a-zA-Z0-9]*$")); + boost::regex("^[a-zA-Z0-9][._\\-a-zA-Z0-9]*$")); } catch (boost::bad_expression) { // boost::bad_expression can be thrown by basic_regex when compiling a // regular expression. @@ -21,4 +20,4 @@ bool ValidateFilterKey(std::string const& filter_key) { return false; } } -} // namespace launchdarkly::server_side::data_systems::detail +} // namespace launchdarkly::server_side::data_systems::detail diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp index fad6beb94..0a7713448 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp @@ -162,6 +162,7 @@ void PollingDataSource::HandlePollResult(network::HttpResult const& res) { DataSourceStatus::DataSourceState::kInterrupted, DataSourceStatus::ErrorInfo::ErrorKind::kUnknown, "polling response contained no body."); + } else if (res.Status() == 304) { // This should be handled ahead of here, but if we get a 304, // and it didn't have an etag, we still don't want to try to @@ -250,4 +251,5 @@ void PollingDataSource::ShutdownAsync(std::function completion) { boost::asio::post(timer_.get_executor(), completion); } } + } // namespace launchdarkly::server_side::data_systems diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp index 76a606ed4..e25c84e59 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp @@ -13,6 +13,7 @@ #include namespace launchdarkly::server_side::data_systems { + static char const* const kCouldNotParseEndpoint = "Could not parse streaming endpoint URL"; From 36946c3f7722ed66f4b9458da56a382432736f09 Mon Sep 17 00:00:00 2001 From: Casey Waldren Date: Tue, 3 Sep 2024 16:50:55 -0700 Subject: [PATCH 9/9] add a debug-level log when payload filters are being used --- .../background_sync/sources/polling/polling_data_source.cpp | 3 +++ .../sources/streaming/streaming_data_source.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp index 0a7713448..5b2b2db96 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/polling/polling_data_source.cpp @@ -40,6 +40,9 @@ static network::HttpRequest MakeRequest( if (polling_config.filter_key && url) { if (detail::ValidateFilterKey(*polling_config.filter_key)) { url->append("?filter=" + *polling_config.filter_key); + LD_LOG(logger, LogLevel::kDebug) + << "using payload filter '" << *polling_config.filter_key + << "'"; } else { LD_LOG(logger, LogLevel::kError) << kInvalidFilterKey; } diff --git a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp index e25c84e59..576c051ea 100644 --- a/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp +++ b/libs/server-sdk/src/data_systems/background_sync/sources/streaming/streaming_data_source.cpp @@ -57,6 +57,9 @@ void StreamingDataSource::StartAsync( if (streaming_config_.filter_key && updated_url) { if (detail::ValidateFilterKey(*streaming_config_.filter_key)) { updated_url->append("?filter=" + *streaming_config_.filter_key); + LD_LOG(logger_, LogLevel::kDebug) + << "using payload filter '" << *streaming_config_.filter_key + << "'"; } else { LD_LOG(logger_, LogLevel::kError) << kInvalidFilterKey; }