Skip to content

Commit c43f046

Browse files
committed
refactor: replace bigSegmentStatus string with EvaluationReason::BigSegmentsStatus enum
1 parent 47fa5c2 commit c43f046

5 files changed

Lines changed: 128 additions & 38 deletions

File tree

libs/common/include/launchdarkly/data/evaluation_reason.hpp

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,25 @@ class EvaluationReason {
5555

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

58+
/**
59+
* Do not change these values. They must remain stable for the C API.
60+
*/
61+
enum class BigSegmentsStatus {
62+
// The query was successful and the segment state is up to date.
63+
kHealthy = 0,
64+
// The query was successful, but the segment state may not be up to
65+
// date.
66+
kStale = 1,
67+
// Big Segments could not be queried because the SDK configuration did
68+
// not include a Big Segment store.
69+
kNotConfigured = 2,
70+
// The query failed, for instance due to a database error.
71+
kStoreError = 3,
72+
};
73+
74+
friend std::ostream& operator<<(std::ostream& out,
75+
BigSegmentsStatus const& status);
76+
5877
/**
5978
* @return The general category of the reason.
6079
*/
@@ -66,6 +85,12 @@ class EvaluationReason {
6685
*/
6786
[[nodiscard]] std::optional<ErrorKind> ErrorKind() const;
6887

88+
/**
89+
* The validity of the Big Segment information used in this evaluation, or
90+
* std::nullopt if the evaluation did not query any Big Segments.
91+
*/
92+
[[nodiscard]] std::optional<BigSegmentsStatus> BigSegmentsStatus() const;
93+
6994
/**
7095
* The index of the matched rule (0 for the first), if the kind was
7196
* `"RULE_MATCH"`.
@@ -93,29 +118,13 @@ class EvaluationReason {
93118
*/
94119
[[nodiscard]] bool InExperiment() const;
95120

96-
/**
97-
* Describes the validity of Big Segment information, if and only if the
98-
* flag evaluation required querying at least one Big Segment.
99-
*
100-
* - `"HEALTHY"`: The Big Segment query involved in the flag evaluation was
101-
* successful, and the segment state is considered up to date.
102-
* - `"STALE"`: The Big Segment query involved in the flag evaluation was
103-
* successful, but the segment state may not be up to date
104-
* - `"NOT_CONFIGURED"`: Big Segments could not be queried for the flag
105-
* evaluation because the SDK configuration did not include a Big Segment
106-
* store.
107-
* - `"STORE_ERROR"`: The Big Segment query involved in the flag evaluation
108-
* failed, for instance due to a database error.
109-
*/
110-
[[nodiscard]] std::optional<std::string> BigSegmentStatus() const;
111-
112121
EvaluationReason(enum Kind kind,
113122
std::optional<enum ErrorKind> error_kind,
114123
std::optional<std::size_t> rule_index,
115124
std::optional<std::string> rule_id,
116125
std::optional<std::string> prerequisite_key,
117126
bool in_experiment,
118-
std::optional<std::string> big_segment_status);
127+
std::optional<enum BigSegmentsStatus> big_segments_status);
119128

120129
explicit EvaluationReason(enum ErrorKind error_kind);
121130

@@ -165,7 +174,7 @@ class EvaluationReason {
165174
std::optional<std::string> rule_id_;
166175
std::optional<std::string> prerequisite_key_;
167176
bool in_experiment_;
168-
std::optional<std::string> big_segment_status_;
177+
std::optional<enum BigSegmentsStatus> big_segments_status_;
169178
};
170179

171180
bool operator==(EvaluationReason const& lhs, EvaluationReason const& rhs);

libs/common/src/data/evaluation_reason.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ bool EvaluationReason::InExperiment() const {
2727
return in_experiment_;
2828
}
2929

30-
std::optional<std::string> EvaluationReason::BigSegmentStatus() const {
31-
return big_segment_status_;
30+
std::optional<enum EvaluationReason::BigSegmentsStatus>
31+
EvaluationReason::BigSegmentsStatus() const {
32+
return big_segments_status_;
3233
}
3334

3435
EvaluationReason::EvaluationReason(
@@ -38,14 +39,14 @@ EvaluationReason::EvaluationReason(
3839
std::optional<std::string> rule_id,
3940
std::optional<std::string> prerequisite_key,
4041
bool in_experiment,
41-
std::optional<std::string> big_segment_status)
42+
std::optional<enum BigSegmentsStatus> big_segments_status)
4243
: kind_(kind),
4344
error_kind_(error_kind),
4445
rule_index_(rule_index),
4546
rule_id_(std::move(rule_id)),
4647
prerequisite_key_(std::move(prerequisite_key)),
4748
in_experiment_(in_experiment),
48-
big_segment_status_(std::move(big_segment_status)) {}
49+
big_segments_status_(std::move(big_segments_status)) {}
4950

5051
EvaluationReason::EvaluationReason(enum ErrorKind error_kind)
5152
: EvaluationReason(Kind::kError,
@@ -105,8 +106,8 @@ std::ostream& operator<<(std::ostream& out, EvaluationReason const& reason) {
105106
out << " prerequisiteKey: " << reason.prerequisite_key_.value();
106107
}
107108
out << " inExperiment: " << reason.in_experiment_;
108-
if (reason.big_segment_status_.has_value()) {
109-
out << " bigSegmentStatus: " << reason.big_segment_status_.value();
109+
if (reason.big_segments_status_.has_value()) {
110+
out << " bigSegmentsStatus: " << reason.big_segments_status_.value();
110111
}
111112
out << "}";
112113
return out;
@@ -115,7 +116,7 @@ std::ostream& operator<<(std::ostream& out, EvaluationReason const& reason) {
115116
bool operator==(EvaluationReason const& lhs, EvaluationReason const& rhs) {
116117
return lhs.Kind() == rhs.Kind() && lhs.ErrorKind() == rhs.ErrorKind() &&
117118
lhs.InExperiment() == rhs.InExperiment() &&
118-
lhs.BigSegmentStatus() == rhs.BigSegmentStatus() &&
119+
lhs.BigSegmentsStatus() == rhs.BigSegmentsStatus() &&
119120
lhs.PrerequisiteKey() == rhs.PrerequisiteKey() &&
120121
lhs.RuleId() == rhs.RuleId() && lhs.RuleIndex() == rhs.RuleIndex();
121122
}
@@ -149,6 +150,26 @@ std::ostream& operator<<(std::ostream& out,
149150
return out;
150151
}
151152

153+
std::ostream& operator<<(
154+
std::ostream& out,
155+
enum EvaluationReason::BigSegmentsStatus const& status) {
156+
switch (status) {
157+
case EvaluationReason::BigSegmentsStatus::kHealthy:
158+
out << "HEALTHY";
159+
break;
160+
case EvaluationReason::BigSegmentsStatus::kStale:
161+
out << "STALE";
162+
break;
163+
case EvaluationReason::BigSegmentsStatus::kNotConfigured:
164+
out << "NOT_CONFIGURED";
165+
break;
166+
case EvaluationReason::BigSegmentsStatus::kStoreError:
167+
out << "STORE_ERROR";
168+
break;
169+
}
170+
return out;
171+
}
172+
152173
std::ostream& operator<<(std::ostream& out,
153174
enum EvaluationReason::ErrorKind const& kind) {
154175
switch (kind) {

libs/internal/src/serialization/json_evaluation_reason.cpp

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,54 @@ void tag_invoke(boost::json::value_from_tag const& unused,
125125
}
126126
}
127127

128+
tl::expected<enum EvaluationReason::BigSegmentsStatus, JsonError> tag_invoke(
129+
boost::json::value_to_tag<
130+
tl::expected<enum EvaluationReason::BigSegmentsStatus,
131+
JsonError>> const& unused,
132+
boost::json::value const& json_value) {
133+
boost::ignore_unused(unused);
134+
135+
if (!json_value.is_string()) {
136+
return tl::unexpected(JsonError::kSchemaFailure);
137+
}
138+
auto const& str = json_value.as_string();
139+
if (str == "HEALTHY") {
140+
return EvaluationReason::BigSegmentsStatus::kHealthy;
141+
}
142+
if (str == "STALE") {
143+
return EvaluationReason::BigSegmentsStatus::kStale;
144+
}
145+
if (str == "NOT_CONFIGURED") {
146+
return EvaluationReason::BigSegmentsStatus::kNotConfigured;
147+
}
148+
if (str == "STORE_ERROR") {
149+
return EvaluationReason::BigSegmentsStatus::kStoreError;
150+
}
151+
return tl::make_unexpected(JsonError::kSchemaFailure);
152+
}
153+
154+
void tag_invoke(boost::json::value_from_tag const& unused,
155+
boost::json::value& json_value,
156+
enum EvaluationReason::BigSegmentsStatus const& status) {
157+
boost::ignore_unused(unused);
158+
159+
auto& str = json_value.emplace_string();
160+
switch (status) {
161+
case EvaluationReason::BigSegmentsStatus::kHealthy:
162+
str = "HEALTHY";
163+
break;
164+
case EvaluationReason::BigSegmentsStatus::kStale:
165+
str = "STALE";
166+
break;
167+
case EvaluationReason::BigSegmentsStatus::kNotConfigured:
168+
str = "NOT_CONFIGURED";
169+
break;
170+
case EvaluationReason::BigSegmentsStatus::kStoreError:
171+
str = "STORE_ERROR";
172+
break;
173+
}
174+
}
175+
128176
tl::expected<EvaluationReason, JsonError> tag_invoke(
129177
boost::json::value_to_tag<tl::expected<EvaluationReason, JsonError>> const&
130178
unused,
@@ -172,17 +220,25 @@ tl::expected<EvaluationReason, JsonError> tag_invoke(
172220
auto in_experiment =
173221
ValueOrDefault(in_experiment_iter, json_obj.end(), false);
174222

175-
auto* big_segment_status_iter = json_obj.find("bigSegmentStatus");
176-
auto big_segment_status =
177-
ValueAsOpt<std::string>(big_segment_status_iter, json_obj.end());
223+
auto* big_segments_status_iter = json_obj.find("bigSegmentsStatus");
224+
std::optional<enum EvaluationReason::BigSegmentsStatus>
225+
big_segments_status;
226+
if (big_segments_status_iter != json_obj.end()) {
227+
auto parsed = boost::json::value_to<tl::expected<
228+
enum EvaluationReason::BigSegmentsStatus, JsonError>>(
229+
big_segments_status_iter->value());
230+
if (parsed) {
231+
big_segments_status = *parsed;
232+
}
233+
}
178234

179235
return EvaluationReason{*kind,
180236
error_kind,
181237
rule_index,
182238
rule_id,
183239
prerequisite_key,
184240
in_experiment,
185-
big_segment_status};
241+
big_segments_status};
186242
}
187243
return tl::unexpected(JsonError::kSchemaFailure);
188244
}
@@ -197,8 +253,9 @@ void tag_invoke(boost::json::value_from_tag const& unused,
197253
if (auto error_kind = reason.ErrorKind()) {
198254
obj.emplace("errorKind", boost::json::value_from(*error_kind));
199255
}
200-
if (auto big_segment_status = reason.BigSegmentStatus()) {
201-
obj.emplace("bigSegmentStatus", *big_segment_status);
256+
if (auto big_segments_status = reason.BigSegmentsStatus()) {
257+
obj.emplace("bigSegmentsStatus",
258+
boost::json::value_from(*big_segments_status));
202259
}
203260
if (auto rule_id = reason.RuleId()) {
204261
obj.emplace("ruleId", *rule_id);

libs/internal/tests/evaluation_reason_test.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ TEST(EvaluationReasonsTests, FromJsonAllFields) {
1818
"\"ruleId\":\"RULE_ID\","
1919
"\"prerequisiteKey\":\"PREREQ_KEY\","
2020
"\"inExperiment\":true,"
21-
"\"bigSegmentStatus\":\"STORE_ERROR\""
21+
"\"bigSegmentsStatus\":\"STORE_ERROR\""
2222
"}"));
2323

2424
EXPECT_EQ(EvaluationReason::Kind::kOff, reason.value().Kind());
@@ -27,7 +27,8 @@ TEST(EvaluationReasonsTests, FromJsonAllFields) {
2727
EXPECT_EQ(12, reason.value().RuleIndex());
2828
EXPECT_EQ("RULE_ID", reason.value().RuleId());
2929
EXPECT_EQ("PREREQ_KEY", reason.value().PrerequisiteKey());
30-
EXPECT_EQ("STORE_ERROR", reason.value().BigSegmentStatus());
30+
EXPECT_EQ(EvaluationReason::BigSegmentsStatus::kStoreError,
31+
reason.value().BigSegmentsStatus());
3132
EXPECT_TRUE(reason.value().InExperiment());
3233
}
3334

@@ -43,7 +44,7 @@ TEST(EvaluationReasonsTests, FromMinimalJson) {
4344
EXPECT_EQ(std::nullopt, reason.value().RuleIndex());
4445
EXPECT_EQ(std::nullopt, reason.value().RuleId());
4546
EXPECT_EQ(std::nullopt, reason.value().PrerequisiteKey());
46-
EXPECT_EQ(std::nullopt, reason.value().BigSegmentStatus());
47+
EXPECT_EQ(std::nullopt, reason.value().BigSegmentsStatus());
4748
EXPECT_FALSE(reason.value().InExperiment());
4849
}
4950

libs/internal/tests/evaluation_result_test.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ TEST(EvaluationResultTests, FromJsonAllFields) {
3232
"\"ruleId\":\"RULE_ID\","
3333
"\"prerequisiteKey\":\"PREREQ_KEY\","
3434
"\"inExperiment\":true,"
35-
"\"bigSegmentStatus\":\"STORE_ERROR\""
35+
"\"bigSegmentsStatus\":\"STORE_ERROR\""
3636
"}"
3737
"}"));
3838

@@ -56,14 +56,16 @@ TEST(EvaluationResultTests, FromJsonAllFields) {
5656
EXPECT_EQ(12, val->Detail().Reason()->get().RuleIndex());
5757
EXPECT_EQ("RULE_ID", val->Detail().Reason()->get().RuleId());
5858
EXPECT_EQ("PREREQ_KEY", val->Detail().Reason()->get().PrerequisiteKey());
59-
EXPECT_EQ("STORE_ERROR", val->Detail().Reason()->get().BigSegmentStatus());
59+
EXPECT_EQ(EvaluationReason::BigSegmentsStatus::kStoreError,
60+
val->Detail().Reason()->get().BigSegmentsStatus());
6061
EXPECT_TRUE(val->Detail().Reason()->get().InExperiment());
6162
}
6263

6364
TEST(EvaluationResultTests, ToJsonAllFields) {
6465
EvaluationReason reason(EvaluationReason::Kind::kOff,
6566
EvaluationReason::ErrorKind::kMalformedFlag, 12,
66-
"RULE_ID", "PREREQ_KEY", true, "STORE_ERROR");
67+
"RULE_ID", "PREREQ_KEY", true,
68+
EvaluationReason::BigSegmentsStatus::kStoreError);
6769
launchdarkly::EvaluationDetailInternal detail(
6870
Value(std::map<std::string, Value>{{"item", "a"}}), 84, reason);
6971
EvaluationResult result(12, 24, true, true,
@@ -79,7 +81,7 @@ TEST(EvaluationResultTests, ToJsonAllFields) {
7981
"{\"version\":12,\"flagVersion\":24,\"trackEvents\":true,"
8082
"\"trackReason\":true,\"debugEventsUntilDate\":1680555761,\"value\":{"
8183
"\"item\":\"a\"},\"variationIndex\":84,\"reason\":{\"kind\":\"OFF\","
82-
"\"errorKind\":\"MALFORMED_FLAG\",\"bigSegmentStatus\":\"STORE_ERROR\","
84+
"\"errorKind\":\"MALFORMED_FLAG\",\"bigSegmentsStatus\":\"STORE_ERROR\","
8385
"\"ruleId\":\"RULE_ID\",\"ruleIndex\":12,\"inExperiment\":true,"
8486
"\"prerequisiteKey\":\"PREREQ_KEY\"}}",
8587
res);

0 commit comments

Comments
 (0)