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     virtual ~ConfigBase() = default;
54 
55     explicit ConfigBase(const json& jsonObj)
56     {
57         // Set the name of this configuration object
58         setName(jsonObj);
59         if (jsonObj.contains("profiles"))
60         {
61             for (const auto& profile : jsonObj["profiles"])
62             {
63                 _profiles.emplace_back(profile.get<std::string>());
64             }
65         }
66     }
67 
68     /**
69      * Copy Constructor
70      * Creates a config base from another config base's originally parsed JSON
71      * object data
72      *
73      * @param[in] origObj - Original ConfigBase object to be created from
74      */
75     ConfigBase(const ConfigBase& origObj)
76     {
77         _name = origObj._name;
78         _profiles = origObj._profiles;
79     }
80 
81     /**
82      * @brief Get the configuration object's name
83      *
84      * @return Name of the configuration object
85      */
86     inline const std::string& getName() const
87     {
88         return _name;
89     }
90 
91     /**
92      * @brief Get the configuration object's list of profiles
93      *
94      * Gets the list of profiles this configuration object belongs to if any
95      * are configured, otherwise an empty list of profiles results in the
96      * object always being included in the configuration.
97      *
98      * @return List of profiles the configuration object belongs to
99      */
100     inline const auto& getProfiles() const
101     {
102         return _profiles;
103     }
104 
105   protected:
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     /* Name of the configuration object */
147     std::string _name;
148 
149     /**
150      * Profiles this configuration object belongs to (OPTIONAL).
151      * Otherwise always include this object in the configuration
152      * when no profiles are given
153      */
154     std::vector<std::string> _profiles;
155 
156   private:
157     /**
158      * @brief Sets the configuration object's name from the given JSON
159      *
160      * @param[in] jsonObj - JSON to get configuration object's name from
161      */
162     inline void setName(const json& jsonObj)
163     {
164         if (!jsonObj.contains("name"))
165         {
166             // Log error on missing configuration object's name
167             log<level::ERR>("Missing required configuration object's name",
168                             entry("JSON=%s", jsonObj.dump().c_str()));
169             throw std::runtime_error(
170                 "Missing required configuration object's name");
171         }
172         _name = jsonObj["name"].get<std::string>();
173     }
174 };
175 
176 } // namespace phosphor::fan::control::json
177