control: Complete profile configuration parsing

Profiles' have an active state that are determined by a given method
within the JSON configuration. Each method for determining a profile's
active state is mapped from its JSON configuration method name to the
handler function that is implemented to parse and return the profile's
active state based on the configuration. At this time, only an `all_of`
active state method is supported where all of the provided list of dbus
properties must match their given value for the profile to be active.

Tested:
    Profile method parsed and active state set appropriately

Change-Id: I258ecabccb849b2fa2662a157d6cb108af2c9886
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/control/json/profile.cpp b/control/json/profile.cpp
index f7f80b1..696716e 100644
--- a/control/json/profile.cpp
+++ b/control/json/profile.cpp
@@ -15,18 +15,94 @@
  */
 #include "profile.hpp"
 
+#include "sdbusplus.hpp"
+
 #include <nlohmann/json.hpp>
 #include <phosphor-logging/log.hpp>
 #include <sdbusplus/bus.hpp>
 
+#include <algorithm>
+#include <iterator>
+#include <numeric>
+
 namespace phosphor::fan::control::json
 {
 
 using json = nlohmann::json;
 using namespace phosphor::logging;
 
+// String key must be in all lowercase for method lookup
+const std::map<std::string, methodHandler> Profile::_methods = {
+    {"all_of", Profile::allOf}};
+
 Profile::Profile(sdbusplus::bus::bus& bus, const json& jsonObj) :
-    ConfigBase(jsonObj), _bus(bus)
-{}
+    ConfigBase(jsonObj), _bus(bus), _active(false)
+{
+    setActive(jsonObj);
+}
+
+void Profile::setActive(const json& jsonObj)
+{
+    if (!jsonObj.contains("method") || !jsonObj["method"].contains("name"))
+    {
+        // Log error on missing profile method
+        log<level::ERR>("Missing required profile method",
+                        entry("JSON=%s", jsonObj.dump().c_str()));
+        throw std::runtime_error("Missing required profile method");
+    }
+    // The method to use in determining if the profile is active
+    auto method = jsonObj["method"]["name"].get<std::string>();
+    std::transform(method.begin(), method.end(), method.begin(), tolower);
+    auto handler = _methods.find(method);
+    if (handler != _methods.end())
+    {
+        // Call method for determining profile's active state
+        _active = handler->second(jsonObj["method"]);
+    }
+    else
+    {
+        // Construct list of available methods
+        auto methods = std::accumulate(
+            std::next(_methods.begin()), _methods.end(),
+            _methods.begin()->first, [](auto list, auto method) {
+                return std::move(list) + ", " + method.first;
+            });
+        log<level::ERR>("Configured method not available",
+                        entry("JSON=%s", jsonObj["method"].dump().c_str()),
+                        entry("METHODS_AVAILABLE=%s", methods.c_str()));
+    }
+}
+
+bool Profile::allOf(const json& method)
+{
+    if (!method.contains("properties"))
+    {
+        log<level::ERR>("Missing required all_of method properties list",
+                        entry("JSON=%s", method.dump().c_str()));
+        throw std::runtime_error(
+            "Missing required all_of method properties list");
+    }
+
+    return std::all_of(
+        method["properties"].begin(), method["properties"].end(),
+        [](const json& obj) {
+            if (!obj.contains("path") || !obj.contains("interface") ||
+                !obj.contains("property") || !obj.contains("value"))
+            {
+                log<level::ERR>(
+                    "Missing required all_of method property parameters",
+                    entry("JSON=%s", obj.dump().c_str()));
+                throw std::runtime_error(
+                    "Missing required all_of method parameters");
+            }
+            auto variant =
+                util::SDBusPlus::getPropertyVariant<PropertyVariantType>(
+                    obj["path"].get<std::string>(),
+                    obj["interface"].get<std::string>(),
+                    obj["property"].get<std::string>());
+
+            return getJsonValue(obj["value"]) == variant;
+        });
+}
 
 } // namespace phosphor::fan::control::json