xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision ea2e6eeca15f4019923466f7c8ccc52c53a5ea94)
177dd8813SKowalski, Kamil /*
277dd8813SKowalski, Kamil // Copyright (c) 2018 Intel Corporation
377dd8813SKowalski, Kamil //
477dd8813SKowalski, Kamil // Licensed under the Apache License, Version 2.0 (the "License");
577dd8813SKowalski, Kamil // you may not use this file except in compliance with the License.
677dd8813SKowalski, Kamil // You may obtain a copy of the License at
777dd8813SKowalski, Kamil //
877dd8813SKowalski, Kamil //      http://www.apache.org/licenses/LICENSE-2.0
977dd8813SKowalski, Kamil //
1077dd8813SKowalski, Kamil // Unless required by applicable law or agreed to in writing, software
1177dd8813SKowalski, Kamil // distributed under the License is distributed on an "AS IS" BASIS,
1277dd8813SKowalski, Kamil // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1377dd8813SKowalski, Kamil // See the License for the specific language governing permissions and
1477dd8813SKowalski, Kamil // limitations under the License.
1577dd8813SKowalski, Kamil */
1677dd8813SKowalski, Kamil #pragma once
179712f8acSEd Tanous 
189712f8acSEd Tanous #include <error_messages.hpp>
1904e438cbSEd Tanous #include <http_request.hpp>
2004e438cbSEd Tanous #include <http_response.hpp>
211abe55efSEd Tanous #include <nlohmann/json.hpp>
220627a2c7SEd Tanous 
23*ea2e6eecSWilly Tu #include <span>
241214b7e7SGunnar Mills 
251abe55efSEd Tanous namespace redfish
261abe55efSEd Tanous {
271abe55efSEd Tanous 
281abe55efSEd Tanous namespace json_util
291abe55efSEd Tanous {
3077dd8813SKowalski, Kamil 
3177dd8813SKowalski, Kamil /**
3277dd8813SKowalski, Kamil  * @brief Processes request to extract JSON from its body. If it fails, adds
3377dd8813SKowalski, Kamil  *       MalformedJSON message to response and ends it.
3477dd8813SKowalski, Kamil  *
3577dd8813SKowalski, Kamil  * @param[io]  res       Response object
3677dd8813SKowalski, Kamil  * @param[in]  req       Request object
3777dd8813SKowalski, Kamil  * @param[out] reqJson   JSON object extracted from request's body
3877dd8813SKowalski, Kamil  *
3977dd8813SKowalski, Kamil  * @return true if JSON is valid, false when JSON is invalid and response has
4077dd8813SKowalski, Kamil  *         been filled with message and ended.
4177dd8813SKowalski, Kamil  */
4255c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req,
4377dd8813SKowalski, Kamil                             nlohmann::json& reqJson);
449712f8acSEd Tanous namespace details
459712f8acSEd Tanous {
46771cfa0fSJason M. Bills 
471214b7e7SGunnar Mills template <typename Type>
482c70f800SEd Tanous struct IsOptional : std::false_type
491214b7e7SGunnar Mills {};
509712f8acSEd Tanous 
51771cfa0fSJason M. Bills template <typename Type>
522c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type
531214b7e7SGunnar Mills {};
549712f8acSEd Tanous 
55771cfa0fSJason M. Bills template <typename Type>
562c70f800SEd Tanous struct IsVector : std::false_type
571214b7e7SGunnar Mills {};
58b1556427SEd Tanous 
591214b7e7SGunnar Mills template <typename Type>
602c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type
611214b7e7SGunnar Mills {};
62b1556427SEd Tanous 
631214b7e7SGunnar Mills template <typename Type>
642c70f800SEd Tanous struct IsStdArray : std::false_type
651214b7e7SGunnar Mills {};
66318226c2SJames Feist 
67318226c2SJames Feist template <typename Type, std::size_t size>
682c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type
691214b7e7SGunnar Mills {};
70318226c2SJames Feist 
71471a5eb8SAppaRao Puli enum class UnpackErrorCode
72471a5eb8SAppaRao Puli {
73471a5eb8SAppaRao Puli     success,
74471a5eb8SAppaRao Puli     invalidType,
75471a5eb8SAppaRao Puli     outOfRange
76471a5eb8SAppaRao Puli };
77471a5eb8SAppaRao Puli 
78a6acbb31SJames Feist template <typename ToType, typename FromType>
79*ea2e6eecSWilly Tu bool checkRange(const FromType& from, std::string_view key)
80a6acbb31SJames Feist {
81ee344e0fSEd Tanous     if (from > std::numeric_limits<ToType>::max())
82a6acbb31SJames Feist     {
83a6acbb31SJames Feist         BMCWEB_LOG_DEBUG << "Value for key " << key
84a6acbb31SJames Feist                          << " was greater than max: " << __PRETTY_FUNCTION__;
85a6acbb31SJames Feist         return false;
86a6acbb31SJames Feist     }
87ee344e0fSEd Tanous     if (from < std::numeric_limits<ToType>::lowest())
88a6acbb31SJames Feist     {
89a6acbb31SJames Feist         BMCWEB_LOG_DEBUG << "Value for key " << key
90a6acbb31SJames Feist                          << " was less than min: " << __PRETTY_FUNCTION__;
91a6acbb31SJames Feist         return false;
92a6acbb31SJames Feist     }
93a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<ToType>)
94a6acbb31SJames Feist     {
95ee344e0fSEd Tanous         if (std::isnan(from))
96a6acbb31SJames Feist         {
97a6acbb31SJames Feist             BMCWEB_LOG_DEBUG << "Value for key " << key << " was NAN";
98a6acbb31SJames Feist             return false;
99a6acbb31SJames Feist         }
100a6acbb31SJames Feist     }
101a6acbb31SJames Feist 
102a6acbb31SJames Feist     return true;
103a6acbb31SJames Feist }
104a6acbb31SJames Feist 
105771cfa0fSJason M. Bills template <typename Type>
106471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
107*ea2e6eecSWilly Tu                                          std::string_view key, Type& value)
108771cfa0fSJason M. Bills {
109471a5eb8SAppaRao Puli     UnpackErrorCode ret = UnpackErrorCode::success;
11041352c24SSantosh Puranik 
111a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<Type>)
112771cfa0fSJason M. Bills     {
113a6acbb31SJames Feist         double helper = 0;
114a6acbb31SJames Feist         double* jsonPtr = jsonValue.get_ptr<double*>();
115771cfa0fSJason M. Bills 
116771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
117771cfa0fSJason M. Bills         {
118a6acbb31SJames Feist             int64_t* intPtr = jsonValue.get_ptr<int64_t*>();
119a6acbb31SJames Feist             if (intPtr != nullptr)
120771cfa0fSJason M. Bills             {
121a6acbb31SJames Feist                 helper = static_cast<double>(*intPtr);
122a6acbb31SJames Feist                 jsonPtr = &helper;
123771cfa0fSJason M. Bills             }
124a6acbb31SJames Feist         }
1255eb2bef2SAppaRao Puli         if (jsonPtr == nullptr)
1265eb2bef2SAppaRao Puli         {
127471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
1285eb2bef2SAppaRao Puli         }
129cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
130771cfa0fSJason M. Bills         {
131471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
132771cfa0fSJason M. Bills         }
133771cfa0fSJason M. Bills         value = static_cast<Type>(*jsonPtr);
134771cfa0fSJason M. Bills     }
135a6acbb31SJames Feist 
136a6acbb31SJames Feist     else if constexpr (std::is_signed_v<Type>)
137a6acbb31SJames Feist     {
138a6acbb31SJames Feist         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
139271584abSEd Tanous         if (jsonPtr == nullptr)
140271584abSEd Tanous         {
141471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
142271584abSEd Tanous         }
143cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
144a6acbb31SJames Feist         {
145471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
146a6acbb31SJames Feist         }
147a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
148a6acbb31SJames Feist     }
149a6acbb31SJames Feist 
1508102ddbaSAppaRao Puli     else if constexpr ((std::is_unsigned_v<Type>)&&(
1518102ddbaSAppaRao Puli                            !std::is_same_v<bool, Type>))
152a6acbb31SJames Feist     {
153a6acbb31SJames Feist         uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
154271584abSEd Tanous         if (jsonPtr == nullptr)
155271584abSEd Tanous         {
156471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
157271584abSEd Tanous         }
158cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
159a6acbb31SJames Feist         {
160471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
161a6acbb31SJames Feist         }
162a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
163a6acbb31SJames Feist     }
164a6acbb31SJames Feist 
1650627a2c7SEd Tanous     else if constexpr (std::is_same_v<nlohmann::json, Type>)
1660627a2c7SEd Tanous     {
1670627a2c7SEd Tanous         // Must be a complex type.  Simple types (int string etc) should be
1680627a2c7SEd Tanous         // unpacked directly
1698ebc91f6SEd Tanous         if (!jsonValue.is_object() && !jsonValue.is_array() &&
1708ebc91f6SEd Tanous             !jsonValue.is_null())
1710627a2c7SEd Tanous         {
172471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
1730627a2c7SEd Tanous         }
1740627a2c7SEd Tanous 
1750627a2c7SEd Tanous         value = std::move(jsonValue);
1760627a2c7SEd Tanous     }
177471a5eb8SAppaRao Puli     else
178471a5eb8SAppaRao Puli     {
179471a5eb8SAppaRao Puli         using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
180471a5eb8SAppaRao Puli         JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
181471a5eb8SAppaRao Puli         if (jsonPtr == nullptr)
182471a5eb8SAppaRao Puli         {
183471a5eb8SAppaRao Puli             BMCWEB_LOG_DEBUG
184471a5eb8SAppaRao Puli                 << "Value for key " << key
185471a5eb8SAppaRao Puli                 << " was incorrect type: " << jsonValue.type_name();
186471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
187471a5eb8SAppaRao Puli         }
188471a5eb8SAppaRao Puli         value = std::move(*jsonPtr);
189471a5eb8SAppaRao Puli     }
190471a5eb8SAppaRao Puli     return ret;
191471a5eb8SAppaRao Puli }
192471a5eb8SAppaRao Puli 
193471a5eb8SAppaRao Puli template <typename Type>
194*ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key,
195471a5eb8SAppaRao Puli                  crow::Response& res, Type& value)
196471a5eb8SAppaRao Puli {
197471a5eb8SAppaRao Puli     bool ret = true;
198471a5eb8SAppaRao Puli 
1992c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
200471a5eb8SAppaRao Puli     {
201471a5eb8SAppaRao Puli         value.emplace();
202471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
203471a5eb8SAppaRao Puli                                                      *value) &&
204471a5eb8SAppaRao Puli               ret;
205471a5eb8SAppaRao Puli     }
2062c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
207318226c2SJames Feist     {
208318226c2SJames Feist         if (!jsonValue.is_array())
209318226c2SJames Feist         {
21071f52d96SEd Tanous             messages::propertyValueTypeError(
21171f52d96SEd Tanous                 res,
21271f52d96SEd Tanous                 res.jsonValue.dump(2, ' ', true,
21371f52d96SEd Tanous                                    nlohmann::json::error_handler_t::replace),
21471f52d96SEd Tanous                 key);
21541352c24SSantosh Puranik             return false;
216318226c2SJames Feist         }
217318226c2SJames Feist         if (jsonValue.size() != value.size())
218318226c2SJames Feist         {
21971f52d96SEd Tanous             messages::propertyValueTypeError(
22071f52d96SEd Tanous                 res,
22171f52d96SEd Tanous                 res.jsonValue.dump(2, ' ', true,
22271f52d96SEd Tanous                                    nlohmann::json::error_handler_t::replace),
22371f52d96SEd Tanous                 key);
22441352c24SSantosh Puranik             return false;
225318226c2SJames Feist         }
226318226c2SJames Feist         size_t index = 0;
227318226c2SJames Feist         for (const auto& val : jsonValue.items())
228318226c2SJames Feist         {
22941352c24SSantosh Puranik             ret = unpackValue<typename Type::value_type>(val.value(), key, res,
23041352c24SSantosh Puranik                                                          value[index++]) &&
23141352c24SSantosh Puranik                   ret;
232318226c2SJames Feist         }
233318226c2SJames Feist     }
2342c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
235b1556427SEd Tanous     {
236b1556427SEd Tanous         if (!jsonValue.is_array())
237b1556427SEd Tanous         {
23871f52d96SEd Tanous             messages::propertyValueTypeError(
23971f52d96SEd Tanous                 res,
24071f52d96SEd Tanous                 res.jsonValue.dump(2, ' ', true,
24171f52d96SEd Tanous                                    nlohmann::json::error_handler_t::replace),
24271f52d96SEd Tanous                 key);
24341352c24SSantosh Puranik             return false;
244b1556427SEd Tanous         }
245b1556427SEd Tanous 
246b1556427SEd Tanous         for (const auto& val : jsonValue.items())
247b1556427SEd Tanous         {
248b1556427SEd Tanous             value.emplace_back();
24941352c24SSantosh Puranik             ret = unpackValue<typename Type::value_type>(val.value(), key, res,
25041352c24SSantosh Puranik                                                          value.back()) &&
25141352c24SSantosh Puranik                   ret;
252b1556427SEd Tanous         }
253b1556427SEd Tanous     }
254771cfa0fSJason M. Bills     else
255771cfa0fSJason M. Bills     {
256471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
257471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
258771cfa0fSJason M. Bills         {
259471a5eb8SAppaRao Puli             if (ec == UnpackErrorCode::invalidType)
260471a5eb8SAppaRao Puli             {
26171f52d96SEd Tanous                 messages::propertyValueTypeError(
26271f52d96SEd Tanous                     res,
26371f52d96SEd Tanous                     jsonValue.dump(2, ' ', true,
26471f52d96SEd Tanous                                    nlohmann::json::error_handler_t::replace),
26571f52d96SEd Tanous                     key);
266471a5eb8SAppaRao Puli             }
267471a5eb8SAppaRao Puli             else if (ec == UnpackErrorCode::outOfRange)
268471a5eb8SAppaRao Puli             {
26971f52d96SEd Tanous                 messages::propertyValueNotInList(
27071f52d96SEd Tanous                     res,
27171f52d96SEd Tanous                     jsonValue.dump(2, ' ', true,
27271f52d96SEd Tanous                                    nlohmann::json::error_handler_t::replace),
27371f52d96SEd Tanous                     key);
274471a5eb8SAppaRao Puli             }
27541352c24SSantosh Puranik             return false;
276771cfa0fSJason M. Bills         }
277771cfa0fSJason M. Bills     }
278471a5eb8SAppaRao Puli 
279471a5eb8SAppaRao Puli     return ret;
280471a5eb8SAppaRao Puli }
281471a5eb8SAppaRao Puli 
282471a5eb8SAppaRao Puli template <typename Type>
283*ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value)
284471a5eb8SAppaRao Puli {
285471a5eb8SAppaRao Puli     bool ret = true;
2862c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
287471a5eb8SAppaRao Puli     {
288471a5eb8SAppaRao Puli         value.emplace();
289471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) &&
290471a5eb8SAppaRao Puli               ret;
291471a5eb8SAppaRao Puli     }
2922c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
293471a5eb8SAppaRao Puli     {
294471a5eb8SAppaRao Puli         if (!jsonValue.is_array())
295471a5eb8SAppaRao Puli         {
296471a5eb8SAppaRao Puli             return false;
297471a5eb8SAppaRao Puli         }
298471a5eb8SAppaRao Puli         if (jsonValue.size() != value.size())
299471a5eb8SAppaRao Puli         {
300471a5eb8SAppaRao Puli             return false;
301471a5eb8SAppaRao Puli         }
302471a5eb8SAppaRao Puli         size_t index = 0;
303471a5eb8SAppaRao Puli         for (const auto& val : jsonValue.items())
304471a5eb8SAppaRao Puli         {
305471a5eb8SAppaRao Puli             ret = unpackValue<typename Type::value_type>(val.value(), key,
306471a5eb8SAppaRao Puli                                                          value[index++]) &&
307471a5eb8SAppaRao Puli                   ret;
308471a5eb8SAppaRao Puli         }
309471a5eb8SAppaRao Puli     }
3102c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
311471a5eb8SAppaRao Puli     {
312471a5eb8SAppaRao Puli         if (!jsonValue.is_array())
313471a5eb8SAppaRao Puli         {
314471a5eb8SAppaRao Puli             return false;
315471a5eb8SAppaRao Puli         }
316471a5eb8SAppaRao Puli 
317471a5eb8SAppaRao Puli         for (const auto& val : jsonValue.items())
318471a5eb8SAppaRao Puli         {
319471a5eb8SAppaRao Puli             value.emplace_back();
320471a5eb8SAppaRao Puli             ret = unpackValue<typename Type::value_type>(val.value(), key,
321471a5eb8SAppaRao Puli                                                          value.back()) &&
322471a5eb8SAppaRao Puli                   ret;
323471a5eb8SAppaRao Puli         }
324471a5eb8SAppaRao Puli     }
325471a5eb8SAppaRao Puli     else
326471a5eb8SAppaRao Puli     {
327471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
328471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
329471a5eb8SAppaRao Puli         {
330471a5eb8SAppaRao Puli             return false;
331471a5eb8SAppaRao Puli         }
332471a5eb8SAppaRao Puli     }
333471a5eb8SAppaRao Puli 
33441352c24SSantosh Puranik     return ret;
335771cfa0fSJason M. Bills }
3369712f8acSEd Tanous } // namespace details
3379712f8acSEd Tanous 
338*ea2e6eecSWilly Tu // clang-format off
339*ea2e6eecSWilly Tu using UnpackVariant = std::variant<
340*ea2e6eecSWilly Tu     uint8_t*,
341*ea2e6eecSWilly Tu     uint16_t*,
342*ea2e6eecSWilly Tu     int16_t*,
343*ea2e6eecSWilly Tu     uint32_t*,
344*ea2e6eecSWilly Tu     int32_t*,
345*ea2e6eecSWilly Tu     uint64_t*,
346*ea2e6eecSWilly Tu     int64_t*,
347*ea2e6eecSWilly Tu     bool*,
348*ea2e6eecSWilly Tu     double*,
349*ea2e6eecSWilly Tu     std::string*,
350*ea2e6eecSWilly Tu     nlohmann::json*,
351*ea2e6eecSWilly Tu     std::vector<uint8_t>*,
352*ea2e6eecSWilly Tu     std::vector<uint16_t>*,
353*ea2e6eecSWilly Tu     std::vector<int16_t>*,
354*ea2e6eecSWilly Tu     std::vector<uint32_t>*,
355*ea2e6eecSWilly Tu     std::vector<int32_t>*,
356*ea2e6eecSWilly Tu     std::vector<uint64_t>*,
357*ea2e6eecSWilly Tu     std::vector<int64_t>*,
358*ea2e6eecSWilly Tu     //std::vector<bool>*,
359*ea2e6eecSWilly Tu     std::vector<double>*,
360*ea2e6eecSWilly Tu     std::vector<std::string>*,
361*ea2e6eecSWilly Tu     std::vector<nlohmann::json>*,
362*ea2e6eecSWilly Tu     std::optional<uint8_t>*,
363*ea2e6eecSWilly Tu     std::optional<uint16_t>*,
364*ea2e6eecSWilly Tu     std::optional<int16_t>*,
365*ea2e6eecSWilly Tu     std::optional<uint32_t>*,
366*ea2e6eecSWilly Tu     std::optional<int32_t>*,
367*ea2e6eecSWilly Tu     std::optional<uint64_t>*,
368*ea2e6eecSWilly Tu     std::optional<int64_t>*,
369*ea2e6eecSWilly Tu     std::optional<bool>*,
370*ea2e6eecSWilly Tu     std::optional<double>*,
371*ea2e6eecSWilly Tu     std::optional<std::string>*,
372*ea2e6eecSWilly Tu     std::optional<nlohmann::json>*,
373*ea2e6eecSWilly Tu     std::optional<std::vector<uint8_t>>*,
374*ea2e6eecSWilly Tu     std::optional<std::vector<uint16_t>>*,
375*ea2e6eecSWilly Tu     std::optional<std::vector<int16_t>>*,
376*ea2e6eecSWilly Tu     std::optional<std::vector<uint32_t>>*,
377*ea2e6eecSWilly Tu     std::optional<std::vector<int32_t>>*,
378*ea2e6eecSWilly Tu     std::optional<std::vector<uint64_t>>*,
379*ea2e6eecSWilly Tu     std::optional<std::vector<int64_t>>*,
380*ea2e6eecSWilly Tu     //std::optional<std::vector<bool>>*,
381*ea2e6eecSWilly Tu     std::optional<std::vector<double>>*,
382*ea2e6eecSWilly Tu     std::optional<std::vector<std::string>>*,
383*ea2e6eecSWilly Tu     std::optional<std::vector<nlohmann::json>>*
384*ea2e6eecSWilly Tu >;
385*ea2e6eecSWilly Tu // clang-format on
386*ea2e6eecSWilly Tu 
387*ea2e6eecSWilly Tu struct PerUnpack
388*ea2e6eecSWilly Tu {
389*ea2e6eecSWilly Tu     std::string_view key;
390*ea2e6eecSWilly Tu     UnpackVariant value;
391*ea2e6eecSWilly Tu     bool complete = false;
392*ea2e6eecSWilly Tu };
393*ea2e6eecSWilly Tu 
394*ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
395*ea2e6eecSWilly Tu                            std::span<PerUnpack> toUnpack)
3969712f8acSEd Tanous {
39741352c24SSantosh Puranik     bool result = true;
3989712f8acSEd Tanous     if (!jsonRequest.is_object())
3999712f8acSEd Tanous     {
4009712f8acSEd Tanous         BMCWEB_LOG_DEBUG << "Json value is not an object";
401f12894f8SJason M. Bills         messages::unrecognizedRequestBody(res);
4029712f8acSEd Tanous         return false;
4039712f8acSEd Tanous     }
404*ea2e6eecSWilly Tu     for (auto& item : jsonRequest.items())
4059712f8acSEd Tanous     {
406*ea2e6eecSWilly Tu         size_t unpackIndex = 0;
407*ea2e6eecSWilly Tu         for (; unpackIndex < toUnpack.size(); unpackIndex++)
408*ea2e6eecSWilly Tu         {
409*ea2e6eecSWilly Tu             PerUnpack& unpackSpec = toUnpack[unpackIndex];
410*ea2e6eecSWilly Tu             std::string_view key = unpackSpec.key;
411*ea2e6eecSWilly Tu             size_t keysplitIndex = key.find('/');
412*ea2e6eecSWilly Tu             std::string_view leftover;
413*ea2e6eecSWilly Tu             if (keysplitIndex != std::string_view::npos)
414*ea2e6eecSWilly Tu             {
415*ea2e6eecSWilly Tu                 leftover = key.substr(keysplitIndex + 1);
416*ea2e6eecSWilly Tu                 key = key.substr(0, keysplitIndex);
417*ea2e6eecSWilly Tu             }
418*ea2e6eecSWilly Tu 
419*ea2e6eecSWilly Tu             if (key != item.key() || unpackSpec.complete)
420*ea2e6eecSWilly Tu             {
421*ea2e6eecSWilly Tu                 continue;
422*ea2e6eecSWilly Tu             }
423*ea2e6eecSWilly Tu 
424*ea2e6eecSWilly Tu             // Sublevel key
425*ea2e6eecSWilly Tu             if (!leftover.empty())
426*ea2e6eecSWilly Tu             {
427*ea2e6eecSWilly Tu                 // Include the slash in the key so we can compare later
428*ea2e6eecSWilly Tu                 key = unpackSpec.key.substr(0, keysplitIndex + 1);
429*ea2e6eecSWilly Tu                 nlohmann::json j;
430*ea2e6eecSWilly Tu                 result = details::unpackValue<nlohmann::json>(item.value(), key,
431*ea2e6eecSWilly Tu                                                               res, j) &&
43241352c24SSantosh Puranik                          result;
433*ea2e6eecSWilly Tu                 if (result == false)
434*ea2e6eecSWilly Tu                 {
435*ea2e6eecSWilly Tu                     return result;
4369712f8acSEd Tanous                 }
4379712f8acSEd Tanous 
438*ea2e6eecSWilly Tu                 std::vector<PerUnpack> nextLevel;
439*ea2e6eecSWilly Tu                 for (PerUnpack& p : toUnpack)
440*ea2e6eecSWilly Tu                 {
441*ea2e6eecSWilly Tu                     if (!p.key.starts_with(key))
442*ea2e6eecSWilly Tu                     {
443*ea2e6eecSWilly Tu                         continue;
444*ea2e6eecSWilly Tu                     }
445*ea2e6eecSWilly Tu                     std::string_view thisLeftover = p.key.substr(key.size());
446*ea2e6eecSWilly Tu                     nextLevel.push_back({thisLeftover, p.value, false});
447*ea2e6eecSWilly Tu                     p.complete = true;
4489712f8acSEd Tanous                 }
44977dd8813SKowalski, Kamil 
450*ea2e6eecSWilly Tu                 result = readJsonHelper(j, res, nextLevel) && result;
451*ea2e6eecSWilly Tu                 break;
452*ea2e6eecSWilly Tu             }
453*ea2e6eecSWilly Tu 
454*ea2e6eecSWilly Tu             result =
455*ea2e6eecSWilly Tu                 std::visit(
456*ea2e6eecSWilly Tu                     [&item, &unpackSpec, &res](auto&& val) {
457*ea2e6eecSWilly Tu                         using ContainedT =
458*ea2e6eecSWilly Tu                             std::remove_pointer_t<std::decay_t<decltype(val)>>;
459*ea2e6eecSWilly Tu                         return details::unpackValue<ContainedT>(
460*ea2e6eecSWilly Tu                             item.value(), unpackSpec.key, res, *val);
461*ea2e6eecSWilly Tu                     },
462*ea2e6eecSWilly Tu                     unpackSpec.value) &&
463*ea2e6eecSWilly Tu                 result;
464*ea2e6eecSWilly Tu 
465*ea2e6eecSWilly Tu             unpackSpec.complete = true;
466*ea2e6eecSWilly Tu             break;
467*ea2e6eecSWilly Tu         }
468*ea2e6eecSWilly Tu 
469*ea2e6eecSWilly Tu         if (unpackIndex == toUnpack.size())
470*ea2e6eecSWilly Tu         {
471*ea2e6eecSWilly Tu             messages::propertyUnknown(res, item.key());
472*ea2e6eecSWilly Tu             result = false;
473*ea2e6eecSWilly Tu         }
474*ea2e6eecSWilly Tu     }
475*ea2e6eecSWilly Tu 
476*ea2e6eecSWilly Tu     for (PerUnpack& perUnpack : toUnpack)
477*ea2e6eecSWilly Tu     {
478*ea2e6eecSWilly Tu         if (perUnpack.complete == false)
479*ea2e6eecSWilly Tu         {
480*ea2e6eecSWilly Tu             bool isOptional = std::visit(
481*ea2e6eecSWilly Tu                 [](auto&& val) {
482*ea2e6eecSWilly Tu                     using ContainedType =
483*ea2e6eecSWilly Tu                         std::remove_pointer_t<std::decay_t<decltype(val)>>;
484*ea2e6eecSWilly Tu                     return details::IsOptional<ContainedType>::value;
485*ea2e6eecSWilly Tu                 },
486*ea2e6eecSWilly Tu                 perUnpack.value);
487*ea2e6eecSWilly Tu             if (isOptional)
488*ea2e6eecSWilly Tu             {
489*ea2e6eecSWilly Tu                 continue;
490*ea2e6eecSWilly Tu             }
491*ea2e6eecSWilly Tu             messages::propertyMissing(res, perUnpack.key);
492*ea2e6eecSWilly Tu             result = false;
493*ea2e6eecSWilly Tu         }
494*ea2e6eecSWilly Tu     }
495*ea2e6eecSWilly Tu     return result;
496*ea2e6eecSWilly Tu }
497*ea2e6eecSWilly Tu 
498*ea2e6eecSWilly Tu inline void packVariant(std::span<PerUnpack> /*toPack*/)
499*ea2e6eecSWilly Tu {}
500*ea2e6eecSWilly Tu 
501*ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
502*ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key,
503*ea2e6eecSWilly Tu                  FirstType& first, UnpackTypes&&... in)
504*ea2e6eecSWilly Tu {
505*ea2e6eecSWilly Tu     if (toPack.empty())
506*ea2e6eecSWilly Tu     {
507*ea2e6eecSWilly Tu         return;
508*ea2e6eecSWilly Tu     }
509*ea2e6eecSWilly Tu     toPack[0].key = key;
510*ea2e6eecSWilly Tu     toPack[0].value = &first;
511*ea2e6eecSWilly Tu     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
512*ea2e6eecSWilly Tu     packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...);
513*ea2e6eecSWilly Tu }
514*ea2e6eecSWilly Tu 
515*ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
516*ea2e6eecSWilly Tu bool readJson(nlohmann::json& jsonRequest, crow::Response& res,
517*ea2e6eecSWilly Tu               std::string_view key, FirstType&& first, UnpackTypes&&... in)
518*ea2e6eecSWilly Tu {
519*ea2e6eecSWilly Tu     const std::size_t n = sizeof...(UnpackTypes) + 2;
520*ea2e6eecSWilly Tu     std::array<PerUnpack, n / 2> toUnpack2;
521*ea2e6eecSWilly Tu     packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...);
522*ea2e6eecSWilly Tu     return readJsonHelper(jsonRequest, res, toUnpack2);
523*ea2e6eecSWilly Tu }
524*ea2e6eecSWilly Tu 
525*ea2e6eecSWilly Tu inline std::optional<nlohmann::json>
526*ea2e6eecSWilly Tu     readJsonPatchHelper(const crow::Request& req, crow::Response& res)
5270627a2c7SEd Tanous {
5280627a2c7SEd Tanous     nlohmann::json jsonRequest;
5290627a2c7SEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
5300627a2c7SEd Tanous     {
5310627a2c7SEd Tanous         BMCWEB_LOG_DEBUG << "Json value not readable";
532*ea2e6eecSWilly Tu         return std::nullopt;
5330627a2c7SEd Tanous     }
53415ed6780SWilly Tu 
53515ed6780SWilly Tu     if (jsonRequest.empty())
53615ed6780SWilly Tu     {
53715ed6780SWilly Tu         BMCWEB_LOG_DEBUG << "Json value is empty";
53815ed6780SWilly Tu         messages::emptyJSON(res);
539*ea2e6eecSWilly Tu         return std::nullopt;
540*ea2e6eecSWilly Tu     }
541*ea2e6eecSWilly Tu     return {std::move(jsonRequest)};
542*ea2e6eecSWilly Tu }
543*ea2e6eecSWilly Tu 
544*ea2e6eecSWilly Tu template <typename... UnpackTypes>
545*ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res,
546*ea2e6eecSWilly Tu                    std::string_view key, UnpackTypes&&... in)
547*ea2e6eecSWilly Tu {
548*ea2e6eecSWilly Tu     std::optional<nlohmann::json> jsonRequest = readJsonPatchHelper(req, res);
549*ea2e6eecSWilly Tu     if (jsonRequest == std::nullopt)
550*ea2e6eecSWilly Tu     {
55115ed6780SWilly Tu         return false;
55215ed6780SWilly Tu     }
55315ed6780SWilly Tu 
554*ea2e6eecSWilly Tu     return readJson(*jsonRequest, res, key, std::forward<UnpackTypes&&>(in)...);
55515ed6780SWilly Tu }
55615ed6780SWilly Tu 
55715ed6780SWilly Tu template <typename... UnpackTypes>
55815ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res,
559*ea2e6eecSWilly Tu                     const char* key, UnpackTypes&&... in)
56015ed6780SWilly Tu {
56115ed6780SWilly Tu     nlohmann::json jsonRequest;
56215ed6780SWilly Tu     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
56315ed6780SWilly Tu     {
56415ed6780SWilly Tu         BMCWEB_LOG_DEBUG << "Json value not readable";
56515ed6780SWilly Tu         return false;
56615ed6780SWilly Tu     }
567*ea2e6eecSWilly Tu     return readJson(jsonRequest, res, key, std::forward<UnpackTypes&&>(in)...);
5680627a2c7SEd Tanous }
5690627a2c7SEd Tanous 
57077dd8813SKowalski, Kamil } // namespace json_util
57177dd8813SKowalski, Kamil } // namespace redfish
572