Skip to content

Commit 70f5cd8

Browse files
committed
Method to extract ConfigurableParam as JSON string and update from such string
1 parent 62fd72e commit 70f5cd8

3 files changed

Lines changed: 117 additions & 22 deletions

File tree

Common/Utils/include/CommonUtils/ConfigurableParam.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ class ConfigurableParam
407407
// writes a human readable INI or JSON file depending on the extension
408408
static void write(std::string const& filename, std::string const& keyOnly = "");
409409

410+
static std::string asJSON(std::string const& keyOnly = "");
411+
410412
// can be used instead of using API on concrete child classes
411413
template <typename T>
412414
static T getValueAs(std::string key)
@@ -494,6 +496,9 @@ class ConfigurableParam
494496
// be updated, absence of data for any of requested params will lead to fatal
495497
static void updateFromFile(std::string const&, std::string const& paramsList = "", bool unchangedOnly = false);
496498

499+
// update from a JSON string with the same filtering semantics as updateFromFile
500+
static void updateFromJSONString(std::string const&, std::string const& paramsList = "", bool unchangedOnly = false);
501+
497502
// interface for use from the CCDB API; allows to sync objects read from CCDB with the information
498503
// stored in the registry; modifies given object as well as registry
499504
virtual void syncCCDBandRegistry(void* obj) = 0;

Common/Utils/src/ConfigurableParam.cxx

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#endif
3838
#include <cassert>
3939
#include <iostream>
40+
#include <sstream>
4041
#include <string>
4142
#include <fairlogger/Logger.h>
4243
#include <typeindex>
@@ -754,6 +755,29 @@ void ConfigurableParam::writeJSON(std::string const& filename, std::string const
754755

755756
// ------------------------------------------------------------------
756757

758+
std::string ConfigurableParam::asJSON(std::string const& keyOnly)
759+
{
760+
initPropertyTree(); // update the boost tree before writing
761+
std::ostringstream os;
762+
if (!keyOnly.empty()) { // write ini for selected key only
763+
try {
764+
boost::property_tree::ptree kTree;
765+
auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true);
766+
for (const auto& k : keys) {
767+
kTree.add_child(k, sPtree->get_child(k));
768+
}
769+
boost::property_tree::write_json(os, kTree);
770+
} catch (const boost::property_tree::ptree_bad_path& err) {
771+
LOG(fatal) << "non-existing key " << keyOnly << " provided to writeJSON";
772+
}
773+
} else {
774+
boost::property_tree::write_json(os, *sPtree);
775+
}
776+
return os.str();
777+
}
778+
779+
// ------------------------------------------------------------------
780+
757781
void ConfigurableParam::initPropertyTree()
758782
{
759783
sPtree->clear();
@@ -870,26 +894,10 @@ void ConfigurableParam::printAllRegisteredParamNames()
870894

871895
// ------------------------------------------------------------------
872896

873-
// Update the storage map of params from the given configuration file.
874-
// It can be in JSON or INI format.
875-
// If nonempty comma-separated paramsList is provided, only those params will
876-
// be updated, absence of data for any of requested params will lead to fatal
877-
// If unchangedOnly is true, then only those parameters whose provenance is kCODE will be updated
878-
// (to allow preference of run-time settings)
879-
void ConfigurableParam::updateFromFile(std::string const& configFile, std::string const& paramsList, bool unchangedOnly)
897+
namespace
898+
{
899+
void updateFromPropertyTree(boost::property_tree::ptree const& pt, std::string const& source, std::string const& paramsList, bool unchangedOnly)
880900
{
881-
if (!sIsFullyInitialized) {
882-
initialize();
883-
}
884-
885-
auto cfgfile = o2::utils::Str::trim_copy(configFile);
886-
887-
if (cfgfile.length() == 0) {
888-
return;
889-
}
890-
891-
boost::property_tree::ptree pt = ConfigurableParamReaders::readConfigFile(cfgfile);
892-
893901
std::vector<std::pair<std::string, std::string>> keyValPairs;
894902
auto request = o2::utils::Str::tokenize(paramsList, ',', true);
895903
std::unordered_map<std::string, int> requestMap;
@@ -913,7 +921,7 @@ void ConfigurableParam::updateFromFile(std::string const& configFile, std::strin
913921
auto name = subKey.first;
914922
auto value = subKey.second.get_value<std::string>();
915923
std::string key = mainKey + "." + name;
916-
if (!unchangedOnly || getProvenance(key) == kCODE) {
924+
if (!unchangedOnly || ConfigurableParam::getProvenance(key) == ConfigurableParam::kCODE) {
917925
std::pair<std::string, std::string> pair = std::make_pair(key, o2::utils::Str::trim_copy(value));
918926
keyValPairs.push_back(pair);
919927
}
@@ -928,16 +936,62 @@ void ConfigurableParam::updateFromFile(std::string const& configFile, std::strin
928936
// make sure all requested params were retrieved
929937
for (const auto& req : requestMap) {
930938
if (req.second == 0) {
931-
throw std::runtime_error(fmt::format("Param {:s} was not found in {:s}", req.first, configFile));
939+
throw std::runtime_error(fmt::format("Param {:s} was not found in {:s}", req.first, source));
932940
}
933941
}
934942

935943
try {
936-
setValues(keyValPairs);
944+
ConfigurableParam::setValues(keyValPairs);
937945
} catch (std::exception const& error) {
938946
LOG(error) << "Error while setting values " << error.what();
939947
}
940948
}
949+
} // namespace
950+
951+
// Update the storage map of params from the given configuration file.
952+
// It can be in JSON or INI format.
953+
// If nonempty comma-separated paramsList is provided, only those params will
954+
// be updated, absence of data for any of requested params will lead to fatal
955+
// If unchangedOnly is true, then only those parameters whose provenance is kCODE will be updated
956+
// (to allow preference of run-time settings)
957+
void ConfigurableParam::updateFromFile(std::string const& configFile, std::string const& paramsList, bool unchangedOnly)
958+
{
959+
if (!sIsFullyInitialized) {
960+
initialize();
961+
}
962+
963+
auto cfgfile = o2::utils::Str::trim_copy(configFile);
964+
965+
if (cfgfile.length() == 0) {
966+
return;
967+
}
968+
969+
updateFromPropertyTree(ConfigurableParamReaders::readConfigFile(cfgfile), configFile, paramsList, unchangedOnly);
970+
}
971+
972+
// ------------------------------------------------------------------
973+
974+
void ConfigurableParam::updateFromJSONString(std::string const& configJSON, std::string const& paramsList, bool unchangedOnly)
975+
{
976+
if (!sIsFullyInitialized) {
977+
initialize();
978+
}
979+
980+
auto json = o2::utils::Str::trim_copy(configJSON);
981+
if (json.length() == 0) {
982+
return;
983+
}
984+
985+
boost::property_tree::ptree pt;
986+
std::istringstream input(json);
987+
try {
988+
boost::property_tree::read_json(input, pt);
989+
} catch (const boost::property_tree::ptree_error& e) {
990+
LOG(fatal) << "Failed to read JSON config string (" << e.what() << ")";
991+
}
992+
993+
updateFromPropertyTree(pt, "provided JSON string", paramsList, unchangedOnly);
994+
}
941995

942996
// ------------------------------------------------------------------
943997
// ------------------------------------------------------------------

Common/Utils/test/testConfigurableParam.cxx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,42 @@ BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Json)
151151
std::remove(testFileName.c_str());
152152
}
153153

154+
BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_UnchangedOnly)
155+
{
156+
ConfigurableParam::setValue("TestParam.ulValue", "2");
157+
ConfigurableParam::setProvenance("TestParam", "lValue", ConfigurableParam::kCODE);
158+
ConfigurableParam::setProvenance("TestParam", "ulValue", ConfigurableParam::kRT);
159+
ConfigurableParam::updateFromJSONString(R"json({"TestParam":{"lValue":"77","ulValue":"88"}})json", "TestParam", true);
160+
161+
BOOST_CHECK_EQUAL(TestParam::Instance().lValue, 77);
162+
BOOST_CHECK_EQUAL(TestParam::Instance().ulValue, 2);
163+
}
164+
165+
BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_FromAsJSON)
166+
{
167+
ConfigurableParam::setValue("TestParam.iValue", "321");
168+
ConfigurableParam::setValue("TestParam.sValue", "json-source");
169+
ConfigurableParam::setValues({{"TestParam.map", "{7:8,9:10}"}});
170+
const auto mapBefore = TestParam::Instance().map;
171+
const auto json = ConfigurableParam::asJSON("TestParam");
172+
173+
ConfigurableParam::setValue("TestParam.iValue", "999");
174+
ConfigurableParam::setValue("TestParam.sValue", "json-modified");
175+
ConfigurableParam::setValues({{"TestParam.map", "{1:2}"}});
176+
ConfigurableParam::updateFromJSONString(json, "TestParam");
177+
178+
BOOST_CHECK_EQUAL(TestParam::Instance().iValue, 321);
179+
BOOST_CHECK_EQUAL(TestParam::Instance().sValue, "json-source");
180+
BOOST_CHECK_EQUAL(TestParam::Instance().map.size(), mapBefore.size());
181+
BOOST_CHECK_EQUAL(TestParam::Instance().map.at(7), mapBefore.at(7));
182+
BOOST_CHECK_EQUAL(TestParam::Instance().map.at(9), mapBefore.at(9));
183+
}
184+
185+
BOOST_AUTO_TEST_CASE(ConfigurableParam_JSONString_ParamsListMissing)
186+
{
187+
BOOST_CHECK_THROW(ConfigurableParam::updateFromJSONString(ConfigurableParam::asJSON("TestParam"), "MissingParam"), std::runtime_error);
188+
}
189+
154190
BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_ROOT)
155191
{
156192
// test for root file serialization

0 commit comments

Comments
 (0)