Skip to content
Open
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
6 changes: 5 additions & 1 deletion providers/flagd/src/sync/grpc/grpc_sync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,11 @@ GrpcSync::StreamResult GrpcSync::ProcessStream(
continue;
}

UpdateFlags(raw);
absl::Status status = UpdateFlags(raw);
if (!status.ok()) {
LOG(ERROR) << "Failed to update flags: " << status;
continue;
}
Comment on lines +239 to +243

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Pass raw using std::move to avoid copying the entire JSON object when calling UpdateFlags. Use auto for the status variable declaration as its type is clearly deduced from the function call.

Suggested change
absl::Status status = UpdateFlags(raw);
if (!status.ok()) {
LOG(ERROR) << "Failed to update flags: " << status;
continue;
}
auto status = UpdateFlags(std::move(raw));
if (!status.ok()) {
LOG(ERROR) << "Failed to update flags: " << status;
continue;
}
References
  1. In modern C++, use 'auto' for variable declarations when the type is clearly deduced from the expression on the right-hand side.


if (!connected) {
connected = true;
Expand Down
20 changes: 11 additions & 9 deletions providers/flagd/src/sync/sync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <nlohmann/json.hpp>

#include "absl/log/log.h"
#include "absl/strings/str_cat.h"
#include "embedded_schemas.h"

using Json = nlohmann::json;
Expand Down Expand Up @@ -38,13 +39,13 @@ class FlagSync::Validator {
validator.set_root_schema(Json::parse(schema::schemas.at("flagd.json")));
}

bool Validate(const Json& json) const {
absl::Status Validate(const Json& json) const {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Mark Validate with [[nodiscard]] to ensure that callers do not silently ignore validation errors.

Suggested change
absl::Status Validate(const Json& json) const {
[[nodiscard]] absl::Status Validate(const Json& json) const {

try {
validator.validate(json);
return true;
return absl::OkStatus();
} catch (const std::exception& e) {
LOG(ERROR) << "Flag configuration validation failed: " << e.what();
return false;
return absl::InvalidArgumentError(
absl::StrCat("Flag configuration validation failed: ", e.what()));
}
}
};
Expand All @@ -58,12 +59,11 @@ FlagSync::FlagSync()

FlagSync::~FlagSync() = default;

void FlagSync::UpdateFlags(const nlohmann::json& new_json) {
absl::Status FlagSync::UpdateFlags(const nlohmann::json& new_json) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the definition of UpdateFlags to accept new_json by value and mark it [[nodiscard]].

To fully leverage move semantics and avoid deep copies of the JSON structure, you should also update the body of this function (which is outside the current diff) to use std::move when extracting "flags" and "metadata":

  auto new_flags_snapshot =
      std::make_shared<const nlohmann::json>(std::move(new_json["flags"]));
  auto new_metadata_snapshot =
      std::make_shared<const nlohmann::json>(std::move(new_json["metadata"]));
Suggested change
absl::Status FlagSync::UpdateFlags(const nlohmann::json& new_json) {
[[nodiscard]] absl::Status FlagSync::UpdateFlags(nlohmann::json new_json) {
References
  1. Avoid redundant checks for the existence of JSON fields if their presence is guaranteed by schema validation performed before the data is processed.
  2. In modern C++, use 'auto' for variable declarations when the type is clearly deduced from the expression on the right-hand side.

if (validator_) {
if (!validator_->Validate(new_json)) {
// Validation failed, do not update flags.
LOG(ERROR) << "Flag configuration validation failed, skipping update.";
return;
absl::Status status = validator_->Validate(new_json);
if (!status.ok()) {
return status;
}
}

Expand All @@ -83,6 +83,8 @@ void FlagSync::UpdateFlags(const nlohmann::json& new_json) {
current_flags_ = std::move(new_flags_snapshot);
global_metadata_ = std::move(new_metadata_snapshot);
}

return absl::OkStatus();
}

void FlagSync::ClearFlags() {
Expand Down
2 changes: 1 addition & 1 deletion providers/flagd/src/sync/sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class FlagSync {
std::shared_ptr<const nlohmann::json> GetMetadata() const;

protected:
void UpdateFlags(const nlohmann::json& new_json);
absl::Status UpdateFlags(const nlohmann::json& new_json);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Mark UpdateFlags with [[nodiscard]] to ensure that callers do not silently ignore validation or update errors. Additionally, pass new_json by value (nlohmann::json) instead of const nlohmann::json& to enable move semantics and avoid expensive deep copies of the flag configuration JSON.

Suggested change
absl::Status UpdateFlags(const nlohmann::json& new_json);
[[nodiscard]] absl::Status UpdateFlags(nlohmann::json new_json);

void ClearFlags();

private:
Expand Down
42 changes: 21 additions & 21 deletions providers/flagd/tests/evaluator/evaluator_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class TestableSync : public flagd::FlagSync {
}
absl::Status Shutdown() override { return absl::OkStatus(); }

void TriggerUpdate(const nlohmann::json& new_json) {
this->UpdateFlags(new_json);
absl::Status TriggerUpdate(const nlohmann::json& new_json) {
return this->UpdateFlags(new_json);
}
Comment on lines +19 to 21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update TriggerUpdate to accept new_json by value and move it to UpdateFlags to align with the new signature and avoid unnecessary copies.

Suggested change
absl::Status TriggerUpdate(const nlohmann::json& new_json) {
return this->UpdateFlags(new_json);
}
absl::Status TriggerUpdate(nlohmann::json new_json) {
return this->UpdateFlags(std::move(new_json));
}

};

Expand Down Expand Up @@ -46,7 +46,7 @@ TEST_F(EvaluatorTest, ResolveBooleanSuccess) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand All @@ -63,7 +63,7 @@ TEST_F(EvaluatorTest, ResolveBooleanFlagNotFound) {
nlohmann::json flags = nlohmann::json::parse(R"({
"flags": {}
})");
sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand All @@ -89,7 +89,7 @@ TEST_F(EvaluatorTest, ResolveBooleanTypeMismatch) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand Down Expand Up @@ -123,7 +123,7 @@ TEST_F(EvaluatorTest, ResolveBooleanMetadata) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand Down Expand Up @@ -154,7 +154,7 @@ TEST_F(EvaluatorTest, ResolveBooleanVariantNotFound) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand Down Expand Up @@ -183,7 +183,7 @@ TEST_F(EvaluatorTest, ResolveStringSuccess) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand All @@ -209,7 +209,7 @@ TEST_F(EvaluatorTest, ResolveIntegerSuccess) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand All @@ -235,7 +235,7 @@ TEST_F(EvaluatorTest, ResolveDoubleSuccess) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand Down Expand Up @@ -266,7 +266,7 @@ TEST_F(EvaluatorTest, ResolveObjectSuccess) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand Down Expand Up @@ -302,7 +302,7 @@ TEST_F(EvaluatorTest, ResolveStringTypeMismatch) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand All @@ -327,7 +327,7 @@ TEST_F(EvaluatorTest, ResolveIntegerTypeMismatch) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand All @@ -352,7 +352,7 @@ TEST_F(EvaluatorTest, ResolveDoubleTypeMismatch) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand All @@ -371,7 +371,7 @@ class EvaluatorDefaultVariantTest
TEST_P(EvaluatorDefaultVariantTest, ResolveBooleanReturnsDefault) {
nlohmann::json flags_json =
nlohmann::json::parse(R"({"flags":)" + GetParam() + "}");
sync_->TriggerUpdate(flags_json);
EXPECT_TRUE(sync_->TriggerUpdate(flags_json).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand Down Expand Up @@ -421,7 +421,7 @@ TEST_F(EvaluatorTest, ResolveBooleanDisabled) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand Down Expand Up @@ -453,7 +453,7 @@ TEST_F(EvaluatorTest, ResolveStringTargetingSuccess) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

// Match targeting rule
openfeature::EvaluationContext ctx_blue =
Expand Down Expand Up @@ -504,7 +504,7 @@ TEST_F(EvaluatorTest, ResolveIntegerComplexTargeting) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx = openfeature::EvaluationContext::Builder()
.WithAttribute("env", "prod")
Expand Down Expand Up @@ -538,7 +538,7 @@ TEST_F(EvaluatorTest, ResolveObjectNestedStructure) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand Down Expand Up @@ -587,7 +587,7 @@ TEST_F(EvaluatorTest, ResolveBooleanFlagdSpecialVars) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx =
openfeature::EvaluationContext::Builder().build();
Expand All @@ -612,7 +612,7 @@ TEST_F(EvaluatorTest, ResolveBooleanTypeMismatchInTargeting) {
}
})");

sync_->TriggerUpdate(flags);
EXPECT_TRUE(sync_->TriggerUpdate(flags).ok());

openfeature::EvaluationContext ctx = openfeature::EvaluationContext::Builder()
.WithAttribute("color", "blue")
Expand Down
16 changes: 9 additions & 7 deletions providers/flagd/tests/smoke/openfeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ class MockSync : public flagd::FlagSync {
}
absl::Status Shutdown() override { return absl::OkStatus(); }

void SetFlags(const nlohmann::json& flags) { this->UpdateFlags(flags); }
absl::Status SetFlags(const nlohmann::json& flags) {
return this->UpdateFlags(flags);
}
Comment on lines +19 to +21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update SetFlags to accept flags by value and move it to UpdateFlags to align with the new signature and avoid unnecessary copies.

Suggested change
absl::Status SetFlags(const nlohmann::json& flags) {
return this->UpdateFlags(flags);
}
absl::Status SetFlags(nlohmann::json flags) {
return this->UpdateFlags(std::move(flags));
}

};

class FlagdOpenFeatureTest : public ::testing::Test {
Expand Down Expand Up @@ -54,7 +56,7 @@ TEST_F(FlagdOpenFeatureTest, BooleanEvaluation) {
}
}
})");
sync_->SetFlags(flags);
EXPECT_TRUE(sync_->SetFlags(flags).ok());

EXPECT_TRUE(client_->GetBooleanValue("bool-flag", false));
EXPECT_FALSE(client_->GetBooleanValue("non-existent", false));
Expand All @@ -73,7 +75,7 @@ TEST_F(FlagdOpenFeatureTest, StringEvaluation) {
}
}
})");
sync_->SetFlags(flags);
EXPECT_TRUE(sync_->SetFlags(flags).ok());

EXPECT_EQ(client_->GetStringValue("string-flag", "default"), "value2");
}
Expand All @@ -91,7 +93,7 @@ TEST_F(FlagdOpenFeatureTest, IntegerEvaluation) {
}
}
})");
sync_->SetFlags(flags);
EXPECT_TRUE(sync_->SetFlags(flags).ok());

EXPECT_EQ(client_->GetIntegerValue("int-flag", 0), 1);
}
Expand All @@ -109,7 +111,7 @@ TEST_F(FlagdOpenFeatureTest, DoubleEvaluation) {
}
}
})");
sync_->SetFlags(flags);
EXPECT_TRUE(sync_->SetFlags(flags).ok());

EXPECT_DOUBLE_EQ(client_->GetDoubleValue("double-flag", 0.0), 2.2);
}
Expand All @@ -128,7 +130,7 @@ TEST_F(FlagdOpenFeatureTest, ObjectEvaluation) {
}
}
})");
sync_->SetFlags(flags);
EXPECT_TRUE(sync_->SetFlags(flags).ok());

openfeature::Value val =
client_->GetObjectValue("obj-flag", openfeature::Value());
Expand Down Expand Up @@ -159,7 +161,7 @@ TEST_F(FlagdOpenFeatureTest, EvaluationContextTargeting) {
}
}
})");
sync_->SetFlags(flags);
EXPECT_TRUE(sync_->SetFlags(flags).ok());

// Without context, should return defaultVariant "red"
EXPECT_EQ(client_->GetStringValue("targeting-flag", "default"), "red-value");
Expand Down
12 changes: 6 additions & 6 deletions providers/flagd/tests/smoke/sync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class MockSync : public flagd::FlagSync {
(override));
MOCK_METHOD(absl::Status, Shutdown, (), (override));

void TriggerUpdate(const nlohmann::json& new_json) {
this->UpdateFlags(new_json);
absl::Status TriggerUpdate(const nlohmann::json& new_json) {
return this->UpdateFlags(new_json);
}
Comment on lines +21 to 23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update TriggerUpdate to accept new_json by value and move it to UpdateFlags to align with the new signature and avoid unnecessary copies.

Suggested change
absl::Status TriggerUpdate(const nlohmann::json& new_json) {
return this->UpdateFlags(new_json);
}
absl::Status TriggerUpdate(nlohmann::json new_json) {
return this->UpdateFlags(std::move(new_json));
}

};

Expand Down Expand Up @@ -55,7 +55,7 @@ TEST_F(FlagSyncTest, HelperMethodsUpdateAndRetrieveFlags) {
}
})"_json;

sync_.TriggerUpdate(expected_flags);
EXPECT_TRUE(sync_.TriggerUpdate(expected_flags).ok());

result_ptr = sync_.GetFlags();

Expand All @@ -82,7 +82,7 @@ TEST_F(FlagSyncTest, ThreadSafetyReadersAndWriters) {
const int k_iterations = 5000;
std::atomic<bool> start_flag{false};

auto writer_func = [&]() {
auto writer_func = [&] {
while (!start_flag.load());

for (int i = 0; i < k_iterations; ++i) {
Expand All @@ -99,11 +99,11 @@ TEST_F(FlagSyncTest, ThreadSafetyReadersAndWriters) {
"metadata": {}
})"_json;
update["flags"]["myFlag"]["variants"]["iteration"] = i;
sync_.TriggerUpdate(update);
EXPECT_TRUE(sync_.TriggerUpdate(update).ok());
}
};

auto reader_func = [&]() {
auto reader_func = [&] {
while (!start_flag.load());

for (int i = 0; i < k_iterations; ++i) {
Expand Down
Loading