1 /**
2  * Copyright © 2020 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <nlohmann/json.hpp>
19 #include <phosphor-logging/log.hpp>
20 
21 #include <vector>
22 
23 namespace phosphor::fan::control::json
24 {
25 
26 using json = nlohmann::json;
27 using namespace phosphor::logging;
28 
29 using PropertyVariantType =
30     std::variant<bool, int32_t, int64_t, double, std::string>;
31 
32 /**
33  * Configuration object key to uniquely map to the configuration object
34  * Pair constructed of:
35  *      std::string = Configuration object's name
36  *      std::vector<std::string> = List of profiles the configuration object
37  *                                 is included in
38  */
39 using configKey = std::pair<std::string, std::vector<std::string>>;
40 
41 /**
42  * @class ConfigBase - Base configuration object
43  *
44  * Base class for fan control's JSON configuration objects.
45  */
46 class ConfigBase
47 {
48   public:
49     ConfigBase() = delete;
50     ConfigBase(ConfigBase&&) = delete;
51     ConfigBase& operator=(const ConfigBase&) = delete;
52     ConfigBase& operator=(ConfigBase&&) = delete;
53 
54     virtual ~ConfigBase() = default;
55 
56     explicit ConfigBase(const json& jsonObj)
57     {
58         // Set the name of this configuration object
59         setName(jsonObj);
60         if (jsonObj.contains("profiles"))
61         {
62             for (const auto& profile : jsonObj["profiles"])
63             {
64                 _profiles.emplace_back(profile.get<std::string>());
65             }
66         }
67     }
68 
69     /**
70      * Copy Constructor
71      * Creates a config base from another config base's originally parsed JSON
72      * object data
73      *
74      * @param[in] origObj - Original ConfigBase object to be created from
75      */
76     ConfigBase(const ConfigBase& origObj)
77     {
78         _name = origObj._name;
79         _profiles = origObj._profiles;
80     }
81 
82     /**
83      * @brief Get the configuration object's name
84      *
85      * @return Name of the configuration object
86      */
87     inline const std::string& getName() const
88     {
89         return _name;
90     }
91 
92     /**
93      * @brief Get the configuration object's list of profiles
94      *
95      * Gets the list of profiles this configuration object belongs to if any
96      * are configured, otherwise an empty list of profiles results in the
97      * object always being included in the configuration.
98      *
99      * @return List of profiles the configuration object belongs to
100      */
101     inline const auto& getProfiles() const
102     {
103         return _profiles;
104     }
105 
106     /**
107      * @brief Determines the data type of a JSON configured parameter that is
108      * used as a variant within the fan control application and returns the
109      * value as that variant.
110      * @details Retrieves a JSON object by the first derived data type that
111      * is not null. Expected data types should appear in a logical order of
112      * conversion. i.e.) uint and int could both be uint
113      *
114      * @param[in] object - A single JSON object
115      *
116      * @return A `PropertyVariantType` variant containing the JSON object's
117      * value
118      */
119     static const PropertyVariantType getJsonValue(const json& object)
120     {
121         if (auto boolPtr = object.get_ptr<const bool*>())
122         {
123             return *boolPtr;
124         }
125         if (auto intPtr = object.get_ptr<const int64_t*>())
126         {
127             return *intPtr;
128         }
129         if (auto doublePtr = object.get_ptr<const double*>())
130         {
131             return *doublePtr;
132         }
133         if (auto stringPtr = object.get_ptr<const std::string*>())
134         {
135             return *stringPtr;
136         }
137 
138         log<level::ERR>(
139             "Unsupported data type for JSON object's value",
140             entry("JSON_ENTRY=%s", object.dump().c_str()),
141             entry("SUPPORTED_TYPES=%s", "{bool, int, double, string}"));
142         throw std::runtime_error(
143             "Unsupported data type for JSON object's value");
144     }
145 
146   protected:
147     /* Name of the configuration object */
148     std::string _name;
149 
150     /**
151      * Profiles this configuration object belongs to (OPTIONAL).
152      * Otherwise always include this object in the configuration
153      * when no profiles are given
154      */
155     std::vector<std::string> _profiles;
156 
157   private:
158     /**
159      * @brief Sets the configuration object's name from the given JSON
160      *
161      * @param[in] jsonObj - JSON to get configuration object's name from
162      */
163     inline void setName(const json& jsonObj)
164     {
165         if (!jsonObj.contains("name"))
166         {
167             // Log error on missing configuration object's name
168             log<level::ERR>("Missing required configuration object's name",
169                             entry("JSON=%s", jsonObj.dump().c_str()));
170             throw std::runtime_error(
171                 "Missing required configuration object's name");
172         }
173         _name = jsonObj["name"].get<std::string>();
174     }
175 };
176 
177 } // namespace phosphor::fan::control::json
178