cleanup: move all dbus interface functions in own files and namespace

Improving maintainability by 'separation of concern'.

Change-Id: I2797813c44ca0d70d03c8115035adc2c5efae4fe
Signed-off-by: Christopher Meis <christopher.meis@9elements.com>
diff --git a/src/dbus_interface.cpp b/src/dbus_interface.cpp
new file mode 100644
index 0000000..8c975bb
--- /dev/null
+++ b/src/dbus_interface.cpp
@@ -0,0 +1,395 @@
+#include "dbus_interface.hpp"
+
+#include "perform_probe.hpp"
+#include "utils.hpp"
+
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/container/flat_map.hpp>
+
+#include <regex>
+#include <string>
+#include <vector>
+
+using JsonVariantType =
+    std::variant<std::vector<std::string>, std::vector<double>, std::string,
+                 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
+                 uint16_t, uint8_t, bool>;
+
+namespace dbus_interface
+{
+
+const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
+const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
+
+// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
+// store reference to all interfaces so we can destroy them later
+boost::container::flat_map<
+    std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
+    inventory;
+// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
+
+void tryIfaceInitialize(std::shared_ptr<sdbusplus::asio::dbus_interface>& iface)
+{
+    try
+    {
+        iface->initialize();
+    }
+    catch (std::exception& e)
+    {
+        std::cerr << "Unable to initialize dbus interface : " << e.what()
+                  << "\n"
+                  << "object Path : " << iface->get_object_path() << "\n"
+                  << "interface name : " << iface->get_interface_name() << "\n";
+    }
+}
+
+std::shared_ptr<sdbusplus::asio::dbus_interface> createInterface(
+    sdbusplus::asio::object_server& objServer, const std::string& path,
+    const std::string& interface, const std::string& parent, bool checkNull)
+{
+    // on first add we have no reason to check for null before add, as there
+    // won't be any. For dynamically added interfaces, we check for null so that
+    // a constant delete/add will not create a memory leak
+
+    auto ptr = objServer.add_interface(path, interface);
+    auto& dataVector = inventory[parent];
+    if (checkNull)
+    {
+        auto it = std::find_if(dataVector.begin(), dataVector.end(),
+                               [](const auto& p) { return p.expired(); });
+        if (it != dataVector.end())
+        {
+            *it = ptr;
+            return ptr;
+        }
+    }
+    dataVector.emplace_back(ptr);
+    return ptr;
+}
+
+void createDeleteObjectMethod(
+    const std::string& jsonPointerPath,
+    const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
+    sdbusplus::asio::object_server& objServer,
+    nlohmann::json& systemConfiguration)
+{
+    std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
+    iface->register_method(
+        "Delete", [&objServer, &systemConfiguration, interface,
+                   jsonPointerPath{std::string(jsonPointerPath)}]() {
+            std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
+                interface.lock();
+            if (!dbusInterface)
+            {
+                // this technically can't happen as the pointer is pointing to
+                // us
+                throw DBusInternalError();
+            }
+            nlohmann::json::json_pointer ptr(jsonPointerPath);
+            systemConfiguration[ptr] = nullptr;
+
+            // todo(james): dig through sdbusplus to find out why we can't
+            // delete it in a method call
+            boost::asio::post(io, [&objServer, dbusInterface]() mutable {
+                objServer.remove_interface(dbusInterface);
+            });
+
+            if (!configuration::writeJsonFiles(systemConfiguration))
+            {
+                std::cerr << "error setting json file\n";
+                throw DBusInternalError();
+            }
+        });
+}
+
+// adds simple json types to interface's properties
+void populateInterfaceFromJson(
+    nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
+    std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
+    nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
+    sdbusplus::asio::PropertyPermission permission)
+{
+    for (const auto& [key, value] : dict.items())
+    {
+        auto type = value.type();
+        bool array = false;
+        if (value.type() == nlohmann::json::value_t::array)
+        {
+            array = true;
+            if (value.empty())
+            {
+                continue;
+            }
+            type = value[0].type();
+            bool isLegal = true;
+            for (const auto& arrayItem : value)
+            {
+                if (arrayItem.type() != type)
+                {
+                    isLegal = false;
+                    break;
+                }
+            }
+            if (!isLegal)
+            {
+                std::cerr << "dbus format error" << value << "\n";
+                continue;
+            }
+        }
+        if (type == nlohmann::json::value_t::object)
+        {
+            continue; // handled elsewhere
+        }
+
+        std::string path = jsonPointerPath;
+        path.append("/").append(key);
+        if (permission == sdbusplus::asio::PropertyPermission::readWrite)
+        {
+            // all setable numbers are doubles as it is difficult to always
+            // create a configuration file with all whole numbers as decimals
+            // i.e. 1.0
+            if (array)
+            {
+                if (value[0].is_number())
+                {
+                    type = nlohmann::json::value_t::number_float;
+                }
+            }
+            else if (value.is_number())
+            {
+                type = nlohmann::json::value_t::number_float;
+            }
+        }
+
+        switch (type)
+        {
+            case (nlohmann::json::value_t::boolean):
+            {
+                if (array)
+                {
+                    // todo: array of bool isn't detected correctly by
+                    // sdbusplus, change it to numbers
+                    addArrayToDbus<uint64_t>(key, value, iface.get(),
+                                             permission, systemConfiguration,
+                                             path);
+                }
+
+                else
+                {
+                    addProperty(key, value.get<bool>(), iface.get(),
+                                systemConfiguration, path, permission);
+                }
+                break;
+            }
+            case (nlohmann::json::value_t::number_integer):
+            {
+                if (array)
+                {
+                    addArrayToDbus<int64_t>(key, value, iface.get(), permission,
+                                            systemConfiguration, path);
+                }
+                else
+                {
+                    addProperty(key, value.get<int64_t>(), iface.get(),
+                                systemConfiguration, path,
+                                sdbusplus::asio::PropertyPermission::readOnly);
+                }
+                break;
+            }
+            case (nlohmann::json::value_t::number_unsigned):
+            {
+                if (array)
+                {
+                    addArrayToDbus<uint64_t>(key, value, iface.get(),
+                                             permission, systemConfiguration,
+                                             path);
+                }
+                else
+                {
+                    addProperty(key, value.get<uint64_t>(), iface.get(),
+                                systemConfiguration, path,
+                                sdbusplus::asio::PropertyPermission::readOnly);
+                }
+                break;
+            }
+            case (nlohmann::json::value_t::number_float):
+            {
+                if (array)
+                {
+                    addArrayToDbus<double>(key, value, iface.get(), permission,
+                                           systemConfiguration, path);
+                }
+
+                else
+                {
+                    addProperty(key, value.get<double>(), iface.get(),
+                                systemConfiguration, path, permission);
+                }
+                break;
+            }
+            case (nlohmann::json::value_t::string):
+            {
+                if (array)
+                {
+                    addArrayToDbus<std::string>(key, value, iface.get(),
+                                                permission, systemConfiguration,
+                                                path);
+                }
+                else
+                {
+                    addProperty(key, value.get<std::string>(), iface.get(),
+                                systemConfiguration, path, permission);
+                }
+                break;
+            }
+            default:
+            {
+                std::cerr << "Unexpected json type in system configuration "
+                          << key << ": " << value.type_name() << "\n";
+                break;
+            }
+        }
+    }
+    if (permission == sdbusplus::asio::PropertyPermission::readWrite)
+    {
+        createDeleteObjectMethod(jsonPointerPath, iface, objServer,
+                                 systemConfiguration);
+    }
+    tryIfaceInitialize(iface);
+}
+
+void createAddObjectMethod(
+    const std::string& jsonPointerPath, const std::string& path,
+    nlohmann::json& systemConfiguration,
+    sdbusplus::asio::object_server& objServer, const std::string& board)
+{
+    std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
+        objServer, path, "xyz.openbmc_project.AddObject", board);
+
+    iface->register_method(
+        "AddObject",
+        [&systemConfiguration, &objServer,
+         jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
+         board](const boost::container::flat_map<std::string, JsonVariantType>&
+                    data) {
+            nlohmann::json::json_pointer ptr(jsonPointerPath);
+            nlohmann::json& base = systemConfiguration[ptr];
+            auto findExposes = base.find("Exposes");
+
+            if (findExposes == base.end())
+            {
+                throw std::invalid_argument("Entity must have children.");
+            }
+
+            // this will throw invalid-argument to sdbusplus if invalid json
+            nlohmann::json newData{};
+            for (const auto& item : data)
+            {
+                nlohmann::json& newJson = newData[item.first];
+                std::visit(
+                    [&newJson](auto&& val) {
+                        newJson = std::forward<decltype(val)>(val);
+                    },
+                    item.second);
+            }
+
+            auto findName = newData.find("Name");
+            auto findType = newData.find("Type");
+            if (findName == newData.end() || findType == newData.end())
+            {
+                throw std::invalid_argument("AddObject missing Name or Type");
+            }
+            const std::string* type = findType->get_ptr<const std::string*>();
+            const std::string* name = findName->get_ptr<const std::string*>();
+            if (type == nullptr || name == nullptr)
+            {
+                throw std::invalid_argument("Type and Name must be a string.");
+            }
+
+            bool foundNull = false;
+            size_t lastIndex = 0;
+            // we add in the "exposes"
+            for (const auto& expose : *findExposes)
+            {
+                if (expose.is_null())
+                {
+                    foundNull = true;
+                    continue;
+                }
+
+                if (expose["Name"] == *name && expose["Type"] == *type)
+                {
+                    throw std::invalid_argument(
+                        "Field already in JSON, not adding");
+                }
+
+                if (foundNull)
+                {
+                    continue;
+                }
+
+                lastIndex++;
+            }
+
+            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
+            if (!schemaFile.good())
+            {
+                throw std::invalid_argument(
+                    "No schema avaliable, cannot validate.");
+            }
+            nlohmann::json schema =
+                nlohmann::json::parse(schemaFile, nullptr, false, true);
+            if (schema.is_discarded())
+            {
+                std::cerr << "Schema not legal" << *type << ".json\n";
+                throw DBusInternalError();
+            }
+            if (!configuration::validateJson(schema, newData))
+            {
+                throw std::invalid_argument("Data does not match schema");
+            }
+            if (foundNull)
+            {
+                findExposes->at(lastIndex) = newData;
+            }
+            else
+            {
+                findExposes->push_back(newData);
+            }
+            if (!configuration::writeJsonFiles(systemConfiguration))
+            {
+                std::cerr << "Error writing json files\n";
+                throw DBusInternalError();
+            }
+            std::string dbusName = *name;
+
+            std::regex_replace(dbusName.begin(), dbusName.begin(),
+                               dbusName.end(), illegalDbusMemberRegex, "_");
+
+            std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
+                createInterface(objServer, path + "/" + dbusName,
+                                "xyz.openbmc_project.Configuration." + *type,
+                                board, true);
+            // permission is read-write, as since we just created it, must be
+            // runtime modifiable
+            populateInterfaceFromJson(
+                systemConfiguration,
+                jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
+                interface, newData, objServer,
+                sdbusplus::asio::PropertyPermission::readWrite);
+        });
+    tryIfaceInitialize(iface);
+}
+
+std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
+    getDeviceInterfaces(const nlohmann::json& device)
+{
+    return inventory[device["Name"].get<std::string>()];
+}
+
+} // namespace dbus_interface
diff --git a/src/dbus_interface.hpp b/src/dbus_interface.hpp
new file mode 100644
index 0000000..35ea742
--- /dev/null
+++ b/src/dbus_interface.hpp
@@ -0,0 +1,123 @@
+#pragma once
+
+#include "configuration.hpp"
+
+#include <nlohmann/json.hpp>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <iostream>
+#include <set>
+#include <vector>
+
+namespace dbus_interface
+{
+void tryIfaceInitialize(
+    std::shared_ptr<sdbusplus::asio::dbus_interface>& iface);
+
+std::shared_ptr<sdbusplus::asio::dbus_interface> createInterface(
+    sdbusplus::asio::object_server& objServer, const std::string& path,
+    const std::string& interface, const std::string& parent,
+    bool checkNull = false);
+
+template <typename PropertyType>
+void addArrayToDbus(const std::string& name, const nlohmann::json& array,
+                    sdbusplus::asio::dbus_interface* iface,
+                    sdbusplus::asio::PropertyPermission permission,
+                    nlohmann::json& systemConfiguration,
+                    const std::string& jsonPointerString)
+{
+    std::vector<PropertyType> values;
+    for (const auto& property : array)
+    {
+        auto ptr = property.get_ptr<const PropertyType*>();
+        if (ptr != nullptr)
+        {
+            values.emplace_back(*ptr);
+        }
+    }
+
+    if (permission == sdbusplus::asio::PropertyPermission::readOnly)
+    {
+        iface->register_property(name, values);
+    }
+    else
+    {
+        iface->register_property(
+            name, values,
+            [&systemConfiguration,
+             jsonPointerString{std::string(jsonPointerString)}](
+                const std::vector<PropertyType>& newVal,
+                std::vector<PropertyType>& val) {
+                val = newVal;
+                if (!configuration::setJsonFromPointer(jsonPointerString, val,
+                                                       systemConfiguration))
+                {
+                    std::cerr << "error setting json field\n";
+                    return -1;
+                }
+                if (!configuration::writeJsonFiles(systemConfiguration))
+                {
+                    std::cerr << "error setting json file\n";
+                    return -1;
+                }
+                return 1;
+            });
+    }
+}
+
+template <typename PropertyType>
+void addProperty(const std::string& name, const PropertyType& value,
+                 sdbusplus::asio::dbus_interface* iface,
+                 nlohmann::json& systemConfiguration,
+                 const std::string& jsonPointerString,
+                 sdbusplus::asio::PropertyPermission permission)
+{
+    if (permission == sdbusplus::asio::PropertyPermission::readOnly)
+    {
+        iface->register_property(name, value);
+        return;
+    }
+    iface->register_property(
+        name, value,
+        [&systemConfiguration,
+         jsonPointerString{std::string(jsonPointerString)}](
+            const PropertyType& newVal, PropertyType& val) {
+            val = newVal;
+            if (!configuration::setJsonFromPointer(jsonPointerString, val,
+                                                   systemConfiguration))
+            {
+                std::cerr << "error setting json field\n";
+                return -1;
+            }
+            if (!configuration::writeJsonFiles(systemConfiguration))
+            {
+                std::cerr << "error setting json file\n";
+                return -1;
+            }
+            return 1;
+        });
+}
+
+void createDeleteObjectMethod(
+    const std::string& jsonPointerPath,
+    const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
+    sdbusplus::asio::object_server& objServer,
+    nlohmann::json& systemConfiguration);
+
+void populateInterfaceFromJson(
+    nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
+    std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
+    nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
+    sdbusplus::asio::PropertyPermission permission =
+        sdbusplus::asio::PropertyPermission::readOnly);
+
+void createAddObjectMethod(
+    const std::string& jsonPointerPath, const std::string& path,
+    nlohmann::json& systemConfiguration,
+    sdbusplus::asio::object_server& objServer, const std::string& board);
+
+std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
+    getDeviceInterfaces(const nlohmann::json& device);
+
+} // namespace dbus_interface
diff --git a/src/entity_manager.cpp b/src/entity_manager.cpp
index e4e458e..5f5ec06 100644
--- a/src/entity_manager.cpp
+++ b/src/entity_manager.cpp
@@ -18,6 +18,7 @@
 #include "entity_manager.hpp"
 
 #include "configuration.hpp"
+#include "dbus_interface.hpp"
 #include "overlay.hpp"
 #include "perform_scan.hpp"
 #include "topology.hpp"
@@ -52,16 +53,8 @@
 
 static constexpr std::array<const char*, 6> settableInterfaces = {
     "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
-using JsonVariantType =
-    std::variant<std::vector<std::string>, std::vector<double>, std::string,
-                 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
-                 uint16_t, uint8_t, bool>;
 
 // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
-// store reference to all interfaces so we can destroy them later
-boost::container::flat_map<
-    std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
-    inventory;
 
 // todo: pass this through nicer
 std::shared_ptr<sdbusplus::asio::connection> systemBus;
@@ -74,318 +67,6 @@
 const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
 const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
 
-void tryIfaceInitialize(std::shared_ptr<sdbusplus::asio::dbus_interface>& iface)
-{
-    try
-    {
-        iface->initialize();
-    }
-    catch (std::exception& e)
-    {
-        std::cerr << "Unable to initialize dbus interface : " << e.what()
-                  << "\n"
-                  << "object Path : " << iface->get_object_path() << "\n"
-                  << "interface name : " << iface->get_interface_name() << "\n";
-    }
-}
-
-static std::shared_ptr<sdbusplus::asio::dbus_interface> createInterface(
-    sdbusplus::asio::object_server& objServer, const std::string& path,
-    const std::string& interface, const std::string& parent,
-    bool checkNull = false)
-{
-    // on first add we have no reason to check for null before add, as there
-    // won't be any. For dynamically added interfaces, we check for null so that
-    // a constant delete/add will not create a memory leak
-
-    auto ptr = objServer.add_interface(path, interface);
-    auto& dataVector = inventory[parent];
-    if (checkNull)
-    {
-        auto it = std::find_if(dataVector.begin(), dataVector.end(),
-                               [](const auto& p) { return p.expired(); });
-        if (it != dataVector.end())
-        {
-            *it = ptr;
-            return ptr;
-        }
-    }
-    dataVector.emplace_back(ptr);
-    return ptr;
-}
-
-// template function to add array as dbus property
-template <typename PropertyType>
-void addArrayToDbus(const std::string& name, const nlohmann::json& array,
-                    sdbusplus::asio::dbus_interface* iface,
-                    sdbusplus::asio::PropertyPermission permission,
-                    nlohmann::json& systemConfiguration,
-                    const std::string& jsonPointerString)
-{
-    std::vector<PropertyType> values;
-    for (const auto& property : array)
-    {
-        auto ptr = property.get_ptr<const PropertyType*>();
-        if (ptr != nullptr)
-        {
-            values.emplace_back(*ptr);
-        }
-    }
-
-    if (permission == sdbusplus::asio::PropertyPermission::readOnly)
-    {
-        iface->register_property(name, values);
-    }
-    else
-    {
-        iface->register_property(
-            name, values,
-            [&systemConfiguration,
-             jsonPointerString{std::string(jsonPointerString)}](
-                const std::vector<PropertyType>& newVal,
-                std::vector<PropertyType>& val) {
-                val = newVal;
-                if (!configuration::setJsonFromPointer(jsonPointerString, val,
-                                                       systemConfiguration))
-                {
-                    std::cerr << "error setting json field\n";
-                    return -1;
-                }
-                if (!configuration::writeJsonFiles(systemConfiguration))
-                {
-                    std::cerr << "error setting json file\n";
-                    return -1;
-                }
-                return 1;
-            });
-    }
-}
-
-template <typename PropertyType>
-void addProperty(const std::string& name, const PropertyType& value,
-                 sdbusplus::asio::dbus_interface* iface,
-                 nlohmann::json& systemConfiguration,
-                 const std::string& jsonPointerString,
-                 sdbusplus::asio::PropertyPermission permission)
-{
-    if (permission == sdbusplus::asio::PropertyPermission::readOnly)
-    {
-        iface->register_property(name, value);
-        return;
-    }
-    iface->register_property(
-        name, value,
-        [&systemConfiguration,
-         jsonPointerString{std::string(jsonPointerString)}](
-            const PropertyType& newVal, PropertyType& val) {
-            val = newVal;
-            if (!configuration::setJsonFromPointer(jsonPointerString, val,
-                                                   systemConfiguration))
-            {
-                std::cerr << "error setting json field\n";
-                return -1;
-            }
-            if (!configuration::writeJsonFiles(systemConfiguration))
-            {
-                std::cerr << "error setting json file\n";
-                return -1;
-            }
-            return 1;
-        });
-}
-
-void createDeleteObjectMethod(
-    const std::string& jsonPointerPath,
-    const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
-    sdbusplus::asio::object_server& objServer,
-    nlohmann::json& systemConfiguration)
-{
-    std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
-    iface->register_method(
-        "Delete", [&objServer, &systemConfiguration, interface,
-                   jsonPointerPath{std::string(jsonPointerPath)}]() {
-            std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
-                interface.lock();
-            if (!dbusInterface)
-            {
-                // this technically can't happen as the pointer is pointing to
-                // us
-                throw DBusInternalError();
-            }
-            nlohmann::json::json_pointer ptr(jsonPointerPath);
-            systemConfiguration[ptr] = nullptr;
-
-            // todo(james): dig through sdbusplus to find out why we can't
-            // delete it in a method call
-            boost::asio::post(io, [&objServer, dbusInterface]() mutable {
-                objServer.remove_interface(dbusInterface);
-            });
-
-            if (!configuration::writeJsonFiles(systemConfiguration))
-            {
-                std::cerr << "error setting json file\n";
-                throw DBusInternalError();
-            }
-        });
-}
-
-// adds simple json types to interface's properties
-void populateInterfaceFromJson(
-    nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
-    std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
-    nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
-    sdbusplus::asio::PropertyPermission permission =
-        sdbusplus::asio::PropertyPermission::readOnly)
-{
-    for (const auto& [key, value] : dict.items())
-    {
-        auto type = value.type();
-        bool array = false;
-        if (value.type() == nlohmann::json::value_t::array)
-        {
-            array = true;
-            if (value.empty())
-            {
-                continue;
-            }
-            type = value[0].type();
-            bool isLegal = true;
-            for (const auto& arrayItem : value)
-            {
-                if (arrayItem.type() != type)
-                {
-                    isLegal = false;
-                    break;
-                }
-            }
-            if (!isLegal)
-            {
-                std::cerr << "dbus format error" << value << "\n";
-                continue;
-            }
-        }
-        if (type == nlohmann::json::value_t::object)
-        {
-            continue; // handled elsewhere
-        }
-
-        std::string path = jsonPointerPath;
-        path.append("/").append(key);
-        if (permission == sdbusplus::asio::PropertyPermission::readWrite)
-        {
-            // all setable numbers are doubles as it is difficult to always
-            // create a configuration file with all whole numbers as decimals
-            // i.e. 1.0
-            if (array)
-            {
-                if (value[0].is_number())
-                {
-                    type = nlohmann::json::value_t::number_float;
-                }
-            }
-            else if (value.is_number())
-            {
-                type = nlohmann::json::value_t::number_float;
-            }
-        }
-
-        switch (type)
-        {
-            case (nlohmann::json::value_t::boolean):
-            {
-                if (array)
-                {
-                    // todo: array of bool isn't detected correctly by
-                    // sdbusplus, change it to numbers
-                    addArrayToDbus<uint64_t>(key, value, iface.get(),
-                                             permission, systemConfiguration,
-                                             path);
-                }
-
-                else
-                {
-                    addProperty(key, value.get<bool>(), iface.get(),
-                                systemConfiguration, path, permission);
-                }
-                break;
-            }
-            case (nlohmann::json::value_t::number_integer):
-            {
-                if (array)
-                {
-                    addArrayToDbus<int64_t>(key, value, iface.get(), permission,
-                                            systemConfiguration, path);
-                }
-                else
-                {
-                    addProperty(key, value.get<int64_t>(), iface.get(),
-                                systemConfiguration, path,
-                                sdbusplus::asio::PropertyPermission::readOnly);
-                }
-                break;
-            }
-            case (nlohmann::json::value_t::number_unsigned):
-            {
-                if (array)
-                {
-                    addArrayToDbus<uint64_t>(key, value, iface.get(),
-                                             permission, systemConfiguration,
-                                             path);
-                }
-                else
-                {
-                    addProperty(key, value.get<uint64_t>(), iface.get(),
-                                systemConfiguration, path,
-                                sdbusplus::asio::PropertyPermission::readOnly);
-                }
-                break;
-            }
-            case (nlohmann::json::value_t::number_float):
-            {
-                if (array)
-                {
-                    addArrayToDbus<double>(key, value, iface.get(), permission,
-                                           systemConfiguration, path);
-                }
-
-                else
-                {
-                    addProperty(key, value.get<double>(), iface.get(),
-                                systemConfiguration, path, permission);
-                }
-                break;
-            }
-            case (nlohmann::json::value_t::string):
-            {
-                if (array)
-                {
-                    addArrayToDbus<std::string>(key, value, iface.get(),
-                                                permission, systemConfiguration,
-                                                path);
-                }
-                else
-                {
-                    addProperty(key, value.get<std::string>(), iface.get(),
-                                systemConfiguration, path, permission);
-                }
-                break;
-            }
-            default:
-            {
-                std::cerr << "Unexpected json type in system configuration "
-                          << key << ": " << value.type_name() << "\n";
-                break;
-            }
-        }
-    }
-    if (permission == sdbusplus::asio::PropertyPermission::readWrite)
-    {
-        createDeleteObjectMethod(jsonPointerPath, iface, objServer,
-                                 systemConfiguration);
-    }
-    tryIfaceInitialize(iface);
-}
-
 sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
 {
     return std::find(settableInterfaces.begin(), settableInterfaces.end(),
@@ -394,134 +75,6 @@
                : sdbusplus::asio::PropertyPermission::readOnly;
 }
 
-void createAddObjectMethod(
-    const std::string& jsonPointerPath, const std::string& path,
-    nlohmann::json& systemConfiguration,
-    sdbusplus::asio::object_server& objServer, const std::string& board)
-{
-    std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
-        objServer, path, "xyz.openbmc_project.AddObject", board);
-
-    iface->register_method(
-        "AddObject",
-        [&systemConfiguration, &objServer,
-         jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
-         board](const boost::container::flat_map<std::string, JsonVariantType>&
-                    data) {
-            nlohmann::json::json_pointer ptr(jsonPointerPath);
-            nlohmann::json& base = systemConfiguration[ptr];
-            auto findExposes = base.find("Exposes");
-
-            if (findExposes == base.end())
-            {
-                throw std::invalid_argument("Entity must have children.");
-            }
-
-            // this will throw invalid-argument to sdbusplus if invalid json
-            nlohmann::json newData{};
-            for (const auto& item : data)
-            {
-                nlohmann::json& newJson = newData[item.first];
-                std::visit(
-                    [&newJson](auto&& val) {
-                        newJson = std::forward<decltype(val)>(val);
-                    },
-                    item.second);
-            }
-
-            auto findName = newData.find("Name");
-            auto findType = newData.find("Type");
-            if (findName == newData.end() || findType == newData.end())
-            {
-                throw std::invalid_argument("AddObject missing Name or Type");
-            }
-            const std::string* type = findType->get_ptr<const std::string*>();
-            const std::string* name = findName->get_ptr<const std::string*>();
-            if (type == nullptr || name == nullptr)
-            {
-                throw std::invalid_argument("Type and Name must be a string.");
-            }
-
-            bool foundNull = false;
-            size_t lastIndex = 0;
-            // we add in the "exposes"
-            for (const auto& expose : *findExposes)
-            {
-                if (expose.is_null())
-                {
-                    foundNull = true;
-                    continue;
-                }
-
-                if (expose["Name"] == *name && expose["Type"] == *type)
-                {
-                    throw std::invalid_argument(
-                        "Field already in JSON, not adding");
-                }
-
-                if (foundNull)
-                {
-                    continue;
-                }
-
-                lastIndex++;
-            }
-
-            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
-            if (!schemaFile.good())
-            {
-                throw std::invalid_argument(
-                    "No schema avaliable, cannot validate.");
-            }
-            nlohmann::json schema =
-                nlohmann::json::parse(schemaFile, nullptr, false, true);
-            if (schema.is_discarded())
-            {
-                std::cerr << "Schema not legal" << *type << ".json\n";
-                throw DBusInternalError();
-            }
-            if (!configuration::validateJson(schema, newData))
-            {
-                throw std::invalid_argument("Data does not match schema");
-            }
-            if (foundNull)
-            {
-                findExposes->at(lastIndex) = newData;
-            }
-            else
-            {
-                findExposes->push_back(newData);
-            }
-            if (!configuration::writeJsonFiles(systemConfiguration))
-            {
-                std::cerr << "Error writing json files\n";
-                throw DBusInternalError();
-            }
-            std::string dbusName = *name;
-
-            std::regex_replace(dbusName.begin(), dbusName.begin(),
-                               dbusName.end(), illegalDbusMemberRegex, "_");
-
-            std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
-                createInterface(objServer, path + "/" + dbusName,
-                                "xyz.openbmc_project.Configuration." + *type,
-                                board, true);
-            // permission is read-write, as since we just created it, must be
-            // runtime modifiable
-            populateInterfaceFromJson(
-                systemConfiguration,
-                jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
-                interface, newData, objServer,
-                sdbusplus::asio::PropertyPermission::readWrite);
-        });
-    tryIfaceInitialize(iface);
-}
-
 void postToDbus(const nlohmann::json& newConfiguration,
                 nlohmann::json& systemConfiguration,
                 sdbusplus::asio::object_server& objServer)
@@ -563,19 +116,23 @@
         boardPath += boardName;
 
         std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
-            createInterface(objServer, boardPath,
-                            "xyz.openbmc_project.Inventory.Item", boardName);
+            dbus_interface::createInterface(
+                objServer, boardPath, "xyz.openbmc_project.Inventory.Item",
+                boardName);
 
         std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
-            createInterface(objServer, boardPath,
-                            "xyz.openbmc_project.Inventory.Item." + boardType,
-                            boardNameOrig);
+            dbus_interface::createInterface(
+                objServer, boardPath,
+                "xyz.openbmc_project.Inventory.Item." + boardType,
+                boardNameOrig);
 
-        createAddObjectMethod(jsonPointerPath, boardPath, systemConfiguration,
-                              objServer, boardNameOrig);
+        dbus_interface::createAddObjectMethod(
+            jsonPointerPath, boardPath, systemConfiguration, objServer,
+            boardNameOrig);
 
-        populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
-                                  boardIface, boardValues, objServer);
+        dbus_interface::populateInterfaceFromJson(
+            systemConfiguration, jsonPointerPath, boardIface, boardValues,
+            objServer);
         jsonPointerPath += "/";
         // iterate through board properties
         for (const auto& [propName, propValue] : boardValues.items())
@@ -583,12 +140,12 @@
             if (propValue.type() == nlohmann::json::value_t::object)
             {
                 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
-                    createInterface(objServer, boardPath, propName,
-                                    boardNameOrig);
+                    dbus_interface::createInterface(objServer, boardPath,
+                                                    propName, boardNameOrig);
 
-                populateInterfaceFromJson(systemConfiguration,
-                                          jsonPointerPath + propName, iface,
-                                          propValue, objServer);
+                dbus_interface::populateInterfaceFromJson(
+                    systemConfiguration, jsonPointerPath + propName, iface,
+                    propValue, objServer);
             }
         }
 
@@ -646,22 +203,24 @@
             if (itemType == "BMC")
             {
                 std::shared_ptr<sdbusplus::asio::dbus_interface> bmcIface =
-                    createInterface(objServer, ifacePath,
-                                    "xyz.openbmc_project.Inventory.Item.Bmc",
-                                    boardNameOrig);
-                populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
-                                          bmcIface, item, objServer,
-                                          getPermission(itemType));
+                    dbus_interface::createInterface(
+                        objServer, ifacePath,
+                        "xyz.openbmc_project.Inventory.Item.Bmc",
+                        boardNameOrig);
+                dbus_interface::populateInterfaceFromJson(
+                    systemConfiguration, jsonPointerPath, bmcIface, item,
+                    objServer, getPermission(itemType));
             }
             else if (itemType == "System")
             {
                 std::shared_ptr<sdbusplus::asio::dbus_interface> systemIface =
-                    createInterface(objServer, ifacePath,
-                                    "xyz.openbmc_project.Inventory.Item.System",
-                                    boardNameOrig);
-                populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
-                                          systemIface, item, objServer,
-                                          getPermission(itemType));
+                    dbus_interface::createInterface(
+                        objServer, ifacePath,
+                        "xyz.openbmc_project.Inventory.Item.System",
+                        boardNameOrig);
+                dbus_interface::populateInterfaceFromJson(
+                    systemConfiguration, jsonPointerPath, systemIface, item,
+                    objServer, getPermission(itemType));
             }
 
             for (const auto& [name, config] : item.items())
@@ -677,10 +236,10 @@
                     ifaceName.append(itemType).append(".").append(name);
 
                     std::shared_ptr<sdbusplus::asio::dbus_interface>
-                        objectIface = createInterface(objServer, ifacePath,
-                                                      ifaceName, boardNameOrig);
+                        objectIface = dbus_interface::createInterface(
+                            objServer, ifacePath, ifaceName, boardNameOrig);
 
-                    populateInterfaceFromJson(
+                    dbus_interface::populateInterfaceFromJson(
                         systemConfiguration, jsonPointerPath, objectIface,
                         config, objServer, getPermission(name));
                 }
@@ -721,10 +280,10 @@
                         ifaceName.append(std::to_string(index));
 
                         std::shared_ptr<sdbusplus::asio::dbus_interface>
-                            objectIface = createInterface(
+                            objectIface = dbus_interface::createInterface(
                                 objServer, ifacePath, ifaceName, boardNameOrig);
 
-                        populateInterfaceFromJson(
+                        dbus_interface::populateInterfaceFromJson(
                             systemConfiguration,
                             jsonPointerPath + "/" + std::to_string(index),
                             objectIface, arrayItem, objServer,
@@ -735,13 +294,14 @@
             }
 
             std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
-                createInterface(objServer, ifacePath,
-                                "xyz.openbmc_project.Configuration." + itemType,
-                                boardNameOrig);
+                dbus_interface::createInterface(
+                    objServer, ifacePath,
+                    "xyz.openbmc_project.Configuration." + itemType,
+                    boardNameOrig);
 
-            populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
-                                      itemIface, item, objServer,
-                                      getPermission(itemType));
+            dbus_interface::populateInterfaceFromJson(
+                systemConfiguration, jsonPointerPath, itemIface, item,
+                objServer, getPermission(itemType));
 
             topology.addBoard(boardPath, boardType, boardNameOrig, item);
         }
@@ -758,12 +318,12 @@
             continue;
         }
 
-        auto ifacePtr = createInterface(
+        auto ifacePtr = dbus_interface::createInterface(
             objServer, assocPath, "xyz.openbmc_project.Association.Definitions",
             findBoard->second);
 
         ifacePtr->register_property("Associations", assocPropValue);
-        tryIfaceInitialize(ifacePtr);
+        dbus_interface::tryIfaceInitialize(ifacePtr);
     }
 }
 
@@ -844,12 +404,6 @@
         });
 }
 
-static std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
-    getDeviceInterfaces(const nlohmann::json& device)
-{
-    return inventory[device["Name"].get<std::string>()];
-}
-
 static void pruneConfiguration(nlohmann::json& systemConfiguration,
                                sdbusplus::asio::object_server& objServer,
                                bool powerOff, const std::string& name,
@@ -861,7 +415,7 @@
         return;
     }
 
-    auto& ifaces = getDeviceInterfaces(device);
+    auto& ifaces = dbus_interface::getDeviceInterfaces(device);
     for (auto& iface : ifaces)
     {
         auto sharedPtr = iface.lock();
@@ -1104,7 +658,7 @@
     entityIface->register_method("ReScan", [&]() {
         propertiesChangedCallback(systemConfiguration, objServer);
     });
-    tryIfaceInitialize(entityIface);
+    dbus_interface::tryIfaceInitialize(entityIface);
 
     if (fwVersionIsSame())
     {
diff --git a/src/meson.build b/src/meson.build
index e4c2759..3699484 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -7,6 +7,7 @@
     'entity_manager.cpp',
     'configuration.cpp',
     'expression.cpp',
+    'dbus_interface.cpp',
     'perform_scan.cpp',
     'perform_probe.cpp',
     'overlay.cpp',