Skip to content

Commit 7ef503b

Browse files
authored
feat: Implement polling data source. (#28)
1 parent 853d3ff commit 7ef503b

27 files changed

+741
-225
lines changed

apps/hello-cpp/main.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <boost/asio/io_context.hpp>
55

6+
#include "config/detail/builders/data_source_builder.hpp"
67
#include "console_backend.hpp"
78
#include "context_builder.hpp"
89
#include "launchdarkly/client_side/data_sources/detail/streaming_data_source.hpp"
@@ -17,6 +18,7 @@ using launchdarkly::Logger;
1718
using launchdarkly::LogLevel;
1819
using launchdarkly::client_side::Client;
1920
using launchdarkly::client_side::ConfigBuilder;
21+
using launchdarkly::client_side::DataSourceBuilder;
2022
using launchdarkly::client_side::flag_manager::detail::FlagManager;
2123
using launchdarkly::client_side::flag_manager::detail::FlagUpdater;
2224

@@ -31,11 +33,23 @@ int main() {
3133
return 1;
3234
}
3335

34-
Client client(ConfigBuilder(key).Build().value(),
35-
ContextBuilder().kind("user", "ryan").build());
36+
Client client(
37+
ConfigBuilder(key)
38+
.DataSource(DataSourceBuilder()
39+
.Method(DataSourceBuilder::Polling().PollInterval(
40+
std::chrono::seconds{30}))
41+
.WithReasons(true)
42+
.UseReport(true))
43+
.Build()
44+
.value(),
45+
ContextBuilder().kind("user", "ryan").build());
3646

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

3949
auto value = client.BoolVariation("my-boolean-flag", false);
4050
LD_LOG(logger, LogLevel::kInfo) << "Value was: " << value;
51+
52+
// Sit around.
53+
std::cout << "Press enter to exit" << std::endl;
54+
std::cin.get();
4155
}

libs/client-sdk/include/launchdarkly/client_side/data_sources/data_source_status.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "launchdarkly/client_side/connection.hpp"
1212
#include "launchdarkly/client_side/data_sources/data_source_status.hpp"
13+
#include "network/detail/http_requester.hpp"
1314

1415
namespace launchdarkly::client_side::data_sources {
1516

@@ -79,7 +80,7 @@ class DataSourceStatus {
7980
*/
8081
class ErrorInfo {
8182
public:
82-
using StatusCodeType = int32_t;
83+
using StatusCodeType = network::detail::HttpResult::StatusCode;
8384

8485
/**
8586
* An enumeration describing the general type of an error.

libs/client-sdk/include/launchdarkly/client_side/data_sources/detail/streaming_data_handler.hpp renamed to libs/client-sdk/include/launchdarkly/client_side/data_sources/detail/data_source_event_handler.hpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@
88
#include "launchdarkly/client_side/data_source.hpp"
99
#include "launchdarkly/client_side/data_source_update_sink.hpp"
1010
#include "launchdarkly/client_side/data_sources/detail/data_source_status_manager.hpp"
11-
#include "launchdarkly/sse/client.hpp"
1211
#include "logger.hpp"
1312

1413
namespace launchdarkly::client_side::data_sources::detail {
1514

1615
/**
17-
* This class handles events source events, parses them, and then uses
16+
* This class handles LaunchDarkly events, parses them, and then uses
1817
* a IDataSourceUpdateSink to process the parsed events.
18+
*
19+
* This can be used for streaming or for polling. For polling only "put" events
20+
* will be used.
1921
*/
20-
class StreamingDataHandler {
22+
class DataSourceEventHandler {
2123
public:
2224
/**
2325
* Status indicating if the message was processed, or if there
@@ -45,16 +47,18 @@ class StreamingDataHandler {
4547
uint64_t version;
4648
};
4749

48-
StreamingDataHandler(IDataSourceUpdateSink* handler,
49-
Logger const& logger,
50-
DataSourceStatusManager& status_manager);
50+
DataSourceEventHandler(IDataSourceUpdateSink* handler,
51+
Logger const& logger,
52+
DataSourceStatusManager& status_manager);
5153

5254
/**
53-
* Handle an SSE event.
54-
* @param event The event to handle.
55+
* Handles an event from the LaunchDarkly service.
56+
* @param type The type of the event. "put"/"patch"/"delete".
57+
* @param data The content of the evnet.
5558
* @return A status indicating if the message could be handled.
5659
*/
57-
MessageStatus HandleMessage(launchdarkly::sse::Event const& event);
60+
MessageStatus HandleMessage(std::string const& type,
61+
std::string const& data);
5862

5963
private:
6064
IDataSourceUpdateSink* handler_;

libs/client-sdk/include/launchdarkly/client_side/data_sources/detail/data_source_status_manager.hpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,30 @@ class DataSourceStatusManager : public IDataSourceStatusProvider {
2525
*/
2626
void SetState(DataSourceStatus::DataSourceState state);
2727

28+
/**
29+
* If an error and state change happen simultaneously, then they should
30+
* be updated simultaneously.
31+
*
32+
* @param state The new state.
33+
* @param code Status code for an http error.
34+
* @param message The message to associate with the error.
35+
*/
36+
void SetState(DataSourceStatus::DataSourceState state,
37+
DataSourceStatus::ErrorInfo::StatusCodeType code,
38+
std::string message);
39+
40+
/**
41+
* If an error and state change happen simultaneously, then they should
42+
* be updated simultaneously.
43+
*
44+
* @param state The new state.
45+
* @param kind The error kind.
46+
* @param message The message to associate with the error.
47+
*/
48+
void SetState(DataSourceStatus::DataSourceState state,
49+
DataSourceStatus::ErrorInfo::ErrorKind kind,
50+
std::string message);
51+
2852
/**
2953
* Set an error with the given kind and message.
3054
*
@@ -42,7 +66,8 @@ class DataSourceStatusManager : public IDataSourceStatusProvider {
4266
* Set an error based on the given status code.
4367
* @param code The status code of the error.
4468
*/
45-
void SetError(DataSourceStatus::ErrorInfo::StatusCodeType code);
69+
void SetError(DataSourceStatus::ErrorInfo::StatusCodeType code,
70+
std::string message);
4671
// TODO: Handle error codes once the EventSource supports it.
4772

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

6692
} // namespace launchdarkly::client_side::data_sources::detail
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#pragma once
2+
3+
#include <chrono>
4+
5+
#include <boost/asio/any_io_executor.hpp>
6+
7+
#include "config/client.hpp"
8+
#include "config/detail/built/http_properties.hpp"
9+
#include "data_source_status_manager.hpp"
10+
#include "launchdarkly/client_side/data_source.hpp"
11+
#include "launchdarkly/client_side/data_source_update_sink.hpp"
12+
#include "launchdarkly/client_side/data_sources/detail/data_source_event_handler.hpp"
13+
#include "logger.hpp"
14+
#include "network/detail/asio_requester.hpp"
15+
16+
namespace launchdarkly::client_side::data_sources::detail {
17+
18+
class PollingDataSource : public IDataSource {
19+
public:
20+
PollingDataSource(Config const& config,
21+
boost::asio::any_io_executor ioc,
22+
Context const& context,
23+
IDataSourceUpdateSink* handler,
24+
DataSourceStatusManager& status_manager,
25+
Logger const& logger);
26+
27+
void Start() override;
28+
void Close() override;
29+
30+
private:
31+
void DoPoll();
32+
33+
std::string string_context_;
34+
DataSourceStatusManager& status_manager_;
35+
DataSourceEventHandler data_source_handler_;
36+
std::string polling_endpoint_;
37+
38+
network::detail::AsioRequester requester_;
39+
Logger const& logger_;
40+
boost::asio::any_io_executor ioc_;
41+
std::chrono::seconds polling_interval_;
42+
network::detail::HttpRequest request_;
43+
std::optional<std::string> etag_;
44+
45+
boost::asio::steady_timer timer_;
46+
std::chrono::time_point<std::chrono::system_clock> last_poll_start_;
47+
48+
void StartPollingTimer();
49+
};
50+
51+
} // namespace launchdarkly::client_side::data_sources::detail

libs/client-sdk/include/launchdarkly/client_side/data_sources/detail/streaming_data_source.hpp

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,39 @@ using namespace std::chrono_literals;
55

66
#include <boost/asio/any_io_executor.hpp>
77

8+
#include "config/client.hpp"
89
#include "config/detail/built/http_properties.hpp"
910
#include "config/detail/built/service_endpoints.hpp"
1011
#include "context.hpp"
1112
#include "data/evaluation_result.hpp"
1213
#include "launchdarkly/client_side/data_source.hpp"
1314
#include "launchdarkly/client_side/data_source_update_sink.hpp"
15+
#include "launchdarkly/client_side/data_sources/detail/data_source_event_handler.hpp"
1416
#include "launchdarkly/client_side/data_sources/detail/data_source_status_manager.hpp"
15-
#include "launchdarkly/client_side/data_sources/detail/streaming_data_handler.hpp"
1617
#include "launchdarkly/sse/client.hpp"
1718
#include "logger.hpp"
1819

1920
namespace launchdarkly::client_side::data_sources::detail {
2021

2122
class StreamingDataSource final : public IDataSource {
2223
public:
23-
StreamingDataSource(
24-
std::string const& sdk_key,
25-
boost::asio::any_io_executor ioc,
26-
Context const& context,
27-
config::detail::built::ServiceEndpoints const& endpoints,
28-
config::detail::built::HttpProperties const& http_properties,
29-
bool use_report,
30-
bool with_reasons,
31-
IDataSourceUpdateSink* handler,
32-
DataSourceStatusManager& status_manager,
33-
Logger const& logger);
24+
StreamingDataSource(Config const& config,
25+
boost::asio::any_io_executor ioc,
26+
Context const& context,
27+
IDataSourceUpdateSink* handler,
28+
DataSourceStatusManager& status_manager,
29+
Logger const& logger);
3430

3531
void Start() override;
3632
void Close() override;
3733

3834
private:
3935
DataSourceStatusManager& status_manager_;
40-
StreamingDataHandler data_source_handler_;
36+
DataSourceEventHandler data_source_handler_;
4137
std::string streaming_endpoint_;
4238
std::string string_context_;
4339

4440
Logger const& logger_;
4541
std::shared_ptr<launchdarkly::sse::Client> client_;
46-
47-
inline static const std::string streaming_path_ = "/meval";
4842
};
4943
} // namespace launchdarkly::client_side::data_sources::detail

libs/client-sdk/src/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ add_library(${LIBNAME}
1515
${HEADER_LIST}
1616
data_sources/streaming_data_source.cpp
1717
data_sources/base_64.cpp
18-
data_sources/streaming_data_handler.cpp
18+
data_sources/data_source_event_handler.cpp
1919
data_source_update_sink.cpp
20+
data_sources/polling_data_source.cpp
2021
flag_manager/flag_manager.cpp
2122
flag_manager/flag_updater.cpp
2223
flag_manager/flag_change_event.cpp

libs/client-sdk/src/api.cpp

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,32 @@
44
#include <utility>
55

66
#include "events/detail/asio_event_processor.hpp"
7+
#include "launchdarkly/client_side/data_sources/detail/polling_data_source.hpp"
78
#include "launchdarkly/client_side/data_sources/detail/streaming_data_source.hpp"
89

910
namespace launchdarkly::client_side {
1011

1112
using launchdarkly::client_side::data_sources::DataSourceStatus;
1213

14+
static std::unique_ptr<IDataSource> MakeDataSource(
15+
Config const& config,
16+
Context const& context,
17+
boost::asio::any_io_executor executor,
18+
flag_manager::detail::FlagUpdater& flag_updater,
19+
data_sources::detail::DataSourceStatusManager& status_manager,
20+
Logger& logger) {
21+
if (config.DataSourceConfig().method.which() == 0) {
22+
// TODO: use initial reconnect delay.
23+
return std::make_unique<launchdarkly::client_side::data_sources::
24+
detail::StreamingDataSource>(
25+
config, executor, context, &flag_updater, status_manager, logger);
26+
} else {
27+
return std::make_unique<
28+
launchdarkly::client_side::data_sources::detail::PollingDataSource>(
29+
config, executor, context, &flag_updater, status_manager, logger);
30+
}
31+
}
32+
1333
Client::Client(Config config, Context context)
1434
: logger_(config.Logger()),
1535
context_(std::move(context)),
@@ -21,19 +41,12 @@ Client::Client(Config config, Context context)
2141
config.SdkKey(),
2242
logger_)),
2343
flag_updater_(flag_manager_),
24-
// TODO: Support polling.
25-
data_source_(std::make_unique<launchdarkly::client_side::data_sources::
26-
detail::StreamingDataSource>(
27-
config.SdkKey(),
28-
ioc_.get_executor(),
29-
context_,
30-
config.ServiceEndpoints(),
31-
config.HttpProperties(),
32-
config.DataSourceConfig().use_report,
33-
config.DataSourceConfig().with_reasons,
34-
&flag_updater_,
35-
status_manager_,
36-
logger_)),
44+
data_source_(MakeDataSource(config,
45+
context_,
46+
ioc_.get_executor(),
47+
flag_updater_,
48+
status_manager_,
49+
logger_)),
3750
initialized_(false) {
3851
data_source_->Start();
3952

0 commit comments

Comments
 (0)