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
4 changes: 3 additions & 1 deletion apps/hello-cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ int main() {

auto value = client.BoolVariationDetail("my-bool-flag", false);
LD_LOG(logger, LogLevel::kInfo) << "Value was: " << *value;
LD_LOG(logger, LogLevel::kInfo) << "Reason was: " << value.Reason();
if (auto reason = value.Reason()) {
LD_LOG(logger, LogLevel::kInfo) << "Reason was: " << *reason;
}

// Sit around.
std::cout << "Press enter to exit" << std::endl;
Expand Down
17 changes: 7 additions & 10 deletions libs/client-sdk/src/api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ EvaluationDetail<T> Client::VariationInternal(FlagKey const& key,
"Returning default value";

// TODO: SC-199918
auto error_reason = EvaluationReason("CLIENT_NOT_READY");
auto error_reason =
EvaluationReason(EvaluationReason::ErrorKind::kClientNotReady);
if (eval_reasons_available_) {
event.reason = error_reason;
}
Expand All @@ -147,7 +148,8 @@ EvaluationDetail<T> Client::VariationInternal(FlagKey const& key,
LD_LOG(logger_, LogLevel::kInfo)
<< "Unknown feature flag " << key << "; returning default value";

auto error_reason = EvaluationReason("FLAG_NOT_FOUND");
auto error_reason =
EvaluationReason(EvaluationReason::ErrorKind::kFlagNotFound);
if (eval_reasons_available_) {
event.reason = error_reason;
}
Expand All @@ -168,7 +170,8 @@ EvaluationDetail<T> Client::VariationInternal(FlagKey const& key,

if (check_type && default_value.type() != Value::Type::kNull &&
detail.value().type() != default_value.type()) {
auto error_reason = EvaluationReason("WRONG_TYPE");
auto error_reason =
EvaluationReason(EvaluationReason::ErrorKind::kWrongType);
if (eval_reasons_available_) {
event.reason = error_reason;
}
Expand All @@ -191,14 +194,8 @@ EvaluationDetail<T> Client::VariationInternal(FlagKey const& key,

event_processor_->AsyncSend(std::move(event));

// TODO: this isn't a valid error, figure out how to handle if reason is
// missing.
EvaluationReason returned_reason("UNKNOWN");
if (detail.reason()) {
returned_reason = detail.reason()->get();
}
return EvaluationDetail<T>(detail.value(), detail.variation_index(),
returned_reason);
detail.reason());
}

EvaluationDetail<bool> Client::BoolVariationDetail(Client::FlagKey const& key,
Expand Down
8 changes: 4 additions & 4 deletions libs/common/include/data/evaluation_detail.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ class EvaluationDetail {
*/
EvaluationDetail(T value,
std::optional<std::size_t> variation_index,
EvaluationReason reason);
std::optional<EvaluationReason> reason);

/**
* Constructs an EvaluationDetail representing an error and a default
* value.
* @param error_kind Kind of the error.
* @param default_value Default value.
*/
EvaluationDetail(std::string error_kind, T default_value);
EvaluationDetail(EvaluationReason::ErrorKind error_kind, T default_value);

/**
* @return A reference to the variation value. For convenience, the *
Expand All @@ -50,7 +50,7 @@ class EvaluationDetail {
/**
* @return A reference to the reason for the results.
*/
[[nodiscard]] EvaluationReason const& Reason() const;
[[nodiscard]] std::optional<EvaluationReason> const& Reason() const;

/**
* @return A reference to the variation value.
Expand All @@ -60,6 +60,6 @@ class EvaluationDetail {
private:
T value_;
std::optional<std::size_t> variation_index_;
EvaluationReason reason_;
std::optional<EvaluationReason> reason_;
};
} // namespace launchdarkly
69 changes: 47 additions & 22 deletions libs/common/include/data/evaluation_reason.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,53 @@ namespace launchdarkly {
*/
class EvaluationReason {
public:
enum class Kind {
// The flag was off and therefore returned its configured off value.
kOff = 0,
// The flag was on but the context did not match any targets or rules.
kFallthrough = 1,
// The context key was specifically targeted for this flag.
kTargetMatch = 2,
// The context matched one of the flag's rules.
kRuleMatch = 3,
// The flag was considered off because it had at least one prerequisite
// flag that either was off or did not return the desired variation.
kPrerequisiteFailed = 4,
// The flag could not be evaluated, e.g. because it does not exist or
// due to an unexpected error.
kError = 5
};
friend std::ostream& operator<<(std::ostream& out, Kind const& kind);

enum class ErrorKind {
// The SDK was not yet fully initialized and cannot evaluate flags.
kClientNotReady = 0,
// The application did not pass valid context attributes to the SDK
// evaluation method.
kUserNotSpecified = 1,
// No flag existed with the specified flag key.
kFlagNotFound = 2,
// The application requested an evaluation result of one type but the
// resulting flag variation value was of a different type.
kWrongType = 3,
// The flag had invalid properties.
kMalformedFlag = 4,
// An unexpected error happened that stopped evaluation.
kException = 5,
};

friend std::ostream& operator<<(std::ostream& out, ErrorKind const& kind);

/**
* The general category of the reason:
*
* - `"OFF"`: The flag was off and therefore returned its configured off
* value.
* - `"FALLTHROUGH"`: The flag was on but the context did not match any
* targets or rules.
* - `"TARGET_MATCH"`: The context key was specifically targeted for this
* flag.
* - `"RULE_MATCH"`: the context matched one of the flag"s rules.
* - `"PREREQUISITE_FAILED"`: The flag was considered off because it had at
* least one prerequisite flag that either was off or did not return the
* desired variation.
* - `"ERROR"`: The flag could not be evaluated, e.g. because it does not
* exist or due to an unexpected error.
* @return The general category of the reason.
*/
[[nodiscard]] std::string const& kind() const;
[[nodiscard]] Kind const& kind() const;

/**
* A further description of the error condition, if the kind was `"ERROR"`.
* A further description of the error condition, if the Kind was
* Kind::kError.
*/
[[nodiscard]] std::optional<std::string> error_kind() const;
[[nodiscard]] std::optional<ErrorKind> error_kind() const;

/**
* The index of the matched rule (0 for the first), if the kind was
Expand Down Expand Up @@ -78,22 +103,22 @@ class EvaluationReason {
*/
[[nodiscard]] std::optional<std::string> big_segment_status() const;

EvaluationReason(std::string kind,
std::optional<std::string> error_kind,
EvaluationReason(Kind kind,
std::optional<ErrorKind> error_kind,
std::optional<std::size_t> rule_index,
std::optional<std::string> rule_id,
std::optional<std::string> prerequisite_key,
bool in_experiment,
std::optional<std::string> big_segment_status);

explicit EvaluationReason(std::string error_kind);
explicit EvaluationReason(ErrorKind error_kind);

friend std::ostream& operator<<(std::ostream& out,
EvaluationReason const& reason);

private:
std::string kind_;
std::optional<std::string> error_kind_;
Kind kind_;
std::optional<ErrorKind> error_kind_;
std::optional<std::size_t> rule_index_;
std::optional<std::string> rule_id_;
std::optional<std::string> prerequisite_key_;
Expand Down
18 changes: 18 additions & 0 deletions libs/common/include/serialization/json_evaluation_reason.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ tl::expected<EvaluationReason, JsonError> tag_invoke(
unused,
boost::json::value const& json_value);

tl::expected<EvaluationReason::Kind, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<EvaluationReason::Kind, JsonError>> const& unused,
boost::json::value const& json_value);

tl::expected<EvaluationReason::ErrorKind, JsonError> tag_invoke(
boost::json::value_to_tag<
tl::expected<EvaluationReason::ErrorKind, JsonError>> const& unused,
boost::json::value const& json_value);

void tag_invoke(boost::json::value_from_tag const& unused,
boost::json::value& json_value,
EvaluationReason::Kind const& kind);

void tag_invoke(boost::json::value_from_tag const& unused,
boost::json::value& json_value,
EvaluationReason::ErrorKind const& kind);

void tag_invoke(boost::json::value_from_tag const& unused,
boost::json::value& json_value,
EvaluationReason const& reason);
Expand Down
7 changes: 4 additions & 3 deletions libs/common/src/data/evaluation_detail.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ template <typename T>
EvaluationDetail<T>::EvaluationDetail(
T value,
std::optional<std::size_t> variation_index,
launchdarkly::EvaluationReason reason)
std::optional<launchdarkly::EvaluationReason> reason)
: value_(std::move(value)),
variation_index_(variation_index),
reason_(std::move(reason)) {}

template <typename T>
EvaluationDetail<T>::EvaluationDetail(std::string error_kind, T default_value)
EvaluationDetail<T>::EvaluationDetail(EvaluationReason::ErrorKind error_kind,
T default_value)
: value_(std::move(default_value)),
variation_index_(std::nullopt),
reason_(error_kind) {}
Expand All @@ -25,7 +26,7 @@ T const& EvaluationDetail<T>::Value() const {
}

template <typename T>
EvaluationReason const& EvaluationDetail<T>::Reason() const {
std::optional<EvaluationReason> const& EvaluationDetail<T>::Reason() const {
return reason_;
}

Expand Down
32 changes: 23 additions & 9 deletions libs/common/src/data/evaluation_reason.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#include "data/evaluation_reason.hpp"

#include "serialization/json_evaluation_reason.hpp"
namespace launchdarkly {

std::string const& EvaluationReason::kind() const {
EvaluationReason::Kind const& EvaluationReason::kind() const {
return kind_;
}

std::optional<std::string> EvaluationReason::error_kind() const {
std::optional<EvaluationReason::ErrorKind> EvaluationReason::error_kind()
const {
return error_kind_;
}

Expand All @@ -31,23 +32,23 @@ std::optional<std::string> EvaluationReason::big_segment_status() const {
}

EvaluationReason::EvaluationReason(
std::string kind,
std::optional<std::string> error_kind,
Kind kind,
std::optional<ErrorKind> error_kind,
std::optional<std::size_t> rule_index,
std::optional<std::string> rule_id,
std::optional<std::string> prerequisite_key,
bool in_experiment,
std::optional<std::string> big_segment_status)
: kind_(std::move(kind)),
error_kind_(std::move(error_kind)),
: kind_(kind),
error_kind_(error_kind),
rule_index_(rule_index),
rule_id_(std::move(rule_id)),
prerequisite_key_(std::move(prerequisite_key)),
in_experiment_(in_experiment),
big_segment_status_(std::move(big_segment_status)) {}

EvaluationReason::EvaluationReason(std::string error_kind)
: EvaluationReason("ERROR",
EvaluationReason::EvaluationReason(ErrorKind error_kind)
: EvaluationReason(Kind::kError,
error_kind,
std::nullopt,
std::nullopt,
Expand Down Expand Up @@ -90,4 +91,17 @@ bool operator==(EvaluationReason const& lhs, EvaluationReason const& rhs) {
bool operator!=(EvaluationReason const& lhs, EvaluationReason const& rhs) {
return !(lhs == rhs);
}

std::ostream& operator<<(std::ostream& out,
EvaluationReason::Kind const& kind) {
out << boost::json::value_from(kind).as_string();
return out;
}

std::ostream& operator<<(std::ostream& out,
EvaluationReason::ErrorKind const& kind) {
out << boost::json::value_from(kind).as_string();
return out;
}

} // namespace launchdarkly
Loading