Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions libs/internal/include/launchdarkly/data_model/fdv2_change.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <launchdarkly/data_model/flag.hpp>
#include <launchdarkly/data_model/item_descriptor.hpp>
#include <launchdarkly/data_model/segment.hpp>
#include <launchdarkly/data_model/selector.hpp>

#include <string>
#include <variant>
#include <vector>

namespace launchdarkly::data_model {

struct FDv2Change {
std::string key;
std::variant<ItemDescriptor<Flag>, ItemDescriptor<Segment>> object;
};

struct FDv2ChangeSet {
enum class Type {
kFull = 0,
kPartial = 1,
kNone = 2,
};

Type type;
std::vector<FDv2Change> changes;
Selector selector;
};

} // namespace launchdarkly::data_model
29 changes: 29 additions & 0 deletions libs/internal/include/launchdarkly/data_model/selector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <cstdint>
#include <optional>
#include <string>

namespace launchdarkly::data_model {

/**
* Identifies a specific version of data in the LaunchDarkly backend, used to
* request incremental updates from a known point. <p> A selector is either
* empty or contains a version number and state string that
* were provided by a LaunchDarkly data source. Empty selectors signal that the
* client has no existing data and requires a full payload. <p> <strong>For SDK
* consumers implementing custom data sources:</strong> you should always use
* std::nullopt when constructing a ChangeSet. Non-empty selectors are
* set by LaunchDarkly's own data sources based on state received from the
* LaunchDarkly backend, and are not meaningful when constructed externally.
*/
struct Selector {
struct State {
std::int64_t version;
std::string state;
};

std::optional<State> value;
};

} // namespace launchdarkly::data_model
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#pragma once

#include <launchdarkly/detail/serialization/json_errors.hpp>

#include <boost/json/value.hpp>
#include <tl/expected.hpp>

#include <cstdint>
#include <optional>
#include <string>
#include <vector>

namespace launchdarkly {

enum class IntentCode { kNone, kTransferFull, kTransferChanges, kUnknown };

struct ServerIntentPayload {
std::string id;
std::int64_t target;
IntentCode intent_code;
std::optional<std::string> reason;
};

struct ServerIntent {
std::vector<ServerIntentPayload> payloads;
};

struct PutObject {
std::int64_t version;
std::string kind;
std::string key;
boost::json::value object;
};

struct DeleteObject {
std::int64_t version;
std::string kind;
std::string key;
};

struct PayloadTransferred {
std::string state;
std::int64_t version;
};

struct Goodbye {
std::optional<std::string> reason;
};

struct FDv2Error {
std::optional<std::string> id;
std::string reason;
};

tl::expected<std::optional<IntentCode>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<IntentCode>, JsonError>> const& unused,
boost::json::value const& json_value);

tl::expected<std::optional<ServerIntentPayload>, JsonError> tag_invoke(
boost::json::value_to_tag<tl::expected<std::optional<ServerIntentPayload>,
JsonError>> const& unused,
boost::json::value const& json_value);

tl::expected<std::optional<ServerIntent>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<ServerIntent>, JsonError>> const& unused,
boost::json::value const& json_value);

tl::expected<std::optional<PutObject>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<PutObject>, JsonError>> const& unused,
boost::json::value const& json_value);

tl::expected<std::optional<DeleteObject>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<DeleteObject>, JsonError>> const& unused,
boost::json::value const& json_value);

tl::expected<std::optional<PayloadTransferred>, JsonError> tag_invoke(
boost::json::value_to_tag<tl::expected<std::optional<PayloadTransferred>,
JsonError>> const& unused,
boost::json::value const& json_value);

tl::expected<std::optional<Goodbye>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<Goodbye>, JsonError>> const& unused,
boost::json::value const& json_value);

tl::expected<std::optional<FDv2Error>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<FDv2Error>, JsonError>> const& unused,
boost::json::value const& json_value);

} // namespace launchdarkly
1 change: 1 addition & 0 deletions libs/internal/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ set(INTERNAL_SOURCES
serialization/json_evaluation_reason.cpp
serialization/value_mapping.cpp
serialization/json_evaluation_result.cpp
serialization/json_fdv2_events.cpp
serialization/json_sdk_data_set.cpp
serialization/json_segment.cpp
serialization/json_primitives.cpp
Expand Down
156 changes: 156 additions & 0 deletions libs/internal/src/serialization/json_fdv2_events.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#include <boost/core/ignore_unused.hpp>
#include <boost/json.hpp>
#include <launchdarkly/serialization/json_fdv2_events.hpp>
#include <launchdarkly/serialization/value_mapping.hpp>
#include <tl/expected.hpp>

namespace launchdarkly {

tl::expected<std::optional<IntentCode>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<IntentCode>, JsonError>> const& unused,
boost::json::value const& json_value) {
boost::ignore_unused(unused);

REQUIRE_STRING(json_value);

auto const& str = json_value.as_string();
if (str == "none") {
return IntentCode::kNone;
} else if (str == "xfer-full") {
return IntentCode::kTransferFull;
} else if (str == "xfer-changes") {
return IntentCode::kTransferChanges;
} else {
return IntentCode::kUnknown;
}
}

tl::expected<std::optional<ServerIntentPayload>, JsonError> tag_invoke(
boost::json::value_to_tag<tl::expected<std::optional<ServerIntentPayload>,
JsonError>> const& unused,
boost::json::value const& json_value) {
boost::ignore_unused(unused);

REQUIRE_OBJECT(json_value);
auto const& obj = json_value.as_object();

ServerIntentPayload payload{};

PARSE_REQUIRED_FIELD(payload.id, obj, "id");
PARSE_REQUIRED_FIELD(payload.target, obj, "target");
PARSE_REQUIRED_FIELD(payload.intent_code, obj, "intentCode");
PARSE_CONDITIONAL_FIELD(payload.reason, obj, "reason");

return payload;
}

tl::expected<std::optional<ServerIntent>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<ServerIntent>, JsonError>> const& unused,
boost::json::value const& json_value) {
boost::ignore_unused(unused);

REQUIRE_OBJECT(json_value);
auto const& obj = json_value.as_object();

ServerIntent intent{};

PARSE_REQUIRED_FIELD(intent.payloads, obj, "payloads");

return intent;
}

tl::expected<std::optional<PutObject>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<PutObject>, JsonError>> const& unused,
boost::json::value const& json_value) {
boost::ignore_unused(unused);

REQUIRE_OBJECT(json_value);
auto const& obj = json_value.as_object();

PutObject put{};

PARSE_REQUIRED_FIELD(put.version, obj, "version");
PARSE_REQUIRED_FIELD(put.kind, obj, "kind");
PARSE_REQUIRED_FIELD(put.key, obj, "key");

auto const& it = obj.find("object");
if (it == obj.end()) {
return tl::make_unexpected(JsonError::kSchemaFailure);
}
put.object = it->value();

return put;
}

tl::expected<std::optional<DeleteObject>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<DeleteObject>, JsonError>> const& unused,
boost::json::value const& json_value) {
boost::ignore_unused(unused);

REQUIRE_OBJECT(json_value);
auto const& obj = json_value.as_object();

DeleteObject del{};

PARSE_REQUIRED_FIELD(del.version, obj, "version");
PARSE_REQUIRED_FIELD(del.kind, obj, "kind");
PARSE_REQUIRED_FIELD(del.key, obj, "key");

return del;
}

tl::expected<std::optional<PayloadTransferred>, JsonError> tag_invoke(
boost::json::value_to_tag<tl::expected<std::optional<PayloadTransferred>,
JsonError>> const& unused,
boost::json::value const& json_value) {
boost::ignore_unused(unused);

REQUIRE_OBJECT(json_value);
auto const& obj = json_value.as_object();

PayloadTransferred transferred{};

PARSE_REQUIRED_FIELD(transferred.state, obj, "state");
PARSE_REQUIRED_FIELD(transferred.version, obj, "version");

return transferred;
}

tl::expected<std::optional<Goodbye>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<Goodbye>, JsonError>> const& unused,
boost::json::value const& json_value) {
boost::ignore_unused(unused);

REQUIRE_OBJECT(json_value);
auto const& obj = json_value.as_object();

Goodbye goodbye{};

PARSE_CONDITIONAL_FIELD(goodbye.reason, obj, "reason");

return goodbye;
}

tl::expected<std::optional<FDv2Error>, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<std::optional<FDv2Error>, JsonError>> const& unused,
boost::json::value const& json_value) {
boost::ignore_unused(unused);

REQUIRE_OBJECT(json_value);
auto const& obj = json_value.as_object();

FDv2Error error{};

PARSE_REQUIRED_FIELD(error.reason, obj, "reason");
PARSE_CONDITIONAL_FIELD(error.id, obj, "id");

return error;
}

} // namespace launchdarkly
Loading
Loading