xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision 08fad5d9dc59323a8916ff97a035221621047d8c)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
340e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
477dd8813SKowalski, Kamil #pragma once
59712f8acSEd Tanous 
6d5c80ad9SNan Zhou #include "error_messages.hpp"
7d5c80ad9SNan Zhou #include "http_request.hpp"
8d5c80ad9SNan Zhou #include "http_response.hpp"
9185444b1SNan Zhou #include "human_sort.hpp"
10d5c80ad9SNan Zhou #include "logging.hpp"
11faf100f9SEd Tanous 
12e7bcf475SJayanth Othayoth #include <boost/system/result.hpp>
13e7bcf475SJayanth Othayoth #include <boost/url/parse.hpp>
14e7bcf475SJayanth Othayoth #include <boost/url/url_view.hpp>
15faf100f9SEd Tanous #include <nlohmann/json.hpp>
160627a2c7SEd Tanous 
17185444b1SNan Zhou #include <algorithm>
18d5c80ad9SNan Zhou #include <array>
19d5c80ad9SNan Zhou #include <cmath>
20d5c80ad9SNan Zhou #include <cstddef>
21d5c80ad9SNan Zhou #include <cstdint>
22d5c80ad9SNan Zhou #include <limits>
23d5c80ad9SNan Zhou #include <map>
24d5c80ad9SNan Zhou #include <optional>
253544d2a7SEd Tanous #include <ranges>
26ea2e6eecSWilly Tu #include <span>
27d5c80ad9SNan Zhou #include <string>
28d5c80ad9SNan Zhou #include <string_view>
29d5c80ad9SNan Zhou #include <type_traits>
30d5c80ad9SNan Zhou #include <utility>
31d5c80ad9SNan Zhou #include <variant>
32d5c80ad9SNan Zhou #include <vector>
33d5c80ad9SNan Zhou 
341e75e1ddSNan Zhou // IWYU pragma: no_forward_declare crow::Request
351214b7e7SGunnar Mills 
361abe55efSEd Tanous namespace redfish
371abe55efSEd Tanous {
381abe55efSEd Tanous 
391abe55efSEd Tanous namespace json_util
401abe55efSEd Tanous {
4177dd8813SKowalski, Kamil 
4277dd8813SKowalski, Kamil /**
4377dd8813SKowalski, Kamil  * @brief Processes request to extract JSON from its body. If it fails, adds
4477dd8813SKowalski, Kamil  *       MalformedJSON message to response and ends it.
4577dd8813SKowalski, Kamil  *
4677dd8813SKowalski, Kamil  * @param[io]  res       Response object
4777dd8813SKowalski, Kamil  * @param[in]  req       Request object
4877dd8813SKowalski, Kamil  * @param[out] reqJson   JSON object extracted from request's body
4977dd8813SKowalski, Kamil  *
5077dd8813SKowalski, Kamil  * @return true if JSON is valid, false when JSON is invalid and response has
5177dd8813SKowalski, Kamil  *         been filled with message and ended.
5277dd8813SKowalski, Kamil  */
5355c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req,
5477dd8813SKowalski, Kamil                             nlohmann::json& reqJson);
559712f8acSEd Tanous namespace details
569712f8acSEd Tanous {
57771cfa0fSJason M. Bills 
581214b7e7SGunnar Mills template <typename Type>
592c70f800SEd Tanous struct IsOptional : std::false_type
601214b7e7SGunnar Mills {};
619712f8acSEd Tanous 
62771cfa0fSJason M. Bills template <typename Type>
632c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type
641214b7e7SGunnar Mills {};
659712f8acSEd Tanous 
66771cfa0fSJason M. Bills template <typename Type>
672c70f800SEd Tanous struct IsVector : std::false_type
681214b7e7SGunnar Mills {};
69b1556427SEd Tanous 
701214b7e7SGunnar Mills template <typename Type>
712c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type
721214b7e7SGunnar Mills {};
73b1556427SEd Tanous 
741214b7e7SGunnar Mills template <typename Type>
752c70f800SEd Tanous struct IsStdArray : std::false_type
761214b7e7SGunnar Mills {};
77318226c2SJames Feist 
78318226c2SJames Feist template <typename Type, std::size_t size>
792c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type
801214b7e7SGunnar Mills {};
81318226c2SJames Feist 
828099c517SEd Tanous template <typename Type>
838099c517SEd Tanous struct IsVariant : std::false_type
848099c517SEd Tanous {};
858099c517SEd Tanous 
868099c517SEd Tanous template <typename... Types>
878099c517SEd Tanous struct IsVariant<std::variant<Types...>> : std::true_type
888099c517SEd Tanous {};
898099c517SEd Tanous 
90471a5eb8SAppaRao Puli enum class UnpackErrorCode
91471a5eb8SAppaRao Puli {
92471a5eb8SAppaRao Puli     success,
93471a5eb8SAppaRao Puli     invalidType,
94471a5eb8SAppaRao Puli     outOfRange
95471a5eb8SAppaRao Puli };
96471a5eb8SAppaRao Puli 
97a6acbb31SJames Feist template <typename ToType, typename FromType>
988d9cf72dSEd Tanous bool checkRange(const FromType& from, std::string_view key)
99a6acbb31SJames Feist {
100a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<ToType>)
101a6acbb31SJames Feist     {
102ee344e0fSEd Tanous         if (std::isnan(from))
103a6acbb31SJames Feist         {
10462598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was NAN", key);
105a6acbb31SJames Feist             return false;
106a6acbb31SJames Feist         }
1078d9cf72dSEd Tanous         // Assume for the moment that all floats can represent the full range
1088d9cf72dSEd Tanous         // of any int/uint in a cast.  This is close enough to true for the
1098d9cf72dSEd Tanous         // precision of this json parser.
110a6acbb31SJames Feist     }
1118d9cf72dSEd Tanous     else
112c09966bdSEd Tanous     {
1138d9cf72dSEd Tanous         if (std::cmp_greater(from, std::numeric_limits<ToType>::max()))
114c09966bdSEd Tanous         {
115c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key,
116c09966bdSEd Tanous                              std::numeric_limits<FromType>::max());
117c09966bdSEd Tanous             return false;
118c09966bdSEd Tanous         }
1198d9cf72dSEd Tanous         if (std::cmp_less(from, std::numeric_limits<ToType>::lowest()))
120c09966bdSEd Tanous         {
121c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key,
122c09966bdSEd Tanous                              std::numeric_limits<FromType>::lowest());
123c09966bdSEd Tanous             return false;
124c09966bdSEd Tanous         }
125c09966bdSEd Tanous     }
126a6acbb31SJames Feist 
127a6acbb31SJames Feist     return true;
128a6acbb31SJames Feist }
129a6acbb31SJames Feist 
130771cfa0fSJason M. Bills template <typename Type>
131471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
1328099c517SEd Tanous                                          std::string_view key, Type& value);
1338099c517SEd Tanous 
1348099c517SEd Tanous template <std::size_t Index = 0, typename... Args>
1358099c517SEd Tanous UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key,
1368099c517SEd Tanous                                    std::variant<Args...>& v)
1378099c517SEd Tanous {
1388099c517SEd Tanous     if constexpr (Index < std::variant_size_v<std::variant<Args...>>)
1398099c517SEd Tanous     {
140ed4de7a8SEd Tanous         std::variant_alternative_t<Index, std::variant<Args...>> type{};
1418099c517SEd Tanous         UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type);
1428099c517SEd Tanous         if (unpack == UnpackErrorCode::success)
1438099c517SEd Tanous         {
1448099c517SEd Tanous             v = std::move(type);
1458099c517SEd Tanous             return unpack;
1468099c517SEd Tanous         }
1478099c517SEd Tanous 
1488099c517SEd Tanous         return unpackValueVariant<Index + 1, Args...>(j, key, v);
1498099c517SEd Tanous     }
1508099c517SEd Tanous     return UnpackErrorCode::invalidType;
1518099c517SEd Tanous }
1528099c517SEd Tanous 
1538099c517SEd Tanous template <typename Type>
1548099c517SEd Tanous UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
155ea2e6eecSWilly Tu                                          std::string_view key, Type& value)
156771cfa0fSJason M. Bills {
157471a5eb8SAppaRao Puli     UnpackErrorCode ret = UnpackErrorCode::success;
15841352c24SSantosh Puranik 
159a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<Type>)
160771cfa0fSJason M. Bills     {
161a6acbb31SJames Feist         double helper = 0;
162a6acbb31SJames Feist         double* jsonPtr = jsonValue.get_ptr<double*>();
163771cfa0fSJason M. Bills 
164771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
165771cfa0fSJason M. Bills         {
166a6acbb31SJames Feist             int64_t* intPtr = jsonValue.get_ptr<int64_t*>();
167a6acbb31SJames Feist             if (intPtr != nullptr)
168771cfa0fSJason M. Bills             {
169a6acbb31SJames Feist                 helper = static_cast<double>(*intPtr);
170a6acbb31SJames Feist                 jsonPtr = &helper;
171771cfa0fSJason M. Bills             }
172a6acbb31SJames Feist         }
1735eb2bef2SAppaRao Puli         if (jsonPtr == nullptr)
1745eb2bef2SAppaRao Puli         {
175471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
1765eb2bef2SAppaRao Puli         }
177cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
178771cfa0fSJason M. Bills         {
179471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
180771cfa0fSJason M. Bills         }
181771cfa0fSJason M. Bills         value = static_cast<Type>(*jsonPtr);
182771cfa0fSJason M. Bills     }
183a6acbb31SJames Feist 
184a6acbb31SJames Feist     else if constexpr (std::is_signed_v<Type>)
185a6acbb31SJames Feist     {
186a6acbb31SJames Feist         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
187271584abSEd Tanous         if (jsonPtr == nullptr)
188271584abSEd Tanous         {
1898d9cf72dSEd Tanous             // Value wasn't int, check uint
1908d9cf72dSEd Tanous             uint64_t* uJsonPtr = jsonValue.get_ptr<uint64_t*>();
1918d9cf72dSEd Tanous             if (uJsonPtr == nullptr)
1928d9cf72dSEd Tanous             {
193471a5eb8SAppaRao Puli                 return UnpackErrorCode::invalidType;
194271584abSEd Tanous             }
1958d9cf72dSEd Tanous             if (!checkRange<Type>(*uJsonPtr, key))
1968d9cf72dSEd Tanous             {
1978d9cf72dSEd Tanous                 return UnpackErrorCode::outOfRange;
1988d9cf72dSEd Tanous             }
1998d9cf72dSEd Tanous             value = static_cast<Type>(*uJsonPtr);
2008d9cf72dSEd Tanous         }
2018d9cf72dSEd Tanous         else
2028d9cf72dSEd Tanous         {
203cb13a392SEd Tanous             if (!checkRange<Type>(*jsonPtr, key))
204a6acbb31SJames Feist             {
205471a5eb8SAppaRao Puli                 return UnpackErrorCode::outOfRange;
206a6acbb31SJames Feist             }
207a6acbb31SJames Feist             value = static_cast<Type>(*jsonPtr);
208a6acbb31SJames Feist         }
2098d9cf72dSEd Tanous     }
210a6acbb31SJames Feist 
211bd79bce8SPatrick Williams     else if constexpr ((std::is_unsigned_v<Type>) &&
212bd79bce8SPatrick Williams                        (!std::is_same_v<bool, Type>))
213a6acbb31SJames Feist     {
214a6acbb31SJames Feist         uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
215271584abSEd Tanous         if (jsonPtr == nullptr)
216271584abSEd Tanous         {
2178d9cf72dSEd Tanous             int64_t* ijsonPtr = jsonValue.get_ptr<int64_t*>();
2188d9cf72dSEd Tanous             if (ijsonPtr == nullptr)
2198d9cf72dSEd Tanous             {
220471a5eb8SAppaRao Puli                 return UnpackErrorCode::invalidType;
221271584abSEd Tanous             }
2228d9cf72dSEd Tanous             if (!checkRange<Type>(*ijsonPtr, key))
2238d9cf72dSEd Tanous             {
2248d9cf72dSEd Tanous                 return UnpackErrorCode::outOfRange;
2258d9cf72dSEd Tanous             }
2268d9cf72dSEd Tanous             value = static_cast<Type>(*ijsonPtr);
2278d9cf72dSEd Tanous         }
2288d9cf72dSEd Tanous         else
2298d9cf72dSEd Tanous         {
230cb13a392SEd Tanous             if (!checkRange<Type>(*jsonPtr, key))
231a6acbb31SJames Feist             {
232471a5eb8SAppaRao Puli                 return UnpackErrorCode::outOfRange;
233a6acbb31SJames Feist             }
234a6acbb31SJames Feist             value = static_cast<Type>(*jsonPtr);
235a6acbb31SJames Feist         }
2368d9cf72dSEd Tanous     }
237a6acbb31SJames Feist 
2380627a2c7SEd Tanous     else if constexpr (std::is_same_v<nlohmann::json, Type>)
2390627a2c7SEd Tanous     {
2400627a2c7SEd Tanous         value = std::move(jsonValue);
2410627a2c7SEd Tanous     }
2428099c517SEd Tanous     else if constexpr (std::is_same_v<std::nullptr_t, Type>)
2438099c517SEd Tanous     {
2448099c517SEd Tanous         if (!jsonValue.is_null())
2458099c517SEd Tanous         {
2468099c517SEd Tanous             return UnpackErrorCode::invalidType;
2478099c517SEd Tanous         }
2488099c517SEd Tanous     }
249ed4de7a8SEd Tanous     else if constexpr (IsVector<Type>::value)
250ed4de7a8SEd Tanous     {
251ed4de7a8SEd Tanous         nlohmann::json::object_t* obj =
252ed4de7a8SEd Tanous             jsonValue.get_ptr<nlohmann::json::object_t*>();
253ed4de7a8SEd Tanous         if (obj == nullptr)
254ed4de7a8SEd Tanous         {
255ed4de7a8SEd Tanous             return UnpackErrorCode::invalidType;
256ed4de7a8SEd Tanous         }
257ed4de7a8SEd Tanous 
258ed4de7a8SEd Tanous         for (const auto& val : *obj)
259ed4de7a8SEd Tanous         {
260ed4de7a8SEd Tanous             value.emplace_back();
261ed4de7a8SEd Tanous             ret = unpackValueWithErrorCode<typename Type::value_type>(
262ed4de7a8SEd Tanous                       val, key, value.back()) &&
263ed4de7a8SEd Tanous                   ret;
264ed4de7a8SEd Tanous         }
265ed4de7a8SEd Tanous     }
266471a5eb8SAppaRao Puli     else
267471a5eb8SAppaRao Puli     {
268471a5eb8SAppaRao Puli         using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
269471a5eb8SAppaRao Puli         JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
270471a5eb8SAppaRao Puli         if (jsonPtr == nullptr)
271471a5eb8SAppaRao Puli         {
27262598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key,
27362598e31SEd Tanous                              jsonValue.type_name());
274471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
275471a5eb8SAppaRao Puli         }
276471a5eb8SAppaRao Puli         value = std::move(*jsonPtr);
277471a5eb8SAppaRao Puli     }
278471a5eb8SAppaRao Puli     return ret;
279471a5eb8SAppaRao Puli }
280471a5eb8SAppaRao Puli 
281471a5eb8SAppaRao Puli template <typename Type>
282ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key,
283471a5eb8SAppaRao Puli                  crow::Response& res, Type& value)
284471a5eb8SAppaRao Puli {
285471a5eb8SAppaRao Puli     bool ret = true;
286471a5eb8SAppaRao Puli 
2872c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
288471a5eb8SAppaRao Puli     {
289471a5eb8SAppaRao Puli         value.emplace();
290471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
291471a5eb8SAppaRao Puli                                                      *value) &&
292471a5eb8SAppaRao Puli               ret;
293471a5eb8SAppaRao Puli     }
2942c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
295318226c2SJames Feist     {
2960bdda665SEd Tanous         nlohmann::json::array_t* arr =
2970bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
2980bdda665SEd Tanous         if (arr == nullptr)
299318226c2SJames Feist         {
3008b078385Srohitpai             messages::propertyValueTypeError(res, jsonValue, key);
30141352c24SSantosh Puranik             return false;
302318226c2SJames Feist         }
303318226c2SJames Feist         if (jsonValue.size() != value.size())
304318226c2SJames Feist         {
3058b078385Srohitpai             messages::propertyValueTypeError(res, jsonValue, key);
30641352c24SSantosh Puranik             return false;
307318226c2SJames Feist         }
308318226c2SJames Feist         size_t index = 0;
3090bdda665SEd Tanous         for (auto& val : *arr)
310318226c2SJames Feist         {
3110bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
31241352c24SSantosh Puranik                                                          value[index++]) &&
31341352c24SSantosh Puranik                   ret;
314318226c2SJames Feist         }
315318226c2SJames Feist     }
3162c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
317b1556427SEd Tanous     {
3180bdda665SEd Tanous         nlohmann::json::array_t* arr =
3190bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3200bdda665SEd Tanous         if (arr == nullptr)
321b1556427SEd Tanous         {
3228b078385Srohitpai             messages::propertyValueTypeError(res, jsonValue, key);
32341352c24SSantosh Puranik             return false;
324b1556427SEd Tanous         }
325b1556427SEd Tanous 
3260bdda665SEd Tanous         for (auto& val : *arr)
327b1556427SEd Tanous         {
328b1556427SEd Tanous             value.emplace_back();
3290bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
33041352c24SSantosh Puranik                                                          value.back()) &&
33141352c24SSantosh Puranik                   ret;
332b1556427SEd Tanous         }
333b1556427SEd Tanous     }
3348099c517SEd Tanous     else if constexpr (IsVariant<Type>::value)
3358099c517SEd Tanous     {
3368099c517SEd Tanous         UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value);
3378099c517SEd Tanous         if (ec != UnpackErrorCode::success)
3388099c517SEd Tanous         {
3398099c517SEd Tanous             if (ec == UnpackErrorCode::invalidType)
3408099c517SEd Tanous             {
3418099c517SEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
3428099c517SEd Tanous             }
3438099c517SEd Tanous             else if (ec == UnpackErrorCode::outOfRange)
3448099c517SEd Tanous             {
345340d74c8SMyung Bae                 messages::propertyValueOutOfRange(res, jsonValue, key);
3468099c517SEd Tanous             }
3478099c517SEd Tanous             return false;
3488099c517SEd Tanous         }
3498099c517SEd Tanous     }
350771cfa0fSJason M. Bills     else
351771cfa0fSJason M. Bills     {
352471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
353471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
354771cfa0fSJason M. Bills         {
355471a5eb8SAppaRao Puli             if (ec == UnpackErrorCode::invalidType)
356471a5eb8SAppaRao Puli             {
3572e8c4bdaSEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
358471a5eb8SAppaRao Puli             }
359471a5eb8SAppaRao Puli             else if (ec == UnpackErrorCode::outOfRange)
360471a5eb8SAppaRao Puli             {
361340d74c8SMyung Bae                 messages::propertyValueOutOfRange(res, jsonValue, key);
362471a5eb8SAppaRao Puli             }
36341352c24SSantosh Puranik             return false;
364771cfa0fSJason M. Bills         }
365771cfa0fSJason M. Bills     }
366471a5eb8SAppaRao Puli 
367471a5eb8SAppaRao Puli     return ret;
368471a5eb8SAppaRao Puli }
369471a5eb8SAppaRao Puli 
370471a5eb8SAppaRao Puli template <typename Type>
371ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value)
372471a5eb8SAppaRao Puli {
373471a5eb8SAppaRao Puli     bool ret = true;
3742c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
375471a5eb8SAppaRao Puli     {
376471a5eb8SAppaRao Puli         value.emplace();
377471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) &&
378471a5eb8SAppaRao Puli               ret;
379471a5eb8SAppaRao Puli     }
3802c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
381471a5eb8SAppaRao Puli     {
3820bdda665SEd Tanous         nlohmann::json::array_t* arr =
3830bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3840bdda665SEd Tanous         if (arr == nullptr)
385471a5eb8SAppaRao Puli         {
386471a5eb8SAppaRao Puli             return false;
387471a5eb8SAppaRao Puli         }
388471a5eb8SAppaRao Puli         if (jsonValue.size() != value.size())
389471a5eb8SAppaRao Puli         {
390471a5eb8SAppaRao Puli             return false;
391471a5eb8SAppaRao Puli         }
392471a5eb8SAppaRao Puli         size_t index = 0;
3930bdda665SEd Tanous         for (const auto& val : *arr)
394471a5eb8SAppaRao Puli         {
3950bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
396471a5eb8SAppaRao Puli                                                          value[index++]) &&
397471a5eb8SAppaRao Puli                   ret;
398471a5eb8SAppaRao Puli         }
399471a5eb8SAppaRao Puli     }
4002c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
401471a5eb8SAppaRao Puli     {
4020bdda665SEd Tanous         nlohmann::json::array_t* arr =
4030bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
4040bdda665SEd Tanous         if (arr == nullptr)
405471a5eb8SAppaRao Puli         {
406471a5eb8SAppaRao Puli             return false;
407471a5eb8SAppaRao Puli         }
408471a5eb8SAppaRao Puli 
4090bdda665SEd Tanous         for (const auto& val : *arr)
410471a5eb8SAppaRao Puli         {
411471a5eb8SAppaRao Puli             value.emplace_back();
4120bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
413471a5eb8SAppaRao Puli                                                          value.back()) &&
414471a5eb8SAppaRao Puli                   ret;
415471a5eb8SAppaRao Puli         }
416471a5eb8SAppaRao Puli     }
417471a5eb8SAppaRao Puli     else
418471a5eb8SAppaRao Puli     {
419471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
420471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
421471a5eb8SAppaRao Puli         {
422471a5eb8SAppaRao Puli             return false;
423471a5eb8SAppaRao Puli         }
424471a5eb8SAppaRao Puli     }
425471a5eb8SAppaRao Puli 
42641352c24SSantosh Puranik     return ret;
427771cfa0fSJason M. Bills }
428*08fad5d9SCorey Ethington 
429*08fad5d9SCorey Ethington //  boost::hash_combine
430*08fad5d9SCorey Ethington inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
431*08fad5d9SCorey Ethington {
432*08fad5d9SCorey Ethington     seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
433*08fad5d9SCorey Ethington     return seed;
434*08fad5d9SCorey Ethington }
4359712f8acSEd Tanous } // namespace details
4369712f8acSEd Tanous 
437ea2e6eecSWilly Tu // clang-format off
438ea2e6eecSWilly Tu using UnpackVariant = std::variant<
439ea2e6eecSWilly Tu     uint8_t*,
440ea2e6eecSWilly Tu     uint16_t*,
441ea2e6eecSWilly Tu     int16_t*,
442ea2e6eecSWilly Tu     uint32_t*,
443ea2e6eecSWilly Tu     int32_t*,
444ea2e6eecSWilly Tu     uint64_t*,
445ea2e6eecSWilly Tu     int64_t*,
446ea2e6eecSWilly Tu     bool*,
447ea2e6eecSWilly Tu     double*,
448ea2e6eecSWilly Tu     std::string*,
449b6164cbeSEd Tanous     nlohmann::json::object_t*,
4508099c517SEd Tanous     std::variant<std::string, std::nullptr_t>*,
4518099c517SEd Tanous     std::variant<uint8_t, std::nullptr_t>*,
4528099c517SEd Tanous     std::variant<int16_t, std::nullptr_t>*,
4538099c517SEd Tanous     std::variant<uint16_t, std::nullptr_t>*,
4548099c517SEd Tanous     std::variant<int32_t, std::nullptr_t>*,
4558099c517SEd Tanous     std::variant<uint32_t, std::nullptr_t>*,
4568099c517SEd Tanous     std::variant<int64_t, std::nullptr_t>*,
4578099c517SEd Tanous     std::variant<uint64_t, std::nullptr_t>*,
4588099c517SEd Tanous     std::variant<double, std::nullptr_t>*,
4598099c517SEd Tanous     std::variant<bool, std::nullptr_t>*,
460ea2e6eecSWilly Tu     std::vector<uint8_t>*,
461ea2e6eecSWilly Tu     std::vector<uint16_t>*,
462ea2e6eecSWilly Tu     std::vector<int16_t>*,
463ea2e6eecSWilly Tu     std::vector<uint32_t>*,
464ea2e6eecSWilly Tu     std::vector<int32_t>*,
465ea2e6eecSWilly Tu     std::vector<uint64_t>*,
466ea2e6eecSWilly Tu     std::vector<int64_t>*,
467ea2e6eecSWilly Tu     //std::vector<bool>*,
468ea2e6eecSWilly Tu     std::vector<double>*,
469ea2e6eecSWilly Tu     std::vector<std::string>*,
470b6164cbeSEd Tanous     std::vector<nlohmann::json::object_t>*,
471ea2e6eecSWilly Tu     std::optional<uint8_t>*,
472ea2e6eecSWilly Tu     std::optional<uint16_t>*,
473ea2e6eecSWilly Tu     std::optional<int16_t>*,
474ea2e6eecSWilly Tu     std::optional<uint32_t>*,
475ea2e6eecSWilly Tu     std::optional<int32_t>*,
476ea2e6eecSWilly Tu     std::optional<uint64_t>*,
477ea2e6eecSWilly Tu     std::optional<int64_t>*,
478ea2e6eecSWilly Tu     std::optional<bool>*,
479ea2e6eecSWilly Tu     std::optional<double>*,
480ea2e6eecSWilly Tu     std::optional<std::string>*,
481b6164cbeSEd Tanous     std::optional<nlohmann::json::object_t>*,
482ea2e6eecSWilly Tu     std::optional<std::vector<uint8_t>>*,
483ea2e6eecSWilly Tu     std::optional<std::vector<uint16_t>>*,
484ea2e6eecSWilly Tu     std::optional<std::vector<int16_t>>*,
485ea2e6eecSWilly Tu     std::optional<std::vector<uint32_t>>*,
486ea2e6eecSWilly Tu     std::optional<std::vector<int32_t>>*,
487ea2e6eecSWilly Tu     std::optional<std::vector<uint64_t>>*,
488ea2e6eecSWilly Tu     std::optional<std::vector<int64_t>>*,
489ea2e6eecSWilly Tu     //std::optional<std::vector<bool>>*,
490ea2e6eecSWilly Tu     std::optional<std::vector<double>>*,
491ea2e6eecSWilly Tu     std::optional<std::vector<std::string>>*,
4928099c517SEd Tanous     std::optional<std::vector<nlohmann::json::object_t>>*,
4938099c517SEd Tanous     std::optional<std::variant<std::string, std::nullptr_t>>*,
4948099c517SEd Tanous     std::optional<std::variant<uint8_t, std::nullptr_t>>*,
4958099c517SEd Tanous     std::optional<std::variant<int16_t, std::nullptr_t>>*,
4968099c517SEd Tanous     std::optional<std::variant<uint16_t, std::nullptr_t>>*,
4978099c517SEd Tanous     std::optional<std::variant<int32_t, std::nullptr_t>>*,
4988099c517SEd Tanous     std::optional<std::variant<uint32_t, std::nullptr_t>>*,
4998099c517SEd Tanous     std::optional<std::variant<int64_t, std::nullptr_t>>*,
5008099c517SEd Tanous     std::optional<std::variant<uint64_t, std::nullptr_t>>*,
5018099c517SEd Tanous     std::optional<std::variant<double, std::nullptr_t>>*,
5028099c517SEd Tanous     std::optional<std::variant<bool, std::nullptr_t>>*,
5038099c517SEd Tanous     std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*,
504ed4de7a8SEd Tanous     std::optional<std::vector<std::variant<std::string, nlohmann::json::object_t, std::nullptr_t>>>*,
505ed4de7a8SEd Tanous 
506ed4de7a8SEd Tanous     // Note, these types are kept for historical completeness, but should not be used,
507ed4de7a8SEd Tanous     // As they do not provide object type safety.  Instead, rely on nlohmann::json::object_t
508ed4de7a8SEd Tanous     // Will be removed Q2 2025
509ed4de7a8SEd Tanous     nlohmann::json*,
510ed4de7a8SEd Tanous     std::optional<std::vector<nlohmann::json>>*,
511ed4de7a8SEd Tanous     std::vector<nlohmann::json>*,
512ed4de7a8SEd Tanous     std::optional<nlohmann::json>*
513ea2e6eecSWilly Tu >;
514ea2e6eecSWilly Tu // clang-format on
515ea2e6eecSWilly Tu 
516ea2e6eecSWilly Tu struct PerUnpack
517ea2e6eecSWilly Tu {
518ea2e6eecSWilly Tu     std::string_view key;
519ea2e6eecSWilly Tu     UnpackVariant value;
520ea2e6eecSWilly Tu     bool complete = false;
521ea2e6eecSWilly Tu };
522ea2e6eecSWilly Tu 
523b6164cbeSEd Tanous inline bool readJsonHelperObject(nlohmann::json::object_t& obj,
524b6164cbeSEd Tanous                                  crow::Response& res,
525ea2e6eecSWilly Tu                                  std::span<PerUnpack> toUnpack)
5269712f8acSEd Tanous {
52741352c24SSantosh Puranik     bool result = true;
528b6164cbeSEd Tanous     for (auto& item : obj)
5299712f8acSEd Tanous     {
530ea2e6eecSWilly Tu         size_t unpackIndex = 0;
531ea2e6eecSWilly Tu         for (; unpackIndex < toUnpack.size(); unpackIndex++)
532ea2e6eecSWilly Tu         {
533ea2e6eecSWilly Tu             PerUnpack& unpackSpec = toUnpack[unpackIndex];
534ea2e6eecSWilly Tu             std::string_view key = unpackSpec.key;
535ea2e6eecSWilly Tu             size_t keysplitIndex = key.find('/');
536ea2e6eecSWilly Tu             std::string_view leftover;
537ea2e6eecSWilly Tu             if (keysplitIndex != std::string_view::npos)
538ea2e6eecSWilly Tu             {
539ea2e6eecSWilly Tu                 leftover = key.substr(keysplitIndex + 1);
540ea2e6eecSWilly Tu                 key = key.substr(0, keysplitIndex);
541ea2e6eecSWilly Tu             }
542ea2e6eecSWilly Tu 
543d91415c4SEd Tanous             if (key != item.first || unpackSpec.complete)
544ea2e6eecSWilly Tu             {
545ea2e6eecSWilly Tu                 continue;
546ea2e6eecSWilly Tu             }
547ea2e6eecSWilly Tu 
548ea2e6eecSWilly Tu             // Sublevel key
549ea2e6eecSWilly Tu             if (!leftover.empty())
550ea2e6eecSWilly Tu             {
551ea2e6eecSWilly Tu                 // Include the slash in the key so we can compare later
552ea2e6eecSWilly Tu                 key = unpackSpec.key.substr(0, keysplitIndex + 1);
5539a560319SEd Tanous                 nlohmann::json::object_t j;
5549a560319SEd Tanous                 result = details::unpackValue<nlohmann::json::object_t>(
5559a560319SEd Tanous                              item.second, key, res, j) &&
55641352c24SSantosh Puranik                          result;
55755f79e6fSEd Tanous                 if (!result)
558ea2e6eecSWilly Tu                 {
559ea2e6eecSWilly Tu                     return result;
5609712f8acSEd Tanous                 }
5619712f8acSEd Tanous 
562ea2e6eecSWilly Tu                 std::vector<PerUnpack> nextLevel;
563ea2e6eecSWilly Tu                 for (PerUnpack& p : toUnpack)
564ea2e6eecSWilly Tu                 {
565ea2e6eecSWilly Tu                     if (!p.key.starts_with(key))
566ea2e6eecSWilly Tu                     {
567ea2e6eecSWilly Tu                         continue;
568ea2e6eecSWilly Tu                     }
569ea2e6eecSWilly Tu                     std::string_view thisLeftover = p.key.substr(key.size());
570ea2e6eecSWilly Tu                     nextLevel.push_back({thisLeftover, p.value, false});
571ea2e6eecSWilly Tu                     p.complete = true;
5729712f8acSEd Tanous                 }
57377dd8813SKowalski, Kamil 
5749a560319SEd Tanous                 result = readJsonHelperObject(j, res, nextLevel) && result;
575ea2e6eecSWilly Tu                 break;
576ea2e6eecSWilly Tu             }
577ea2e6eecSWilly Tu 
578bd79bce8SPatrick Williams             result =
579bd79bce8SPatrick Williams                 std::visit(
5805ea927bbSEd Tanous                     [&item, &unpackSpec, &res](auto& val) {
581ea2e6eecSWilly Tu                         using ContainedT =
582ea2e6eecSWilly Tu                             std::remove_pointer_t<std::decay_t<decltype(val)>>;
583ea2e6eecSWilly Tu                         return details::unpackValue<ContainedT>(
584d91415c4SEd Tanous                             item.second, unpackSpec.key, res, *val);
585ea2e6eecSWilly Tu                     },
586ea2e6eecSWilly Tu                     unpackSpec.value) &&
587ea2e6eecSWilly Tu                 result;
588ea2e6eecSWilly Tu 
589ea2e6eecSWilly Tu             unpackSpec.complete = true;
590ea2e6eecSWilly Tu             break;
591ea2e6eecSWilly Tu         }
592ea2e6eecSWilly Tu 
593ea2e6eecSWilly Tu         if (unpackIndex == toUnpack.size())
594ea2e6eecSWilly Tu         {
595d91415c4SEd Tanous             messages::propertyUnknown(res, item.first);
596ea2e6eecSWilly Tu             result = false;
597ea2e6eecSWilly Tu         }
598ea2e6eecSWilly Tu     }
599ea2e6eecSWilly Tu 
600ea2e6eecSWilly Tu     for (PerUnpack& perUnpack : toUnpack)
601ea2e6eecSWilly Tu     {
60255f79e6fSEd Tanous         if (!perUnpack.complete)
603ea2e6eecSWilly Tu         {
604ea2e6eecSWilly Tu             bool isOptional = std::visit(
6055ea927bbSEd Tanous                 [](auto& val) {
606ea2e6eecSWilly Tu                     using ContainedType =
607ea2e6eecSWilly Tu                         std::remove_pointer_t<std::decay_t<decltype(val)>>;
608ea2e6eecSWilly Tu                     return details::IsOptional<ContainedType>::value;
609ea2e6eecSWilly Tu                 },
610ea2e6eecSWilly Tu                 perUnpack.value);
611ea2e6eecSWilly Tu             if (isOptional)
612ea2e6eecSWilly Tu             {
613ea2e6eecSWilly Tu                 continue;
614ea2e6eecSWilly Tu             }
615ea2e6eecSWilly Tu             messages::propertyMissing(res, perUnpack.key);
616ea2e6eecSWilly Tu             result = false;
617ea2e6eecSWilly Tu         }
618ea2e6eecSWilly Tu     }
619ea2e6eecSWilly Tu     return result;
620ea2e6eecSWilly Tu }
621ea2e6eecSWilly Tu 
62289492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {}
623ea2e6eecSWilly Tu 
624ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
625ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key,
6265ea927bbSEd Tanous                  FirstType&& first, UnpackTypes&&... in)
627ea2e6eecSWilly Tu {
628ea2e6eecSWilly Tu     if (toPack.empty())
629ea2e6eecSWilly Tu     {
630ea2e6eecSWilly Tu         return;
631ea2e6eecSWilly Tu     }
632ea2e6eecSWilly Tu     toPack[0].key = key;
633ea2e6eecSWilly Tu     toPack[0].value = &first;
634ea2e6eecSWilly Tu     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
635ea2e6eecSWilly Tu     packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...);
636ea2e6eecSWilly Tu }
637ea2e6eecSWilly Tu 
638ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
639b6164cbeSEd Tanous bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res,
640b6164cbeSEd Tanous                     std::string_view key, FirstType&& first,
641b6164cbeSEd Tanous                     UnpackTypes&&... in)
642ea2e6eecSWilly Tu {
643ea2e6eecSWilly Tu     const std::size_t n = sizeof...(UnpackTypes) + 2;
644ea2e6eecSWilly Tu     std::array<PerUnpack, n / 2> toUnpack2;
6455ea927bbSEd Tanous     packVariant(toUnpack2, key, std::forward<FirstType>(first),
6465ea927bbSEd Tanous                 std::forward<UnpackTypes&&>(in)...);
647b6164cbeSEd Tanous     return readJsonHelperObject(jsonRequest, res, toUnpack2);
648ea2e6eecSWilly Tu }
649ea2e6eecSWilly Tu 
650b6164cbeSEd Tanous template <typename FirstType, typename... UnpackTypes>
651b6164cbeSEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res,
652b6164cbeSEd Tanous               std::string_view key, FirstType&& first, UnpackTypes&&... in)
653b6164cbeSEd Tanous {
654b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
655b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
656b6164cbeSEd Tanous     if (obj == nullptr)
657b6164cbeSEd Tanous     {
658b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
659b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
660b6164cbeSEd Tanous         return false;
661b6164cbeSEd Tanous     }
6625be2b14aSEd Tanous     return readJsonObject(*obj, res, key, std::forward<FirstType>(first),
6635be2b14aSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
664b6164cbeSEd Tanous }
665b6164cbeSEd Tanous 
666504af5a0SPatrick Williams inline std::optional<nlohmann::json::object_t> readJsonPatchHelper(
667504af5a0SPatrick Williams     const crow::Request& req, crow::Response& res)
6680627a2c7SEd Tanous {
6690627a2c7SEd Tanous     nlohmann::json jsonRequest;
6700627a2c7SEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
6710627a2c7SEd Tanous     {
67262598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
673ea2e6eecSWilly Tu         return std::nullopt;
6740627a2c7SEd Tanous     }
675357bb8f8SEd Tanous     nlohmann::json::object_t* object =
676357bb8f8SEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
677357bb8f8SEd Tanous     if (object == nullptr || object->empty())
67815ed6780SWilly Tu     {
67962598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
68015ed6780SWilly Tu         messages::emptyJSON(res);
681ea2e6eecSWilly Tu         return std::nullopt;
682ea2e6eecSWilly Tu     }
683357bb8f8SEd Tanous     std::erase_if(*object,
684357bb8f8SEd Tanous                   [](const std::pair<std::string, nlohmann::json>& item) {
685357bb8f8SEd Tanous                       return item.first.starts_with("@odata.");
686357bb8f8SEd Tanous                   });
687357bb8f8SEd Tanous     if (object->empty())
688357bb8f8SEd Tanous     {
689357bb8f8SEd Tanous         //  If the update request only contains OData annotations, the service
690357bb8f8SEd Tanous         //  should return the HTTP 400 Bad Request status code with the
691357bb8f8SEd Tanous         //  NoOperation message from the Base Message Registry, ...
692357bb8f8SEd Tanous         messages::noOperation(res);
693357bb8f8SEd Tanous         return std::nullopt;
694357bb8f8SEd Tanous     }
695357bb8f8SEd Tanous 
696b6164cbeSEd Tanous     return {std::move(*object)};
697ea2e6eecSWilly Tu }
698ea2e6eecSWilly Tu 
6993c9e6b1cSChandramohan Harkude inline const nlohmann::json* findNestedKey(std::string_view key,
7003c9e6b1cSChandramohan Harkude                                            const nlohmann::json& value)
7013c9e6b1cSChandramohan Harkude {
7023c9e6b1cSChandramohan Harkude     size_t keysplitIndex = key.find('/');
7033c9e6b1cSChandramohan Harkude     std::string_view leftover;
7043c9e6b1cSChandramohan Harkude     nlohmann::json::const_iterator it;
7053c9e6b1cSChandramohan Harkude     if (keysplitIndex != std::string_view::npos)
7063c9e6b1cSChandramohan Harkude     {
7073c9e6b1cSChandramohan Harkude         const nlohmann::json::object_t* obj =
7083c9e6b1cSChandramohan Harkude             value.get_ptr<const nlohmann::json::object_t*>();
7093c9e6b1cSChandramohan Harkude         if (obj == nullptr || obj->empty())
7103c9e6b1cSChandramohan Harkude         {
7113c9e6b1cSChandramohan Harkude             BMCWEB_LOG_ERROR("Requested key wasn't an object");
7123c9e6b1cSChandramohan Harkude             return nullptr;
7133c9e6b1cSChandramohan Harkude         }
7143c9e6b1cSChandramohan Harkude 
7153c9e6b1cSChandramohan Harkude         leftover = key.substr(keysplitIndex + 1);
7163c9e6b1cSChandramohan Harkude         std::string_view keypart = key.substr(0, keysplitIndex);
7173c9e6b1cSChandramohan Harkude         it = value.find(keypart);
7183c9e6b1cSChandramohan Harkude         if (it == value.end())
7193c9e6b1cSChandramohan Harkude         {
7203c9e6b1cSChandramohan Harkude             // Entry didn't have key
7213c9e6b1cSChandramohan Harkude             return nullptr;
7223c9e6b1cSChandramohan Harkude         }
7233c9e6b1cSChandramohan Harkude         return findNestedKey(leftover, it.value());
7243c9e6b1cSChandramohan Harkude     }
7253c9e6b1cSChandramohan Harkude 
7263c9e6b1cSChandramohan Harkude     it = value.find(key);
7273c9e6b1cSChandramohan Harkude     if (it == value.end())
7283c9e6b1cSChandramohan Harkude     {
7293c9e6b1cSChandramohan Harkude         return nullptr;
7303c9e6b1cSChandramohan Harkude     }
7313c9e6b1cSChandramohan Harkude     return &*it;
7323c9e6b1cSChandramohan Harkude }
7333c9e6b1cSChandramohan Harkude 
734ea2e6eecSWilly Tu template <typename... UnpackTypes>
735ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res,
736ea2e6eecSWilly Tu                    std::string_view key, UnpackTypes&&... in)
737ea2e6eecSWilly Tu {
73876b038f2SEd Tanous     std::optional<nlohmann::json::object_t> jsonRequest =
73976b038f2SEd Tanous         readJsonPatchHelper(req, res);
740e01d0c36SEd Tanous     if (!jsonRequest)
741ea2e6eecSWilly Tu     {
74215ed6780SWilly Tu         return false;
74315ed6780SWilly Tu     }
74476b038f2SEd Tanous     if (jsonRequest->empty())
745b6164cbeSEd Tanous     {
746b6164cbeSEd Tanous         messages::emptyJSON(res);
747b6164cbeSEd Tanous         return false;
748b6164cbeSEd Tanous     }
74915ed6780SWilly Tu 
75076b038f2SEd Tanous     return readJsonObject(*jsonRequest, res, key,
751b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
75215ed6780SWilly Tu }
75315ed6780SWilly Tu 
754c1a75ebcSrohitpai inline std::optional<nlohmann::json::json_pointer>
755c1a75ebcSrohitpai     createJsonPointerFromFragment(std::string_view input)
756c1a75ebcSrohitpai {
757c1a75ebcSrohitpai     auto hashPos = input.find('#');
758c1a75ebcSrohitpai     if (hashPos == std::string_view::npos || hashPos + 1 >= input.size())
759c1a75ebcSrohitpai     {
760c1a75ebcSrohitpai         BMCWEB_LOG_ERROR(
761c1a75ebcSrohitpai             "createJsonPointerFromFragment() No fragment found after #");
762c1a75ebcSrohitpai         return std::nullopt;
763c1a75ebcSrohitpai     }
764c1a75ebcSrohitpai 
765c1a75ebcSrohitpai     std::string_view fragment = input.substr(hashPos + 1);
766c1a75ebcSrohitpai     return nlohmann::json::json_pointer(std::string(fragment));
767c1a75ebcSrohitpai }
768c1a75ebcSrohitpai 
76915ed6780SWilly Tu template <typename... UnpackTypes>
77015ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res,
771ea2e6eecSWilly Tu                     const char* key, UnpackTypes&&... in)
77215ed6780SWilly Tu {
77315ed6780SWilly Tu     nlohmann::json jsonRequest;
77415ed6780SWilly Tu     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
77515ed6780SWilly Tu     {
77662598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
77715ed6780SWilly Tu         return false;
77815ed6780SWilly Tu     }
779b6164cbeSEd Tanous     nlohmann::json::object_t* object =
780b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
781b6164cbeSEd Tanous     if (object == nullptr)
782b6164cbeSEd Tanous     {
783b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
784b6164cbeSEd Tanous         messages::emptyJSON(res);
785b6164cbeSEd Tanous         return false;
786b6164cbeSEd Tanous     }
787b6164cbeSEd Tanous     return readJsonObject(*object, res, key,
788b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
7890627a2c7SEd Tanous }
790185444b1SNan Zhou 
791185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the
792185444b1SNan Zhou // @odata.id key
7934e196b9aSEd Tanous inline int objectKeyCmp(std::string_view key, const nlohmann::json& a,
7944e196b9aSEd Tanous                         const nlohmann::json& b)
795185444b1SNan Zhou {
796185444b1SNan Zhou     using object_t = nlohmann::json::object_t;
797185444b1SNan Zhou     const object_t* aObj = a.get_ptr<const object_t*>();
798185444b1SNan Zhou     const object_t* bObj = b.get_ptr<const object_t*>();
799185444b1SNan Zhou 
800185444b1SNan Zhou     if (aObj == nullptr)
801185444b1SNan Zhou     {
802185444b1SNan Zhou         if (bObj == nullptr)
803185444b1SNan Zhou         {
804185444b1SNan Zhou             return 0;
805185444b1SNan Zhou         }
806185444b1SNan Zhou         return -1;
807185444b1SNan Zhou     }
808185444b1SNan Zhou     if (bObj == nullptr)
809185444b1SNan Zhou     {
810185444b1SNan Zhou         return 1;
811185444b1SNan Zhou     }
8124e196b9aSEd Tanous     object_t::const_iterator aIt = aObj->find(key);
8134e196b9aSEd Tanous     object_t::const_iterator bIt = bObj->find(key);
8144e196b9aSEd Tanous     // If either object doesn't have the key, they get "sorted" to the
8154e196b9aSEd Tanous     // beginning.
816185444b1SNan Zhou     if (aIt == aObj->end())
817185444b1SNan Zhou     {
818185444b1SNan Zhou         if (bIt == bObj->end())
819185444b1SNan Zhou         {
820185444b1SNan Zhou             return 0;
821185444b1SNan Zhou         }
822185444b1SNan Zhou         return -1;
823185444b1SNan Zhou     }
824185444b1SNan Zhou     if (bIt == bObj->end())
825185444b1SNan Zhou     {
826185444b1SNan Zhou         return 1;
827185444b1SNan Zhou     }
828185444b1SNan Zhou     const nlohmann::json::string_t* nameA =
829185444b1SNan Zhou         aIt->second.get_ptr<const std::string*>();
830185444b1SNan Zhou     const nlohmann::json::string_t* nameB =
831185444b1SNan Zhou         bIt->second.get_ptr<const std::string*>();
832185444b1SNan Zhou     // If either object doesn't have a string as the key, they get "sorted" to
8334e196b9aSEd Tanous     // the beginning.
834185444b1SNan Zhou     if (nameA == nullptr)
835185444b1SNan Zhou     {
836185444b1SNan Zhou         if (nameB == nullptr)
837185444b1SNan Zhou         {
838185444b1SNan Zhou             return 0;
839185444b1SNan Zhou         }
840185444b1SNan Zhou         return -1;
841185444b1SNan Zhou     }
842185444b1SNan Zhou     if (nameB == nullptr)
843185444b1SNan Zhou     {
844185444b1SNan Zhou         return 1;
845185444b1SNan Zhou     }
846e7bcf475SJayanth Othayoth     if (key != "@odata.id")
847e7bcf475SJayanth Othayoth     {
848e7bcf475SJayanth Othayoth         return alphanumComp(*nameA, *nameB);
849e7bcf475SJayanth Othayoth     }
850185444b1SNan Zhou 
851e7bcf475SJayanth Othayoth     boost::system::result<boost::urls::url_view> aUrl =
852e7bcf475SJayanth Othayoth         boost::urls::parse_relative_ref(*nameA);
853e7bcf475SJayanth Othayoth     boost::system::result<boost::urls::url_view> bUrl =
854e7bcf475SJayanth Othayoth         boost::urls::parse_relative_ref(*nameB);
855e7bcf475SJayanth Othayoth     if (!aUrl)
856185444b1SNan Zhou     {
857e7bcf475SJayanth Othayoth         if (!bUrl)
858185444b1SNan Zhou         {
859185444b1SNan Zhou             return 0;
860185444b1SNan Zhou         }
861185444b1SNan Zhou         return -1;
862185444b1SNan Zhou     }
863e7bcf475SJayanth Othayoth     if (!bUrl)
864e7bcf475SJayanth Othayoth     {
865e7bcf475SJayanth Othayoth         return 1;
866e7bcf475SJayanth Othayoth     }
867e7bcf475SJayanth Othayoth 
868e7bcf475SJayanth Othayoth     auto segmentsAIt = aUrl->segments().begin();
869e7bcf475SJayanth Othayoth     auto segmentsBIt = bUrl->segments().begin();
870e7bcf475SJayanth Othayoth 
871e7bcf475SJayanth Othayoth     while (true)
872e7bcf475SJayanth Othayoth     {
873e7bcf475SJayanth Othayoth         if (segmentsAIt == aUrl->segments().end())
874e7bcf475SJayanth Othayoth         {
875e7bcf475SJayanth Othayoth             if (segmentsBIt == bUrl->segments().end())
876e7bcf475SJayanth Othayoth             {
877e7bcf475SJayanth Othayoth                 return 0;
878e7bcf475SJayanth Othayoth             }
879e7bcf475SJayanth Othayoth             return -1;
880e7bcf475SJayanth Othayoth         }
881e7bcf475SJayanth Othayoth         if (segmentsBIt == bUrl->segments().end())
882185444b1SNan Zhou         {
883185444b1SNan Zhou             return 1;
884185444b1SNan Zhou         }
885185444b1SNan Zhou         int res = alphanumComp(*segmentsAIt, *segmentsBIt);
886185444b1SNan Zhou         if (res != 0)
887185444b1SNan Zhou         {
888185444b1SNan Zhou             return res;
889185444b1SNan Zhou         }
890185444b1SNan Zhou 
891185444b1SNan Zhou         segmentsAIt++;
892185444b1SNan Zhou         segmentsBIt++;
893185444b1SNan Zhou     }
894e7bcf475SJayanth Othayoth     return 0;
895185444b1SNan Zhou };
896185444b1SNan Zhou 
8974e196b9aSEd Tanous // kept for backward compatibility
8984e196b9aSEd Tanous inline int odataObjectCmp(const nlohmann::json& left,
8994e196b9aSEd Tanous                           const nlohmann::json& right)
9004e196b9aSEd Tanous {
9014e196b9aSEd Tanous     return objectKeyCmp("@odata.id", left, right);
9024e196b9aSEd Tanous }
9034e196b9aSEd Tanous 
904185444b1SNan Zhou struct ODataObjectLess
905185444b1SNan Zhou {
9064e196b9aSEd Tanous     std::string_view key;
9074e196b9aSEd Tanous 
9084e196b9aSEd Tanous     explicit ODataObjectLess(std::string_view keyIn) : key(keyIn) {}
9094e196b9aSEd Tanous 
910185444b1SNan Zhou     bool operator()(const nlohmann::json& left,
911185444b1SNan Zhou                     const nlohmann::json& right) const
912185444b1SNan Zhou     {
9134e196b9aSEd Tanous         return objectKeyCmp(key, left, right) < 0;
914185444b1SNan Zhou     }
915185444b1SNan Zhou };
916185444b1SNan Zhou 
917185444b1SNan Zhou // Sort the JSON array by |element[key]|.
918185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller
919185444b1SNan Zhou // those whose |element[key]| is string.
9204e196b9aSEd Tanous inline void sortJsonArrayByKey(nlohmann::json::array_t& array,
9214e196b9aSEd Tanous                                std::string_view key)
9224e196b9aSEd Tanous {
9234e196b9aSEd Tanous     std::ranges::sort(array, ODataObjectLess(key));
9244e196b9aSEd Tanous }
9254e196b9aSEd Tanous 
9264e196b9aSEd Tanous // Sort the JSON array by |element[key]|.
9274e196b9aSEd Tanous // Elements without |key| or type of |element[key]| is not string are smaller
9284e196b9aSEd Tanous // those whose |element[key]| is string.
929185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array)
930185444b1SNan Zhou {
9314e196b9aSEd Tanous     std::ranges::sort(array, ODataObjectLess("@odata.id"));
932185444b1SNan Zhou }
933185444b1SNan Zhou 
9348a7c4b47SNan Zhou // Returns the estimated size of the JSON value
9358a7c4b47SNan Zhou // The implementation walks through every key and every value, accumulates the
9368a7c4b47SNan Zhou //  total size of keys and values.
9378a7c4b47SNan Zhou // Ideally, we should use a custom allocator that nlohmann JSON supports.
9388a7c4b47SNan Zhou 
9398a7c4b47SNan Zhou // Assumption made:
9408a7c4b47SNan Zhou //  1. number: 8 characters
9418a7c4b47SNan Zhou //  2. boolean: 5 characters (False)
9428a7c4b47SNan Zhou //  3. string: len(str) + 2 characters (quote)
9438a7c4b47SNan Zhou //  4. bytes: len(bytes) characters
9448a7c4b47SNan Zhou //  5. null: 4 characters (null)
9458a7c4b47SNan Zhou uint64_t getEstimatedJsonSize(const nlohmann::json& root);
9468a7c4b47SNan Zhou 
947*08fad5d9SCorey Ethington // Hashes a json value, recursively omitting every member with key `keyToIgnore`
948*08fad5d9SCorey Ethington inline size_t hashJsonWithoutKey(const nlohmann::json& jsonValue,
949*08fad5d9SCorey Ethington                                  std::string_view keyToIgnore)
950*08fad5d9SCorey Ethington {
951*08fad5d9SCorey Ethington     const nlohmann::json::object_t* obj =
952*08fad5d9SCorey Ethington         jsonValue.get_ptr<const nlohmann::json::object_t*>();
953*08fad5d9SCorey Ethington     if (obj == nullptr)
954*08fad5d9SCorey Ethington     {
955*08fad5d9SCorey Ethington         // Object has no keys to remove so just return hash
956*08fad5d9SCorey Ethington         return std::hash<nlohmann::json>{}(jsonValue);
957*08fad5d9SCorey Ethington     }
958*08fad5d9SCorey Ethington 
959*08fad5d9SCorey Ethington     const size_t type = static_cast<std::size_t>(jsonValue.type());
960*08fad5d9SCorey Ethington     size_t seed = details::combine(type, jsonValue.size());
961*08fad5d9SCorey Ethington     for (const auto& element : *obj)
962*08fad5d9SCorey Ethington     {
963*08fad5d9SCorey Ethington         const size_t h = std::hash<std::string>{}(element.first);
964*08fad5d9SCorey Ethington         seed = details::combine(seed, h);
965*08fad5d9SCorey Ethington         if (element.first != keyToIgnore)
966*08fad5d9SCorey Ethington         {
967*08fad5d9SCorey Ethington             seed = details::combine(
968*08fad5d9SCorey Ethington                 seed, std::hash<nlohmann::json>{}(element.second));
969*08fad5d9SCorey Ethington         }
970*08fad5d9SCorey Ethington     }
971*08fad5d9SCorey Ethington     return seed;
972*08fad5d9SCorey Ethington }
973*08fad5d9SCorey Ethington 
97477dd8813SKowalski, Kamil } // namespace json_util
97577dd8813SKowalski, Kamil } // namespace redfish
976