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     /**
106      * @brief Determines the data type of a JSON configured parameter that is
107      * used as a variant within the fan control application and returns the
108      * value as that variant.
109      * @details Retrieves a JSON object by the first derived data type that
110      * is not null. Expected data types should appear in a logical order of
111      * conversion. i.e.) uint and int could both be uint
112      *
113      * @param[in] object - A single JSON object
114      *
115      * @return A `PropertyVariantType` variant containing the JSON object's
116      * value
117      */
118     static const PropertyVariantType getJsonValue(const json& object)
119     {
120         if (auto boolPtr = object.get_ptr<const bool*>())
121         {
122             return *boolPtr;
123         }
124         if (auto intPtr = object.get_ptr<const int64_t*>())
125         {
126             return *intPtr;
127         }
128         if (auto doublePtr = object.get_ptr<const double*>())
129         {
130             return *doublePtr;
131         }
132         if (auto stringPtr = object.get_ptr<const std::string*>())
133         {
134             return *stringPtr;
135         }
136 
137         log<level::ERR>(
138             "Unsupported data type for JSON object's value",
139             entry("JSON_ENTRY=%s", object.dump().c_str()),
140             entry("SUPPORTED_TYPES=%s", "{bool, int, double, string}"));
141         throw std::runtime_error(
142             "Unsupported data type for JSON object's value");
143     }
144 
145   protected:
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