cleanup: move json configuration functions in their own files

Improving maintainability by increasing 'separation of concern'.

Change-Id: I80355abf741c037f8834527e3304c80b83c8166e
Signed-off-by: Christopher Meis <christopher.meis@9elements.com>
diff --git a/src/configuration.cpp b/src/configuration.cpp
new file mode 100644
index 0000000..8646f57
--- /dev/null
+++ b/src/configuration.cpp
@@ -0,0 +1,197 @@
+#include "configuration.hpp"
+
+#include "perform_probe.hpp"
+#include "utils.hpp"
+
+#include <nlohmann/json.hpp>
+#include <valijson/adapters/nlohmann_json_adapter.hpp>
+#include <valijson/schema.hpp>
+#include <valijson/schema_parser.hpp>
+#include <valijson/validator.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <list>
+#include <string>
+#include <vector>
+
+namespace configuration
+{
+// writes output files to persist data
+bool writeJsonFiles(const nlohmann::json& systemConfiguration)
+{
+    std::filesystem::create_directory(configurationOutDir);
+    std::ofstream output(currentConfiguration);
+    if (!output.good())
+    {
+        return false;
+    }
+    output << systemConfiguration.dump(4);
+    output.close();
+    return true;
+}
+
+// reads json files out of the filesystem
+bool loadConfigurations(std::list<nlohmann::json>& configurations)
+{
+    // find configuration files
+    std::vector<std::filesystem::path> jsonPaths;
+    if (!findFiles(
+            std::vector<std::filesystem::path>{configurationDirectory,
+                                               hostConfigurationDirectory},
+            R"(.*\.json)", jsonPaths))
+    {
+        std::cerr << "Unable to find any configuration files in "
+                  << configurationDirectory << "\n";
+        return false;
+    }
+
+    std::ifstream schemaStream(
+        std::string(schemaDirectory) + "/" + globalSchema);
+    if (!schemaStream.good())
+    {
+        std::cerr
+            << "Cannot open schema file,  cannot validate JSON, exiting\n\n";
+        std::exit(EXIT_FAILURE);
+        return false;
+    }
+    nlohmann::json schema =
+        nlohmann::json::parse(schemaStream, nullptr, false, true);
+    if (schema.is_discarded())
+    {
+        std::cerr
+            << "Illegal schema file detected, cannot validate JSON, exiting\n";
+        std::exit(EXIT_FAILURE);
+        return false;
+    }
+
+    for (auto& jsonPath : jsonPaths)
+    {
+        std::ifstream jsonStream(jsonPath.c_str());
+        if (!jsonStream.good())
+        {
+            std::cerr << "unable to open " << jsonPath.string() << "\n";
+            continue;
+        }
+        auto data = nlohmann::json::parse(jsonStream, nullptr, false, true);
+        if (data.is_discarded())
+        {
+            std::cerr << "syntax error in " << jsonPath.string() << "\n";
+            continue;
+        }
+        /*
+        * todo(james): reenable this once less things are in flight
+        *
+        if (!validateJson(schema, data))
+        {
+            std::cerr << "Error validating " << jsonPath.string() << "\n";
+            continue;
+        }
+        */
+
+        if (data.type() == nlohmann::json::value_t::array)
+        {
+            for (auto& d : data)
+            {
+                configurations.emplace_back(d);
+            }
+        }
+        else
+        {
+            configurations.emplace_back(data);
+        }
+    }
+    return true;
+}
+
+// Iterate over new configuration and erase items from old configuration.
+void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
+                            nlohmann::json& newConfiguration)
+{
+    for (auto it = newConfiguration.begin(); it != newConfiguration.end();)
+    {
+        auto findKey = oldConfiguration.find(it.key());
+        if (findKey != oldConfiguration.end())
+        {
+            it = newConfiguration.erase(it);
+        }
+        else
+        {
+            it++;
+        }
+    }
+}
+
+// validates a given input(configuration) with a given json schema file.
+bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input)
+{
+    valijson::Schema schema;
+    valijson::SchemaParser parser;
+    valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile);
+    parser.populateSchema(schemaAdapter, schema);
+    valijson::Validator validator;
+    valijson::adapters::NlohmannJsonAdapter targetAdapter(input);
+    return validator.validate(schema, targetAdapter, nullptr);
+}
+
+// Extract the D-Bus interfaces to probe from the JSON config files.
+std::set<std::string> getProbeInterfaces()
+{
+    std::set<std::string> interfaces;
+    std::list<nlohmann::json> configurations;
+    if (!configuration::loadConfigurations(configurations))
+    {
+        return interfaces;
+    }
+
+    for (auto it = configurations.begin(); it != configurations.end();)
+    {
+        auto findProbe = it->find("Probe");
+        if (findProbe == it->end())
+        {
+            std::cerr << "configuration file missing probe:\n " << *it << "\n";
+            it++;
+            continue;
+        }
+
+        nlohmann::json probeCommand;
+        if ((*findProbe).type() != nlohmann::json::value_t::array)
+        {
+            probeCommand = nlohmann::json::array();
+            probeCommand.push_back(*findProbe);
+        }
+        else
+        {
+            probeCommand = *findProbe;
+        }
+
+        for (const nlohmann::json& probeJson : probeCommand)
+        {
+            const std::string* probe = probeJson.get_ptr<const std::string*>();
+            if (probe == nullptr)
+            {
+                std::cerr << "Probe statement wasn't a string, can't parse";
+                continue;
+            }
+            // Skip it if the probe cmd doesn't contain an interface.
+            if (probe::findProbeType(*probe))
+            {
+                continue;
+            }
+
+            // syntax requires probe before first open brace
+            auto findStart = probe->find('(');
+            if (findStart != std::string::npos)
+            {
+                std::string interface = probe->substr(0, findStart);
+                interfaces.emplace(interface);
+            }
+        }
+        it++;
+    }
+
+    return interfaces;
+}
+
+} // namespace configuration
diff --git a/src/configuration.hpp b/src/configuration.hpp
new file mode 100644
index 0000000..b3b2cdc
--- /dev/null
+++ b/src/configuration.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <nlohmann/json.hpp>
+
+#include <list>
+#include <set>
+
+namespace configuration
+{
+constexpr const char* globalSchema = "global.json";
+constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
+constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
+constexpr const char* currentConfiguration = "/var/configuration/system.json";
+constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
+
+bool writeJsonFiles(const nlohmann::json& systemConfiguration);
+
+bool loadConfigurations(std::list<nlohmann::json>& configurations);
+
+template <typename JsonType>
+bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
+                        nlohmann::json& systemConfiguration)
+{
+    try
+    {
+        nlohmann::json::json_pointer ptr(ptrStr);
+        nlohmann::json& ref = systemConfiguration[ptr];
+        ref = value;
+        return true;
+    }
+    catch (const std::out_of_range&)
+    {
+        return false;
+    }
+}
+
+void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
+                            nlohmann::json& newConfiguration);
+
+bool validateJson(const nlohmann::json& schemaFile,
+                  const nlohmann::json& input);
+
+std::set<std::string> getProbeInterfaces();
+
+} // namespace configuration
diff --git a/src/entity_manager.cpp b/src/entity_manager.cpp
index 187373a..e4e458e 100644
--- a/src/entity_manager.cpp
+++ b/src/entity_manager.cpp
@@ -17,8 +17,8 @@
 
 #include "entity_manager.hpp"
 
+#include "configuration.hpp"
 #include "overlay.hpp"
-#include "perform_probe.hpp"
 #include "perform_scan.hpp"
 #include "topology.hpp"
 #include "utils.hpp"
@@ -47,13 +47,8 @@
 #include <map>
 #include <regex>
 #include <variant>
-constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
-constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
-constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
 constexpr const char* tempConfigDir = "/tmp/configuration/";
 constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
-constexpr const char* currentConfiguration = "/var/configuration/system.json";
-constexpr const char* globalSchema = "global.json";
 
 static constexpr std::array<const char*, 6> settableInterfaces = {
     "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
@@ -119,37 +114,6 @@
     return ptr;
 }
 
-// writes output files to persist data
-bool writeJsonFiles(const nlohmann::json& systemConfiguration)
-{
-    std::filesystem::create_directory(configurationOutDir);
-    std::ofstream output(currentConfiguration);
-    if (!output.good())
-    {
-        return false;
-    }
-    output << systemConfiguration.dump(4);
-    output.close();
-    return true;
-}
-
-template <typename JsonType>
-bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
-                        nlohmann::json& systemConfiguration)
-{
-    try
-    {
-        nlohmann::json::json_pointer ptr(ptrStr);
-        nlohmann::json& ref = systemConfiguration[ptr];
-        ref = value;
-        return true;
-    }
-    catch (const std::out_of_range&)
-    {
-        return false;
-    }
-}
-
 // template function to add array as dbus property
 template <typename PropertyType>
 void addArrayToDbus(const std::string& name, const nlohmann::json& array,
@@ -181,13 +145,13 @@
                 const std::vector<PropertyType>& newVal,
                 std::vector<PropertyType>& val) {
                 val = newVal;
-                if (!setJsonFromPointer(jsonPointerString, val,
-                                        systemConfiguration))
+                if (!configuration::setJsonFromPointer(jsonPointerString, val,
+                                                       systemConfiguration))
                 {
                     std::cerr << "error setting json field\n";
                     return -1;
                 }
-                if (!writeJsonFiles(systemConfiguration))
+                if (!configuration::writeJsonFiles(systemConfiguration))
                 {
                     std::cerr << "error setting json file\n";
                     return -1;
@@ -215,13 +179,13 @@
          jsonPointerString{std::string(jsonPointerString)}](
             const PropertyType& newVal, PropertyType& val) {
             val = newVal;
-            if (!setJsonFromPointer(jsonPointerString, val,
-                                    systemConfiguration))
+            if (!configuration::setJsonFromPointer(jsonPointerString, val,
+                                                   systemConfiguration))
             {
                 std::cerr << "error setting json field\n";
                 return -1;
             }
-            if (!writeJsonFiles(systemConfiguration))
+            if (!configuration::writeJsonFiles(systemConfiguration))
             {
                 std::cerr << "error setting json file\n";
                 return -1;
@@ -257,7 +221,7 @@
                 objServer.remove_interface(dbusInterface);
             });
 
-            if (!writeJsonFiles(systemConfiguration))
+            if (!configuration::writeJsonFiles(systemConfiguration))
             {
                 std::cerr << "error setting json file\n";
                 throw DBusInternalError();
@@ -503,8 +467,9 @@
                 lastIndex++;
             }
 
-            std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
-                                     boost::to_lower_copy(*type) + ".json");
+            std::ifstream schemaFile(
+                std::string(configuration::schemaDirectory) + "/" +
+                boost::to_lower_copy(*type) + ".json");
             // todo(james) we might want to also make a list of 'can add'
             // interfaces but for now I think the assumption if there is a
             // schema avaliable that it is allowed to update is fine
@@ -520,7 +485,7 @@
                 std::cerr << "Schema not legal" << *type << ".json\n";
                 throw DBusInternalError();
             }
-            if (!validateJson(schema, newData))
+            if (!configuration::validateJson(schema, newData))
             {
                 throw std::invalid_argument("Data does not match schema");
             }
@@ -532,7 +497,7 @@
             {
                 findExposes->push_back(newData);
             }
-            if (!writeJsonFiles(systemConfiguration))
+            if (!configuration::writeJsonFiles(systemConfiguration))
             {
                 std::cerr << "Error writing json files\n";
                 throw DBusInternalError();
@@ -802,79 +767,6 @@
     }
 }
 
-// reads json files out of the filesystem
-bool loadConfigurations(std::list<nlohmann::json>& configurations)
-{
-    // find configuration files
-    std::vector<std::filesystem::path> jsonPaths;
-    if (!findFiles(
-            std::vector<std::filesystem::path>{configurationDirectory,
-                                               hostConfigurationDirectory},
-            R"(.*\.json)", jsonPaths))
-    {
-        std::cerr << "Unable to find any configuration files in "
-                  << configurationDirectory << "\n";
-        return false;
-    }
-
-    std::ifstream schemaStream(
-        std::string(schemaDirectory) + "/" + globalSchema);
-    if (!schemaStream.good())
-    {
-        std::cerr
-            << "Cannot open schema file,  cannot validate JSON, exiting\n\n";
-        std::exit(EXIT_FAILURE);
-        return false;
-    }
-    nlohmann::json schema =
-        nlohmann::json::parse(schemaStream, nullptr, false, true);
-    if (schema.is_discarded())
-    {
-        std::cerr
-            << "Illegal schema file detected, cannot validate JSON, exiting\n";
-        std::exit(EXIT_FAILURE);
-        return false;
-    }
-
-    for (auto& jsonPath : jsonPaths)
-    {
-        std::ifstream jsonStream(jsonPath.c_str());
-        if (!jsonStream.good())
-        {
-            std::cerr << "unable to open " << jsonPath.string() << "\n";
-            continue;
-        }
-        auto data = nlohmann::json::parse(jsonStream, nullptr, false, true);
-        if (data.is_discarded())
-        {
-            std::cerr << "syntax error in " << jsonPath.string() << "\n";
-            continue;
-        }
-        /*
-         * todo(james): reenable this once less things are in flight
-         *
-        if (!validateJson(schema, data))
-        {
-            std::cerr << "Error validating " << jsonPath.string() << "\n";
-            continue;
-        }
-        */
-
-        if (data.type() == nlohmann::json::value_t::array)
-        {
-            for (auto& d : data)
-            {
-                configurations.emplace_back(d);
-            }
-        }
-        else
-        {
-            configurations.emplace_back(data);
-        }
-    }
-    return true;
-}
-
 static bool deviceRequiresPowerOn(const nlohmann::json& entity)
 {
     auto powerState = entity.find("PowerState");
@@ -985,23 +877,6 @@
     logDeviceRemoved(device);
 }
 
-static void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
-                                   nlohmann::json& newConfiguration)
-{
-    for (auto it = newConfiguration.begin(); it != newConfiguration.end();)
-    {
-        auto findKey = oldConfiguration.find(it.key());
-        if (findKey != oldConfiguration.end())
-        {
-            it = newConfiguration.erase(it);
-        }
-        else
-        {
-            it++;
-        }
-    }
-}
-
 static void publishNewConfiguration(
     const size_t& instance, const size_t count,
     boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
@@ -1018,7 +893,7 @@
     loadOverlays(newConfiguration);
 
     boost::asio::post(io, [systemConfiguration]() {
-        if (!writeJsonFiles(systemConfiguration))
+        if (!configuration::writeJsonFiles(systemConfiguration))
         {
             std::cerr << "Error writing json files\n";
         }
@@ -1072,7 +947,7 @@
         *missingConfigurations = systemConfiguration;
 
         std::list<nlohmann::json> configurations;
-        if (!loadConfigurations(configurations))
+        if (!configuration::loadConfigurations(configurations))
         {
             std::cerr << "Could not load configurations\n";
             inProgress = false;
@@ -1096,7 +971,8 @@
 
                 nlohmann::json newConfiguration = systemConfiguration;
 
-                deriveNewConfiguration(oldConfiguration, newConfiguration);
+                configuration::deriveNewConfiguration(oldConfiguration,
+                                                      newConfiguration);
 
                 for (const auto& [_, device] : newConfiguration.items())
                 {
@@ -1115,65 +991,6 @@
     });
 }
 
-// Extract the D-Bus interfaces to probe from the JSON config files.
-static std::set<std::string> getProbeInterfaces()
-{
-    std::set<std::string> interfaces;
-    std::list<nlohmann::json> configurations;
-    if (!loadConfigurations(configurations))
-    {
-        return interfaces;
-    }
-
-    for (auto it = configurations.begin(); it != configurations.end();)
-    {
-        auto findProbe = it->find("Probe");
-        if (findProbe == it->end())
-        {
-            std::cerr << "configuration file missing probe:\n " << *it << "\n";
-            it++;
-            continue;
-        }
-
-        nlohmann::json probeCommand;
-        if ((*findProbe).type() != nlohmann::json::value_t::array)
-        {
-            probeCommand = nlohmann::json::array();
-            probeCommand.push_back(*findProbe);
-        }
-        else
-        {
-            probeCommand = *findProbe;
-        }
-
-        for (const nlohmann::json& probeJson : probeCommand)
-        {
-            const std::string* probe = probeJson.get_ptr<const std::string*>();
-            if (probe == nullptr)
-            {
-                std::cerr << "Probe statement wasn't a string, can't parse";
-                continue;
-            }
-            // Skip it if the probe cmd doesn't contain an interface.
-            if (probe::findProbeType(*probe))
-            {
-                continue;
-            }
-
-            // syntax requires probe before first open brace
-            auto findStart = probe->find('(');
-            if (findStart != std::string::npos)
-            {
-                std::string interface = probe->substr(0, findStart);
-                interfaces.emplace(interface);
-            }
-        }
-        it++;
-    }
-
-    return interfaces;
-}
-
 // Check if InterfacesAdded payload contains an iface that needs probing.
 static bool iaContainsProbeInterface(
     sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
@@ -1237,7 +1054,7 @@
 
     nlohmann::json systemConfiguration = nlohmann::json::object();
 
-    std::set<std::string> probeInterfaces = getProbeInterfaces();
+    std::set<std::string> probeInterfaces = configuration::getProbeInterfaces();
 
     // We need a poke from DBus for static providers that create all their
     // objects prior to claiming a well-known name, and thus don't emit any
@@ -1291,13 +1108,15 @@
 
     if (fwVersionIsSame())
     {
-        if (std::filesystem::is_regular_file(currentConfiguration))
+        if (std::filesystem::is_regular_file(
+                configuration::currentConfiguration))
         {
             // this file could just be deleted, but it's nice for debug
             std::filesystem::create_directory(tempConfigDir);
             std::filesystem::remove(lastConfiguration);
-            std::filesystem::copy(currentConfiguration, lastConfiguration);
-            std::filesystem::remove(currentConfiguration);
+            std::filesystem::copy(configuration::currentConfiguration,
+                                  lastConfiguration);
+            std::filesystem::remove(configuration::currentConfiguration);
 
             std::ifstream jsonStream(lastConfiguration);
             if (jsonStream.good())
@@ -1323,7 +1142,7 @@
     {
         // not an error, just logging at this level to make it in the journal
         std::cerr << "Clearing previous configuration\n";
-        std::filesystem::remove(currentConfiguration);
+        std::filesystem::remove(configuration::currentConfiguration);
     }
 
     // some boards only show up after power is on, we want to not say they are
diff --git a/src/meson.build b/src/meson.build
index f4b96a8..e4c2759 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -5,6 +5,7 @@
 executable(
     'entity-manager',
     'entity_manager.cpp',
+    'configuration.cpp',
     'expression.cpp',
     'perform_scan.cpp',
     'perform_probe.cpp',
diff --git a/src/utils.cpp b/src/utils.cpp
index d95fb5e..fd01016 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -28,10 +28,6 @@
 #include <boost/container/flat_map.hpp>
 #include <boost/lexical_cast.hpp>
 #include <sdbusplus/bus/match.hpp>
-#include <valijson/adapters/nlohmann_json_adapter.hpp>
-#include <valijson/schema.hpp>
-#include <valijson/schema_parser.hpp>
-#include <valijson/validator.hpp>
 
 #include <charconv>
 #include <filesystem>
@@ -130,17 +126,6 @@
     return true;
 }
 
-bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input)
-{
-    valijson::Schema schema;
-    valijson::SchemaParser parser;
-    valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile);
-    parser.populateSchema(schemaAdapter, schema);
-    valijson::Validator validator;
-    valijson::adapters::NlohmannJsonAdapter targetAdapter(input);
-    return validator.validate(schema, targetAdapter, nullptr);
-}
-
 bool isPowerOn()
 {
     if (!powerMatch)