Skip to content

Commit 43d16c5

Browse files
authored
Added metadata handling (#72)
* Added metadata handling Signed-off-by: Marcin Olko <molko@google.com> * Improved Metadata creation Signed-off-by: Marcin Olko <molko@google.com> * Improved type checking and removed auto variables from test Signed-off-by: Marcin Olko <molko@google.com> --------- Signed-off-by: Marcin Olko <molko@google.com>
1 parent 094831f commit 43d16c5

5 files changed

Lines changed: 105 additions & 13 deletions

File tree

providers/flagd/src/BUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ cc_library(
5757
include_prefix = "flagd",
5858
visibility = ["//visibility:public"],
5959
deps = [
60-
"//providers/flagd/src/evaluator/json_logic:json_logic",
6160
":sync",
61+
"//providers/flagd/src/evaluator/json_logic",
6262
"@abseil-cpp//absl/log",
6363
"@abseil-cpp//absl/strings",
6464
"@nlohmann_json//:json",

providers/flagd/src/evaluator.cpp

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,37 @@ openfeature::Value JsonToValue(const nlohmann::json& json_val) {
4646
return {};
4747
}
4848

49+
std::optional<openfeature::FlagMetadataValue> JsonToMetadataValue(
50+
const nlohmann::json& json) {
51+
if (json.is_boolean()) {
52+
return json.get<bool>();
53+
}
54+
if (json.is_string()) {
55+
return json.get<std::string>();
56+
}
57+
if (json.is_number_integer()) {
58+
return json.get<int64_t>();
59+
}
60+
if (json.is_number_float()) {
61+
return json.get<double>();
62+
}
63+
LOG(ERROR) << "Failed to map JSON value to openfeature::FlagMetadataValue: "
64+
<< json.dump();
65+
return std::nullopt;
66+
}
67+
68+
void EnrichMetadata(openfeature::FlagMetadata& metadata,
69+
const nlohmann::json& metadata_json) {
70+
if (!metadata_json.is_object()) return;
71+
for (const auto& [key, value] : metadata_json.items()) {
72+
std::optional<openfeature::FlagMetadataValue> metadata_value =
73+
JsonToMetadataValue(value);
74+
if (metadata_value.has_value()) {
75+
metadata.data[key] = std::move(metadata_value.value());
76+
}
77+
}
78+
}
79+
4980
nlohmann::json ContextToJson(const openfeature::EvaluationContext& ctx) {
5081
nlohmann::json json = nlohmann::json::object();
5182
std::optional<std::string_view> targeting_key = ctx.GetTargetingKey();
@@ -83,27 +114,39 @@ std::unique_ptr<openfeature::ResolutionDetails<T>>
83114
JsonLogicEvaluator::ResolveAny(std::string_view flag_key, T default_value,
84115
const openfeature::EvaluationContext& ctx) {
85116
std::shared_ptr<const nlohmann::json> flags = sync_->GetFlags();
117+
std::shared_ptr<const nlohmann::json> global_metadata_json =
118+
sync_->GetMetadata();
119+
120+
openfeature::FlagMetadata flag_metadata;
121+
if (global_metadata_json) {
122+
EnrichMetadata(flag_metadata, *global_metadata_json);
123+
}
124+
86125
if (flags == nullptr) {
87126
return std::make_unique<openfeature::ResolutionDetails<T>>(
88127
std::move(default_value), openfeature::Reason::kError, "",
89-
openfeature::FlagMetadata(), openfeature::ErrorCode::kParseError,
128+
flag_metadata, openfeature::ErrorCode::kParseError,
90129
"No flags available");
91130
}
92131

93132
auto flag_it = flags->find(flag_key);
94133
if (flag_it == flags->end()) {
95134
return std::make_unique<openfeature::ResolutionDetails<T>>(
96135
std::move(default_value), openfeature::Reason::kError, "",
97-
openfeature::FlagMetadata(), openfeature::ErrorCode::kFlagNotFound,
136+
flag_metadata, openfeature::ErrorCode::kFlagNotFound,
98137
absl::StrCat("flag: ", flag_key, " not found"));
99138
}
100139

101140
const nlohmann::json& flag_config = *flag_it;
102141

142+
if (flag_config.contains("metadata")) {
143+
EnrichMetadata(flag_metadata, flag_config["metadata"]);
144+
}
145+
103146
if (flag_config["state"] == "DISABLED") {
104147
return std::make_unique<openfeature::ResolutionDetails<T>>(
105148
std::move(default_value), openfeature::Reason::kDisabled, "",
106-
openfeature::FlagMetadata(), openfeature::ErrorCode::kFlagNotFound,
149+
flag_metadata, openfeature::ErrorCode::kFlagNotFound,
107150
absl::StrCat("flag: ", flag_key, " is disabled"));
108151
}
109152

@@ -153,7 +196,7 @@ JsonLogicEvaluator::ResolveAny(std::string_view flag_key, T default_value,
153196
if (!flag_config.contains("defaultVariant")) {
154197
return std::make_unique<openfeature::ResolutionDetails<T>>(
155198
std::move(default_value), openfeature::Reason::kError, "",
156-
openfeature::FlagMetadata(), openfeature::ErrorCode::kFlagNotFound,
199+
flag_metadata, openfeature::ErrorCode::kFlagNotFound,
157200
absl::StrCat("flag: ", flag_key,
158201
" doesn't have defaultVariant defined."));
159202
}
@@ -167,7 +210,7 @@ JsonLogicEvaluator::ResolveAny(std::string_view flag_key, T default_value,
167210
if (!variants.contains(variant_name)) {
168211
return std::make_unique<openfeature::ResolutionDetails<T>>(
169212
std::move(default_value), openfeature::Reason::kError, variant_name,
170-
openfeature::FlagMetadata(), openfeature::ErrorCode::kGeneral,
213+
flag_metadata, openfeature::ErrorCode::kGeneral,
171214
absl::StrCat("flag: ", flag_key, " doesn't contain evaluated variant: ",
172215
variant_name, "."));
173216
}
@@ -185,13 +228,12 @@ JsonLogicEvaluator::ResolveAny(std::string_view flag_key, T default_value,
185228
} catch (const nlohmann::json::exception& err) {
186229
return std::make_unique<openfeature::ResolutionDetails<T>>(
187230
std::move(default_value), openfeature::Reason::kError, variant_name,
188-
openfeature::FlagMetadata(), openfeature::ErrorCode::kTypeMismatch,
189-
err.what());
231+
flag_metadata, openfeature::ErrorCode::kTypeMismatch, err.what());
190232
}
191233

192234
return std::make_unique<openfeature::ResolutionDetails<T>>(
193-
std::move(value), reason, variant_name, openfeature::FlagMetadata(),
194-
std::nullopt, std::nullopt);
235+
std::move(value), reason, variant_name, flag_metadata, std::nullopt,
236+
std::nullopt);
195237
}
196238

197239
std::unique_ptr<openfeature::BoolResolutionDetails>

providers/flagd/src/sync.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class FlagSync::Validator {
6161
FlagSync::FlagSync()
6262
: current_flags_(
6363
std::make_shared<nlohmann::json>(nlohmann::json::object())),
64+
global_metadata_(
65+
std::make_shared<nlohmann::json>(nlohmann::json::object())),
6466
validator_(std::make_unique<Validator>()) {}
6567

6668
FlagSync::~FlagSync() = default;
@@ -74,10 +76,21 @@ void FlagSync::UpdateFlags(const nlohmann::json& new_json) {
7476
}
7577
}
7678

77-
auto new_snapshot = std::make_shared<const nlohmann::json>(new_json["flags"]);
79+
auto new_flags_snapshot =
80+
std::make_shared<const nlohmann::json>(new_json["flags"]);
81+
std::shared_ptr<const nlohmann::json> new_metadata_snapshot;
82+
if (new_json.contains("metadata")) {
83+
new_metadata_snapshot =
84+
std::make_shared<const nlohmann::json>(new_json["metadata"]);
85+
} else {
86+
new_metadata_snapshot =
87+
std::make_shared<const nlohmann::json>(nlohmann::json::object());
88+
}
89+
7890
{
7991
std::scoped_lock lock(flags_mutex_);
80-
current_flags_ = std::move(new_snapshot);
92+
current_flags_ = std::move(new_flags_snapshot);
93+
global_metadata_ = std::move(new_metadata_snapshot);
8194
}
8295
}
8396

@@ -86,6 +99,11 @@ std::shared_ptr<const nlohmann::json> FlagSync::GetFlags() const {
8699
return current_flags_;
87100
}
88101

102+
std::shared_ptr<const nlohmann::json> FlagSync::GetMetadata() const {
103+
std::scoped_lock lock(flags_mutex_);
104+
return global_metadata_;
105+
}
106+
89107
GrpcSync::GrpcSync(FlagdProviderConfig config) : config_(std::move(config)) {}
90108

91109
GrpcSync::~GrpcSync() {

providers/flagd/src/sync.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ class FlagSync {
2525
virtual absl::Status Shutdown() = 0;
2626

2727
std::shared_ptr<const nlohmann::json> GetFlags() const;
28+
std::shared_ptr<const nlohmann::json> GetMetadata() const;
2829

2930
protected:
3031
void UpdateFlags(const nlohmann::json& new_json);
3132

3233
private:
3334
mutable std::mutex flags_mutex_;
3435
std::shared_ptr<const nlohmann::json> current_flags_;
36+
std::shared_ptr<const nlohmann::json> global_metadata_;
3537

3638
class Validator;
3739
std::unique_ptr<Validator> validator_;
@@ -54,7 +56,7 @@ class GrpcSync final : public FlagSync {
5456
kUninitialized,
5557
kInitializing, // Thread started, waiting for first connection
5658
kReady, // First sync complete, running normally
57-
kShuttingDown // Shutdown requested, cleaning up
59+
kShuttingDown, // Shutdown requested, cleaning up
5860
};
5961

6062
std::thread background_thread_;

providers/flagd/tests/evaluator_test.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,36 @@ TEST_F(EvaluatorTest, ResolveBoolean_TypeMismatch) {
8686
EXPECT_EQ(result->GetErrorCode(), openfeature::ErrorCode::kTypeMismatch);
8787
}
8888

89+
TEST_F(EvaluatorTest, ResolveBoolean_Metadata) {
90+
nlohmann::json flags = {
91+
{"metadata",
92+
{{"global-key", "global-value"}, {"override-key", "global-override"}}},
93+
{"flags",
94+
{{"my-flag",
95+
{{"state", "ENABLED"},
96+
{"variants", {{"on", true}, {"off", false}}},
97+
{"defaultVariant", "on"},
98+
{"metadata",
99+
{{"flag-key", "flag-value"},
100+
{"override-key", "flag-override"}}}}}}}};
101+
102+
sync_->TriggerUpdate(flags);
103+
104+
openfeature::EvaluationContext ctx =
105+
openfeature::EvaluationContext::Builder().build();
106+
std::unique_ptr<openfeature::BoolResolutionDetails> result =
107+
evaluator_->ResolveBoolean("my-flag", false, ctx);
108+
109+
EXPECT_EQ(result->GetValue(), true);
110+
const openfeature::FlagMetadata& metadata = result->GetFlagMetadata();
111+
112+
EXPECT_EQ(std::get<std::string>(metadata.data.at("global-key")),
113+
"global-value");
114+
EXPECT_EQ(std::get<std::string>(metadata.data.at("flag-key")), "flag-value");
115+
EXPECT_EQ(std::get<std::string>(metadata.data.at("override-key")),
116+
"flag-override");
117+
}
118+
89119
TEST_F(EvaluatorTest, ResolveBoolean_VariantNotFound) {
90120
nlohmann::json flags = {{"flags",
91121
{{"my-broken-flag",

0 commit comments

Comments
 (0)