Skip to content
Merged
18 changes: 16 additions & 2 deletions apps/hello-cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <boost/asio/io_context.hpp>

#include "config/detail/builders/data_source_builder.hpp"
#include "console_backend.hpp"
#include "context_builder.hpp"
#include "launchdarkly/client_side/data_sources/detail/streaming_data_source.hpp"
Expand All @@ -17,6 +18,7 @@ using launchdarkly::Logger;
using launchdarkly::LogLevel;
using launchdarkly::client_side::Client;
using launchdarkly::client_side::ConfigBuilder;
using launchdarkly::client_side::DataSourceBuilder;
using launchdarkly::client_side::flag_manager::detail::FlagManager;
using launchdarkly::client_side::flag_manager::detail::FlagUpdater;

Expand All @@ -31,11 +33,23 @@ int main() {
return 1;
}

Client client(ConfigBuilder(key).Build().value(),
ContextBuilder().kind("user", "ryan").build());
Client client(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made this use polling for now. So I could watch it poll.

ConfigBuilder(key)
.DataSource(DataSourceBuilder()
.Method(DataSourceBuilder::Polling().PollInterval(
std::chrono::seconds{30}))
.WithReasons(true)
.UseReport(true))
.Build()
.value(),
ContextBuilder().kind("user", "ryan").build());

client.WaitForReadySync(std::chrono::seconds(30));

auto value = client.BoolVariation("my-boolean-flag", false);
LD_LOG(logger, LogLevel::kInfo) << "Value was: " << value;

// Sit around.
std::cout << "Press enter to exit" << std::endl;
std::cin.get();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "launchdarkly/client_side/connection.hpp"
#include "launchdarkly/client_side/data_sources/data_source_status.hpp"
#include "network/detail/http_requester.hpp"

namespace launchdarkly::client_side::data_sources {

Expand Down Expand Up @@ -79,7 +80,7 @@ class DataSourceStatus {
*/
class ErrorInfo {
public:
using StatusCodeType = int32_t;
using StatusCodeType = network::detail::HttpResult::StatusCode;

/**
* An enumeration describing the general type of an error.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@
#include "launchdarkly/client_side/data_source.hpp"
#include "launchdarkly/client_side/data_source_update_sink.hpp"
#include "launchdarkly/client_side/data_sources/detail/data_source_status_manager.hpp"
#include "launchdarkly/sse/client.hpp"
#include "logger.hpp"

namespace launchdarkly::client_side::data_sources::detail {

/**
* This class handles events source events, parses them, and then uses
* This class handles LaunchDarkly events, parses them, and then uses
* a IDataSourceUpdateSink to process the parsed events.
*
* This can be used for streaming or for polling. For polling only "put" events
* will be used.
*/
class StreamingDataHandler {
class DataSourceEventHandler {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used by both polling and streaming.

public:
/**
* Status indicating if the message was processed, or if there
Expand Down Expand Up @@ -45,16 +47,18 @@ class StreamingDataHandler {
uint64_t version;
};

StreamingDataHandler(IDataSourceUpdateSink* handler,
Logger const& logger,
DataSourceStatusManager& status_manager);
DataSourceEventHandler(IDataSourceUpdateSink* handler,
Logger const& logger,
DataSourceStatusManager& status_manager);

/**
* Handle an SSE event.
* @param event The event to handle.
* Handles an event from the LaunchDarkly service.
* @param type The type of the event. "put"/"patch"/"delete".
* @param data The content of the evnet.
* @return A status indicating if the message could be handled.
*/
MessageStatus HandleMessage(launchdarkly::sse::Event const& event);
MessageStatus HandleMessage(std::string const& type,
std::string const& data);

private:
IDataSourceUpdateSink* handler_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,30 @@ class DataSourceStatusManager : public IDataSourceStatusProvider {
*/
void SetState(DataSourceStatus::DataSourceState state);

/**
* If an error and state change happen simultaneously, then they should
* be updated simultaneously.
*
* @param state The new state.
* @param code Status code for an http error.
* @param message The message to associate with the error.
*/
void SetState(DataSourceStatus::DataSourceState state,
DataSourceStatus::ErrorInfo::StatusCodeType code,
std::string message);

/**
* If an error and state change happen simultaneously, then they should
* be updated simultaneously.
*
* @param state The new state.
* @param kind The error kind.
* @param message The message to associate with the error.
*/
void SetState(DataSourceStatus::DataSourceState state,
DataSourceStatus::ErrorInfo::ErrorKind kind,
std::string message);

/**
* Set an error with the given kind and message.
*
Expand All @@ -42,7 +66,8 @@ class DataSourceStatusManager : public IDataSourceStatusProvider {
* Set an error based on the given status code.
* @param code The status code of the error.
*/
void SetError(DataSourceStatus::ErrorInfo::StatusCodeType code);
void SetError(DataSourceStatus::ErrorInfo::StatusCodeType code,
std::string message);
// TODO: Handle error codes once the EventSource supports it.

DataSourceStatus Status() override;
Expand All @@ -61,6 +86,7 @@ class DataSourceStatusManager : public IDataSourceStatusProvider {
boost::signals2::signal<void(data_sources::DataSourceStatus status)>
data_source_status_signal_;
mutable std::mutex status_mutex_;
bool UpdateState(DataSourceStatus::DataSourceState const& requested_state);
};

} // namespace launchdarkly::client_side::data_sources::detail
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

#include <chrono>

#include <boost/asio/any_io_executor.hpp>

#include "config/client.hpp"
#include "config/detail/built/http_properties.hpp"
#include "data_source_status_manager.hpp"
#include "launchdarkly/client_side/data_source.hpp"
#include "launchdarkly/client_side/data_source_update_sink.hpp"
#include "launchdarkly/client_side/data_sources/detail/data_source_event_handler.hpp"
#include "logger.hpp"
#include "network/detail/asio_requester.hpp"

namespace launchdarkly::client_side::data_sources::detail {

class PollingDataSource : public IDataSource {
public:
PollingDataSource(Config const& config,
boost::asio::any_io_executor ioc,
Context const& context,
IDataSourceUpdateSink* handler,
DataSourceStatusManager& status_manager,
Logger const& logger);

void Start() override;
void Close() override;

private:
void DoPoll();

std::string string_context_;
DataSourceStatusManager& status_manager_;
DataSourceEventHandler data_source_handler_;
std::string polling_endpoint_;

network::detail::AsioRequester requester_;
Logger const& logger_;
boost::asio::any_io_executor ioc_;
std::chrono::seconds polling_interval_;
network::detail::HttpRequest request_;
std::optional<std::string> etag_;

boost::asio::steady_timer timer_;
std::chrono::time_point<std::chrono::system_clock> last_poll_start_;

void StartPollingTimer();
};

} // namespace launchdarkly::client_side::data_sources::detail
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,39 @@ using namespace std::chrono_literals;

#include <boost/asio/any_io_executor.hpp>

#include "config/client.hpp"
#include "config/detail/built/http_properties.hpp"
#include "config/detail/built/service_endpoints.hpp"
#include "context.hpp"
#include "data/evaluation_result.hpp"
#include "launchdarkly/client_side/data_source.hpp"
#include "launchdarkly/client_side/data_source_update_sink.hpp"
#include "launchdarkly/client_side/data_sources/detail/data_source_event_handler.hpp"
#include "launchdarkly/client_side/data_sources/detail/data_source_status_manager.hpp"
#include "launchdarkly/client_side/data_sources/detail/streaming_data_handler.hpp"
#include "launchdarkly/sse/client.hpp"
#include "logger.hpp"

namespace launchdarkly::client_side::data_sources::detail {

class StreamingDataSource final : public IDataSource {
public:
StreamingDataSource(
std::string const& sdk_key,
boost::asio::any_io_executor ioc,
Context const& context,
config::detail::built::ServiceEndpoints const& endpoints,
config::detail::built::HttpProperties const& http_properties,
bool use_report,
bool with_reasons,
IDataSourceUpdateSink* handler,
DataSourceStatusManager& status_manager,
Logger const& logger);
StreamingDataSource(Config const& config,
boost::asio::any_io_executor ioc,
Context const& context,
IDataSourceUpdateSink* handler,
DataSourceStatusManager& status_manager,
Logger const& logger);

void Start() override;
void Close() override;

private:
DataSourceStatusManager& status_manager_;
StreamingDataHandler data_source_handler_;
DataSourceEventHandler data_source_handler_;
std::string streaming_endpoint_;
std::string string_context_;

Logger const& logger_;
std::shared_ptr<launchdarkly::sse::Client> client_;

inline static const std::string streaming_path_ = "/meval";
};
} // namespace launchdarkly::client_side::data_sources::detail
3 changes: 2 additions & 1 deletion libs/client-sdk/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ add_library(${LIBNAME}
${HEADER_LIST}
data_sources/streaming_data_source.cpp
data_sources/base_64.cpp
data_sources/streaming_data_handler.cpp
data_sources/data_source_event_handler.cpp
data_source_update_sink.cpp
data_sources/polling_data_source.cpp
flag_manager/flag_manager.cpp
flag_manager/flag_updater.cpp
flag_manager/flag_change_event.cpp
Expand Down
39 changes: 26 additions & 13 deletions libs/client-sdk/src/api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,32 @@
#include <utility>

#include "events/detail/asio_event_processor.hpp"
#include "launchdarkly/client_side/data_sources/detail/polling_data_source.hpp"
#include "launchdarkly/client_side/data_sources/detail/streaming_data_source.hpp"

namespace launchdarkly::client_side {

using launchdarkly::client_side::data_sources::DataSourceStatus;

static std::unique_ptr<IDataSource> MakeDataSource(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we have file and test data sources we may need to scale this differently.

Config const& config,
Context const& context,
boost::asio::any_io_executor executor,
flag_manager::detail::FlagUpdater& flag_updater,
data_sources::detail::DataSourceStatusManager& status_manager,
Logger& logger) {
if (config.DataSourceConfig().method.which() == 0) {
// TODO: use initial reconnect delay.
return std::make_unique<launchdarkly::client_side::data_sources::
detail::StreamingDataSource>(
config, executor, context, &flag_updater, status_manager, logger);
} else {
return std::make_unique<
launchdarkly::client_side::data_sources::detail::PollingDataSource>(
config, executor, context, &flag_updater, status_manager, logger);
}
}

Client::Client(Config config, Context context)
: logger_(config.Logger()),
context_(std::move(context)),
Expand All @@ -21,19 +41,12 @@ Client::Client(Config config, Context context)
config.SdkKey(),
logger_)),
flag_updater_(flag_manager_),
// TODO: Support polling.
data_source_(std::make_unique<launchdarkly::client_side::data_sources::
detail::StreamingDataSource>(
config.SdkKey(),
ioc_.get_executor(),
context_,
config.ServiceEndpoints(),
config.HttpProperties(),
config.DataSourceConfig().use_report,
config.DataSourceConfig().with_reasons,
&flag_updater_,
status_manager_,
logger_)),
data_source_(MakeDataSource(config,
context_,
ioc_.get_executor(),
flag_updater_,
status_manager_,
logger_)),
initialized_(false) {
data_source_->Start();

Expand Down
Loading