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 
17 #include "user_data_json.hpp"
18 
19 #include "json_utils.hpp"
20 #include "pel_types.hpp"
21 #include "pel_values.hpp"
22 #include "user_data_formats.hpp"
23 
24 #include <fifo_map.hpp>
25 #include <iomanip>
26 #include <nlohmann/json.hpp>
27 #include <phosphor-logging/log.hpp>
28 #include <sstream>
29 
30 namespace openpower::pels::user_data
31 {
32 namespace pv = openpower::pels::pel_values;
33 using namespace phosphor::logging;
34 
35 // Use fifo_map as nlohmann::json's map. We are just ignoring the 'less'
36 // compare.  With this map the keys are kept in FIFO order.
37 template <class K, class V, class dummy_compare, class A>
38 using fifoMap = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>;
39 using fifoJSON = nlohmann::basic_json<fifoMap>;
40 
41 /**
42  * @brief Returns a JSON string for use by PEL::printSectionInJSON().
43  *
44  * The returning string will contain a JSON object, but without
45  * the outer {}.  If the input JSON isn't a JSON object (dict), then
46  * one will be created with the input added to a 'Data' key.
47  *
48  * @param[in] json - The JSON to convert to a string
49  *
50  * @return std::string - The JSON string
51  */
52 std::string prettyJSON(uint16_t componentID, uint8_t subType, uint8_t version,
53                        const fifoJSON& json)
54 {
55     fifoJSON output;
56     output[pv::sectionVer] = std::to_string(version);
57     output[pv::subSection] = std::to_string(subType);
58     output[pv::createdBy] = getNumberString("0x%04X", componentID);
59 
60     if (!json.is_object())
61     {
62         output["Data"] = json;
63     }
64     else
65     {
66         for (const auto& [key, value] : json.items())
67         {
68             output[key] = value;
69         }
70     }
71 
72     // Let nlohmann do the pretty printing.
73     std::stringstream stream;
74     stream << std::setw(4) << output;
75 
76     auto jsonString = stream.str();
77 
78     // Now it looks like:
79     // {
80     //     "Section Version": ...
81     //     ...
82     // }
83 
84     // Since PEL::printSectionInJSON() will supply the outer { }s,
85     // remove the existing ones.
86 
87     // Replace the { and the following newline, and the } and its
88     // preceeding newline.
89     jsonString.erase(0, 2);
90 
91     auto pos = jsonString.find_last_of('}');
92     jsonString.erase(pos - 1);
93 
94     return jsonString;
95 }
96 
97 /**
98  * @brief Convert to an appropriate JSON string as the data is one of
99  *        the formats that we natively support.
100  *
101  * @param[in] componentID - The comp ID from the UserData section header
102  * @param[in] subType - The subtype from the UserData section header
103  * @param[in] version - The version from the UserData section header
104  * @param[in] data - The data itself
105  *
106  * @return std::optional<std::string> - The JSON string if it could be created,
107  *                                      else std::nullopt.
108  */
109 std::optional<std::string>
110     getBuiltinFormatJSON(uint16_t componentID, uint8_t subType, uint8_t version,
111                          const std::vector<uint8_t>& data)
112 {
113     switch (subType)
114     {
115         case static_cast<uint8_t>(UserDataFormat::json):
116         {
117             std::string jsonString{data.begin(), data.begin() + data.size()};
118 
119             fifoJSON json = nlohmann::json::parse(jsonString);
120 
121             return prettyJSON(componentID, subType, version, json);
122         }
123         default:
124             break;
125     }
126     return std::nullopt;
127 }
128 
129 std::optional<std::string> getJSON(uint16_t componentID, uint8_t subType,
130                                    uint8_t version,
131                                    const std::vector<uint8_t>& data)
132 {
133     try
134     {
135         switch (componentID)
136         {
137             case static_cast<uint16_t>(ComponentID::phosphorLogging):
138                 return getBuiltinFormatJSON(componentID, subType, version,
139                                             data);
140             default:
141                 break;
142         }
143     }
144     catch (std::exception& e)
145     {
146         log<level::ERR>("Failed parsing UserData", entry("ERROR=%s", e.what()));
147     }
148 
149     return std::nullopt;
150 }
151 
152 } // namespace openpower::pels::user_data
153