From df9138892202a542c22350406c093e412122bbc0 Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:54:01 -0700 Subject: [PATCH 1/5] fix: Ensure that serialization of a variation or rollout uses the correct overload. --- .../include/launchdarkly/serialization/json_flag.hpp | 5 ++++- .../launchdarkly/serialization/value_mapping.hpp | 10 ++++++++++ libs/internal/src/serialization/json_flag.cpp | 4 ++-- libs/internal/tests/data_model_serialization_test.cpp | 7 ++++--- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/libs/internal/include/launchdarkly/serialization/json_flag.hpp b/libs/internal/include/launchdarkly/serialization/json_flag.hpp index ec5a5604c..6fa55cb61 100644 --- a/libs/internal/include/launchdarkly/serialization/json_flag.hpp +++ b/libs/internal/include/launchdarkly/serialization/json_flag.hpp @@ -7,6 +7,9 @@ namespace launchdarkly { +struct VariationOrRolloutContext {}; + + tl::expected, JsonError> tag_invoke( boost::json::value_to_tag< tl::expected, @@ -72,7 +75,7 @@ void tag_invoke(boost::json::value_from_tag const& unused, void tag_invoke( boost::json::value_from_tag const& unused, boost::json::value& json_value, - data_model::Flag::VariationOrRollout const& variation_or_rollout); + data_model::Flag::VariationOrRollout const& variation_or_rollout, const VariationOrRolloutContext&); void tag_invoke( boost::json::value_from_tag const& unused, diff --git a/libs/internal/include/launchdarkly/serialization/value_mapping.hpp b/libs/internal/include/launchdarkly/serialization/value_mapping.hpp index 69ad964fe..4894d02e7 100644 --- a/libs/internal/include/launchdarkly/serialization/value_mapping.hpp +++ b/libs/internal/include/launchdarkly/serialization/value_mapping.hpp @@ -189,6 +189,16 @@ void WriteMinimal(boost::json::object& obj, } } +template +void WriteMinimal(boost::json::object& obj, + std::string const& key, + T const& val, + std::function const& predicate, const C &c) { + if (predicate()) { + obj.emplace(key, boost::json::value_from(val, c)); + } +} + void WriteMinimal(boost::json::object& obj, std::string const& key, // No copy when not used. bool val); diff --git a/libs/internal/src/serialization/json_flag.cpp b/libs/internal/src/serialization/json_flag.cpp index 404ee1def..787a01416 100644 --- a/libs/internal/src/serialization/json_flag.cpp +++ b/libs/internal/src/serialization/json_flag.cpp @@ -255,7 +255,7 @@ void tag_invoke(boost::json::value_from_tag const& unused, void tag_invoke( boost::json::value_from_tag const& unused, boost::json::value& json_value, - data_model::Flag::VariationOrRollout const& variation_or_rollout) { + data_model::Flag::VariationOrRollout const& variation_or_rollout, const VariationOrRolloutContext&) { auto& obj = json_value.emplace_object(); std::visit( [&obj](auto&& arg) { @@ -364,7 +364,7 @@ void tag_invoke(boost::json::value_from_tag const& unused, } }, flag.fallthrough); - }); + }, VariationOrRolloutContext()); WriteMinimal(obj, "clientSideAvailability", flag.clientSideAvailability, [&]() { return flag.clientSideAvailability.usingEnvironmentId || diff --git a/libs/internal/tests/data_model_serialization_test.cpp b/libs/internal/tests/data_model_serialization_test.cpp index 709af98f6..e117035a7 100644 --- a/libs/internal/tests/data_model_serialization_test.cpp +++ b/libs/internal/tests/data_model_serialization_test.cpp @@ -423,7 +423,7 @@ TEST(RolloutTests, SerializeAllFields) { TEST(VariationOrRolloutTests, SerializeVariation) { data_model::Flag::VariationOrRollout variation = 5; - auto json = boost::json::value_from(variation); + auto json = boost::json::value_from(variation, VariationOrRolloutContext()); auto expected = boost::json::parse(R"({"variation":5})"); EXPECT_EQ(expected, json); @@ -438,8 +438,9 @@ TEST(VariationOrRolloutTests, SerializeRollout) { rollout.seed = 42; rollout.variations = { data_model::Flag::Rollout::WeightedVariation::Untracked(1, 2), {3, 4}}; - data_model::Flag::VariationOrRollout var_or_roll = rollout; - auto json = boost::json::value_from(var_or_roll); + data_model::Flag::VariationOrRollout var_or_roll; + var_or_roll.emplace(rollout); + auto json = boost::json::value_from(var_or_roll, VariationOrRolloutContext()); auto expected = boost::json::parse(R"({ "rollout":{ From 24b26297e2084213ea1b831490c48f879b7fe60f Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Wed, 8 Oct 2025 16:18:27 -0700 Subject: [PATCH 2/5] Add conditional compilation. --- .../launchdarkly/serialization/value_mapping.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libs/internal/include/launchdarkly/serialization/value_mapping.hpp b/libs/internal/include/launchdarkly/serialization/value_mapping.hpp index 4894d02e7..2758de0f8 100644 --- a/libs/internal/include/launchdarkly/serialization/value_mapping.hpp +++ b/libs/internal/include/launchdarkly/serialization/value_mapping.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -195,7 +196,16 @@ void WriteMinimal(boost::json::object& obj, T const& val, std::function const& predicate, const C &c) { if (predicate()) { + // In Boost 1.83 the ability to have a conversion context was added. + // It also introduces the potential for the wrong conversion to be used, + // so for boost 1.83 and greater we use a conversion context to ensure + // the correct serialization is used. +#if BOOST_VERSION >= 108300 obj.emplace(key, boost::json::value_from(val, c)); +#else + boost::ignore_unused(c); + obj.emplace(key, boost::json::value_from(val)); +#endif } } From fd3bd447b38737b945802475fbc7a2023fc0a26f Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Wed, 8 Oct 2025 16:22:42 -0700 Subject: [PATCH 3/5] Conditional boost code in tests. --- .../tests/data_model_serialization_test.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/internal/tests/data_model_serialization_test.cpp b/libs/internal/tests/data_model_serialization_test.cpp index e117035a7..66a3efe25 100644 --- a/libs/internal/tests/data_model_serialization_test.cpp +++ b/libs/internal/tests/data_model_serialization_test.cpp @@ -423,8 +423,13 @@ TEST(RolloutTests, SerializeAllFields) { TEST(VariationOrRolloutTests, SerializeVariation) { data_model::Flag::VariationOrRollout variation = 5; - auto json = boost::json::value_from(variation, VariationOrRolloutContext()); + //Explanation in value_mapping.hpp. +#if BOOST_VERSION >= 108300 + auto json = boost::json::value_from(variation, VariationOrRolloutContext()); +#else + auto json = boost::json::value_from(var_or_roll); +#endif auto expected = boost::json::parse(R"({"variation":5})"); EXPECT_EQ(expected, json); } @@ -440,7 +445,13 @@ TEST(VariationOrRolloutTests, SerializeRollout) { data_model::Flag::Rollout::WeightedVariation::Untracked(1, 2), {3, 4}}; data_model::Flag::VariationOrRollout var_or_roll; var_or_roll.emplace(rollout); + //Explanation in value_mapping.hpp. +#if BOOST_VERSION >= 108300 auto json = boost::json::value_from(var_or_roll, VariationOrRolloutContext()); +#else + auto json = boost::json::value_from(var_or_roll); +#endif + auto expected = boost::json::parse(R"({ "rollout":{ From 5ad90f63bb758108dd4dd0930d03b956752c609b Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Wed, 8 Oct 2025 16:28:49 -0700 Subject: [PATCH 4/5] Fix typo --- libs/internal/tests/data_model_serialization_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/internal/tests/data_model_serialization_test.cpp b/libs/internal/tests/data_model_serialization_test.cpp index 66a3efe25..563d2e5f9 100644 --- a/libs/internal/tests/data_model_serialization_test.cpp +++ b/libs/internal/tests/data_model_serialization_test.cpp @@ -428,7 +428,7 @@ TEST(VariationOrRolloutTests, SerializeVariation) { #if BOOST_VERSION >= 108300 auto json = boost::json::value_from(variation, VariationOrRolloutContext()); #else - auto json = boost::json::value_from(var_or_roll); + auto json = boost::json::value_from(variation); #endif auto expected = boost::json::parse(R"({"variation":5})"); EXPECT_EQ(expected, json); From 813a37c48befb85bf57073f047177dc5df4d8a0f Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Wed, 8 Oct 2025 16:36:57 -0700 Subject: [PATCH 5/5] Support both signatures for tag_invoke --- .../include/launchdarkly/serialization/json_flag.hpp | 5 +++++ libs/internal/src/serialization/json_flag.cpp | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/libs/internal/include/launchdarkly/serialization/json_flag.hpp b/libs/internal/include/launchdarkly/serialization/json_flag.hpp index 6fa55cb61..49b4f8cf0 100644 --- a/libs/internal/include/launchdarkly/serialization/json_flag.hpp +++ b/libs/internal/include/launchdarkly/serialization/json_flag.hpp @@ -77,6 +77,11 @@ void tag_invoke( boost::json::value& json_value, data_model::Flag::VariationOrRollout const& variation_or_rollout, const VariationOrRolloutContext&); +void tag_invoke( + boost::json::value_from_tag const& unused, + boost::json::value& json_value, + data_model::Flag::VariationOrRollout const& variation_or_rollout); + void tag_invoke( boost::json::value_from_tag const& unused, boost::json::value& json_value, diff --git a/libs/internal/src/serialization/json_flag.cpp b/libs/internal/src/serialization/json_flag.cpp index 787a01416..2331af82b 100644 --- a/libs/internal/src/serialization/json_flag.cpp +++ b/libs/internal/src/serialization/json_flag.cpp @@ -273,6 +273,13 @@ void tag_invoke( variation_or_rollout); } +void tag_invoke( + boost::json::value_from_tag const& unused, + boost::json::value& json_value, + data_model::Flag::VariationOrRollout const& variation_or_rollout) { + tag_invoke(unused, json_value, variation_or_rollout, VariationOrRolloutContext()); +} + void tag_invoke(boost::json::value_from_tag const& unused, boost::json::value& json_value, data_model::Flag::Prerequisite const& prerequisite) {