xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision 504af5a0568171b72caf13234cc81380b261fa21)
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>
98c09966bdSEd Tanous bool checkRange(const FromType& from [[maybe_unused]],
99c09966bdSEd Tanous                 std::string_view key [[maybe_unused]])
100a6acbb31SJames Feist {
101a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<ToType>)
102a6acbb31SJames Feist     {
103ee344e0fSEd Tanous         if (std::isnan(from))
104a6acbb31SJames Feist         {
10562598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was NAN", key);
106a6acbb31SJames Feist             return false;
107a6acbb31SJames Feist         }
108a6acbb31SJames Feist     }
109c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::max() <
110c09966bdSEd Tanous                   std::numeric_limits<FromType>::max())
111c09966bdSEd Tanous     {
112c09966bdSEd Tanous         if (from > std::numeric_limits<ToType>::max())
113c09966bdSEd Tanous         {
114c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key,
115c09966bdSEd Tanous                              std::numeric_limits<FromType>::max());
116c09966bdSEd Tanous             return false;
117c09966bdSEd Tanous         }
118c09966bdSEd Tanous     }
119c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::lowest() >
120c09966bdSEd Tanous                   std::numeric_limits<FromType>::lowest())
121c09966bdSEd Tanous     {
122c09966bdSEd Tanous         if (from < std::numeric_limits<ToType>::lowest())
123c09966bdSEd Tanous         {
124c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key,
125c09966bdSEd Tanous                              std::numeric_limits<FromType>::lowest());
126c09966bdSEd Tanous             return false;
127c09966bdSEd Tanous         }
128c09966bdSEd Tanous     }
129a6acbb31SJames Feist 
130a6acbb31SJames Feist     return true;
131a6acbb31SJames Feist }
132a6acbb31SJames Feist 
133771cfa0fSJason M. Bills template <typename Type>
134471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
1358099c517SEd Tanous                                          std::string_view key, Type& value);
1368099c517SEd Tanous 
1378099c517SEd Tanous template <std::size_t Index = 0, typename... Args>
1388099c517SEd Tanous UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key,
1398099c517SEd Tanous                                    std::variant<Args...>& v)
1408099c517SEd Tanous {
1418099c517SEd Tanous     if constexpr (Index < std::variant_size_v<std::variant<Args...>>)
1428099c517SEd Tanous     {
143ed4de7a8SEd Tanous         std::variant_alternative_t<Index, std::variant<Args...>> type{};
1448099c517SEd Tanous         UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type);
1458099c517SEd Tanous         if (unpack == UnpackErrorCode::success)
1468099c517SEd Tanous         {
1478099c517SEd Tanous             v = std::move(type);
1488099c517SEd Tanous             return unpack;
1498099c517SEd Tanous         }
1508099c517SEd Tanous 
1518099c517SEd Tanous         return unpackValueVariant<Index + 1, Args...>(j, key, v);
1528099c517SEd Tanous     }
1538099c517SEd Tanous     return UnpackErrorCode::invalidType;
1548099c517SEd Tanous }
1558099c517SEd Tanous 
1568099c517SEd Tanous template <typename Type>
1578099c517SEd Tanous UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
158ea2e6eecSWilly Tu                                          std::string_view key, Type& value)
159771cfa0fSJason M. Bills {
160471a5eb8SAppaRao Puli     UnpackErrorCode ret = UnpackErrorCode::success;
16141352c24SSantosh Puranik 
162a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<Type>)
163771cfa0fSJason M. Bills     {
164a6acbb31SJames Feist         double helper = 0;
165a6acbb31SJames Feist         double* jsonPtr = jsonValue.get_ptr<double*>();
166771cfa0fSJason M. Bills 
167771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
168771cfa0fSJason M. Bills         {
169a6acbb31SJames Feist             int64_t* intPtr = jsonValue.get_ptr<int64_t*>();
170a6acbb31SJames Feist             if (intPtr != nullptr)
171771cfa0fSJason M. Bills             {
172a6acbb31SJames Feist                 helper = static_cast<double>(*intPtr);
173a6acbb31SJames Feist                 jsonPtr = &helper;
174771cfa0fSJason M. Bills             }
175a6acbb31SJames Feist         }
1765eb2bef2SAppaRao Puli         if (jsonPtr == nullptr)
1775eb2bef2SAppaRao Puli         {
178471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
1795eb2bef2SAppaRao Puli         }
180cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
181771cfa0fSJason M. Bills         {
182471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
183771cfa0fSJason M. Bills         }
184771cfa0fSJason M. Bills         value = static_cast<Type>(*jsonPtr);
185771cfa0fSJason M. Bills     }
186a6acbb31SJames Feist 
187a6acbb31SJames Feist     else if constexpr (std::is_signed_v<Type>)
188a6acbb31SJames Feist     {
189a6acbb31SJames Feist         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
190271584abSEd Tanous         if (jsonPtr == nullptr)
191271584abSEd Tanous         {
192471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
193271584abSEd Tanous         }
194cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
195a6acbb31SJames Feist         {
196471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
197a6acbb31SJames Feist         }
198a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
199a6acbb31SJames Feist     }
200a6acbb31SJames Feist 
201bd79bce8SPatrick Williams     else if constexpr ((std::is_unsigned_v<Type>) &&
202bd79bce8SPatrick Williams                        (!std::is_same_v<bool, Type>))
203a6acbb31SJames Feist     {
204a6acbb31SJames Feist         uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
205271584abSEd Tanous         if (jsonPtr == nullptr)
206271584abSEd Tanous         {
207471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
208271584abSEd Tanous         }
209cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
210a6acbb31SJames Feist         {
211471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
212a6acbb31SJames Feist         }
213a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
214a6acbb31SJames Feist     }
215a6acbb31SJames Feist 
2160627a2c7SEd Tanous     else if constexpr (std::is_same_v<nlohmann::json, Type>)
2170627a2c7SEd Tanous     {
2180627a2c7SEd Tanous         value = std::move(jsonValue);
2190627a2c7SEd Tanous     }
2208099c517SEd Tanous     else if constexpr (std::is_same_v<std::nullptr_t, Type>)
2218099c517SEd Tanous     {
2228099c517SEd Tanous         if (!jsonValue.is_null())
2238099c517SEd Tanous         {
2248099c517SEd Tanous             return UnpackErrorCode::invalidType;
2258099c517SEd Tanous         }
2268099c517SEd Tanous     }
227ed4de7a8SEd Tanous     else if constexpr (IsVector<Type>::value)
228ed4de7a8SEd Tanous     {
229ed4de7a8SEd Tanous         nlohmann::json::object_t* obj =
230ed4de7a8SEd Tanous             jsonValue.get_ptr<nlohmann::json::object_t*>();
231ed4de7a8SEd Tanous         if (obj == nullptr)
232ed4de7a8SEd Tanous         {
233ed4de7a8SEd Tanous             return UnpackErrorCode::invalidType;
234ed4de7a8SEd Tanous         }
235ed4de7a8SEd Tanous 
236ed4de7a8SEd Tanous         for (const auto& val : *obj)
237ed4de7a8SEd Tanous         {
238ed4de7a8SEd Tanous             value.emplace_back();
239ed4de7a8SEd Tanous             ret = unpackValueWithErrorCode<typename Type::value_type>(
240ed4de7a8SEd Tanous                       val, key, value.back()) &&
241ed4de7a8SEd Tanous                   ret;
242ed4de7a8SEd Tanous         }
243ed4de7a8SEd Tanous     }
244471a5eb8SAppaRao Puli     else
245471a5eb8SAppaRao Puli     {
246471a5eb8SAppaRao Puli         using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
247471a5eb8SAppaRao Puli         JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
248471a5eb8SAppaRao Puli         if (jsonPtr == nullptr)
249471a5eb8SAppaRao Puli         {
25062598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key,
25162598e31SEd Tanous                              jsonValue.type_name());
252471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
253471a5eb8SAppaRao Puli         }
254471a5eb8SAppaRao Puli         value = std::move(*jsonPtr);
255471a5eb8SAppaRao Puli     }
256471a5eb8SAppaRao Puli     return ret;
257471a5eb8SAppaRao Puli }
258471a5eb8SAppaRao Puli 
259471a5eb8SAppaRao Puli template <typename Type>
260ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key,
261471a5eb8SAppaRao Puli                  crow::Response& res, Type& value)
262471a5eb8SAppaRao Puli {
263471a5eb8SAppaRao Puli     bool ret = true;
264471a5eb8SAppaRao Puli 
2652c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
266471a5eb8SAppaRao Puli     {
267471a5eb8SAppaRao Puli         value.emplace();
268471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
269471a5eb8SAppaRao Puli                                                      *value) &&
270471a5eb8SAppaRao Puli               ret;
271471a5eb8SAppaRao Puli     }
2722c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
273318226c2SJames Feist     {
2740bdda665SEd Tanous         nlohmann::json::array_t* arr =
2750bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
2760bdda665SEd Tanous         if (arr == nullptr)
277318226c2SJames Feist         {
2782e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
27941352c24SSantosh Puranik             return false;
280318226c2SJames Feist         }
281318226c2SJames Feist         if (jsonValue.size() != value.size())
282318226c2SJames Feist         {
2832e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
28441352c24SSantosh Puranik             return false;
285318226c2SJames Feist         }
286318226c2SJames Feist         size_t index = 0;
2870bdda665SEd Tanous         for (auto& val : *arr)
288318226c2SJames Feist         {
2890bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
29041352c24SSantosh Puranik                                                          value[index++]) &&
29141352c24SSantosh Puranik                   ret;
292318226c2SJames Feist         }
293318226c2SJames Feist     }
2942c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
295b1556427SEd Tanous     {
2960bdda665SEd Tanous         nlohmann::json::array_t* arr =
2970bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
2980bdda665SEd Tanous         if (arr == nullptr)
299b1556427SEd Tanous         {
3002e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
30141352c24SSantosh Puranik             return false;
302b1556427SEd Tanous         }
303b1556427SEd Tanous 
3040bdda665SEd Tanous         for (auto& val : *arr)
305b1556427SEd Tanous         {
306b1556427SEd Tanous             value.emplace_back();
3070bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
30841352c24SSantosh Puranik                                                          value.back()) &&
30941352c24SSantosh Puranik                   ret;
310b1556427SEd Tanous         }
311b1556427SEd Tanous     }
3128099c517SEd Tanous     else if constexpr (IsVariant<Type>::value)
3138099c517SEd Tanous     {
3148099c517SEd Tanous         UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value);
3158099c517SEd Tanous         if (ec != UnpackErrorCode::success)
3168099c517SEd Tanous         {
3178099c517SEd Tanous             if (ec == UnpackErrorCode::invalidType)
3188099c517SEd Tanous             {
3198099c517SEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
3208099c517SEd Tanous             }
3218099c517SEd Tanous             else if (ec == UnpackErrorCode::outOfRange)
3228099c517SEd Tanous             {
323340d74c8SMyung Bae                 messages::propertyValueOutOfRange(res, jsonValue, key);
3248099c517SEd Tanous             }
3258099c517SEd Tanous             return false;
3268099c517SEd Tanous         }
3278099c517SEd Tanous     }
328771cfa0fSJason M. Bills     else
329771cfa0fSJason M. Bills     {
330471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
331471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
332771cfa0fSJason M. Bills         {
333471a5eb8SAppaRao Puli             if (ec == UnpackErrorCode::invalidType)
334471a5eb8SAppaRao Puli             {
3352e8c4bdaSEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
336471a5eb8SAppaRao Puli             }
337471a5eb8SAppaRao Puli             else if (ec == UnpackErrorCode::outOfRange)
338471a5eb8SAppaRao Puli             {
339340d74c8SMyung Bae                 messages::propertyValueOutOfRange(res, jsonValue, key);
340471a5eb8SAppaRao Puli             }
34141352c24SSantosh Puranik             return false;
342771cfa0fSJason M. Bills         }
343771cfa0fSJason M. Bills     }
344471a5eb8SAppaRao Puli 
345471a5eb8SAppaRao Puli     return ret;
346471a5eb8SAppaRao Puli }
347471a5eb8SAppaRao Puli 
348471a5eb8SAppaRao Puli template <typename Type>
349ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value)
350471a5eb8SAppaRao Puli {
351471a5eb8SAppaRao Puli     bool ret = true;
3522c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
353471a5eb8SAppaRao Puli     {
354471a5eb8SAppaRao Puli         value.emplace();
355471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) &&
356471a5eb8SAppaRao Puli               ret;
357471a5eb8SAppaRao Puli     }
3582c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
359471a5eb8SAppaRao Puli     {
3600bdda665SEd Tanous         nlohmann::json::array_t* arr =
3610bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3620bdda665SEd Tanous         if (arr == nullptr)
363471a5eb8SAppaRao Puli         {
364471a5eb8SAppaRao Puli             return false;
365471a5eb8SAppaRao Puli         }
366471a5eb8SAppaRao Puli         if (jsonValue.size() != value.size())
367471a5eb8SAppaRao Puli         {
368471a5eb8SAppaRao Puli             return false;
369471a5eb8SAppaRao Puli         }
370471a5eb8SAppaRao Puli         size_t index = 0;
3710bdda665SEd Tanous         for (const auto& val : *arr)
372471a5eb8SAppaRao Puli         {
3730bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
374471a5eb8SAppaRao Puli                                                          value[index++]) &&
375471a5eb8SAppaRao Puli                   ret;
376471a5eb8SAppaRao Puli         }
377471a5eb8SAppaRao Puli     }
3782c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
379471a5eb8SAppaRao Puli     {
3800bdda665SEd Tanous         nlohmann::json::array_t* arr =
3810bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3820bdda665SEd Tanous         if (arr == nullptr)
383471a5eb8SAppaRao Puli         {
384471a5eb8SAppaRao Puli             return false;
385471a5eb8SAppaRao Puli         }
386471a5eb8SAppaRao Puli 
3870bdda665SEd Tanous         for (const auto& val : *arr)
388471a5eb8SAppaRao Puli         {
389471a5eb8SAppaRao Puli             value.emplace_back();
3900bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
391471a5eb8SAppaRao Puli                                                          value.back()) &&
392471a5eb8SAppaRao Puli                   ret;
393471a5eb8SAppaRao Puli         }
394471a5eb8SAppaRao Puli     }
395471a5eb8SAppaRao Puli     else
396471a5eb8SAppaRao Puli     {
397471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
398471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
399471a5eb8SAppaRao Puli         {
400471a5eb8SAppaRao Puli             return false;
401471a5eb8SAppaRao Puli         }
402471a5eb8SAppaRao Puli     }
403471a5eb8SAppaRao Puli 
40441352c24SSantosh Puranik     return ret;
405771cfa0fSJason M. Bills }
4069712f8acSEd Tanous } // namespace details
4079712f8acSEd Tanous 
408ea2e6eecSWilly Tu // clang-format off
409ea2e6eecSWilly Tu using UnpackVariant = std::variant<
410ea2e6eecSWilly Tu     uint8_t*,
411ea2e6eecSWilly Tu     uint16_t*,
412ea2e6eecSWilly Tu     int16_t*,
413ea2e6eecSWilly Tu     uint32_t*,
414ea2e6eecSWilly Tu     int32_t*,
415ea2e6eecSWilly Tu     uint64_t*,
416ea2e6eecSWilly Tu     int64_t*,
417ea2e6eecSWilly Tu     bool*,
418ea2e6eecSWilly Tu     double*,
419ea2e6eecSWilly Tu     std::string*,
420b6164cbeSEd Tanous     nlohmann::json::object_t*,
4218099c517SEd Tanous     std::variant<std::string, std::nullptr_t>*,
4228099c517SEd Tanous     std::variant<uint8_t, std::nullptr_t>*,
4238099c517SEd Tanous     std::variant<int16_t, std::nullptr_t>*,
4248099c517SEd Tanous     std::variant<uint16_t, std::nullptr_t>*,
4258099c517SEd Tanous     std::variant<int32_t, std::nullptr_t>*,
4268099c517SEd Tanous     std::variant<uint32_t, std::nullptr_t>*,
4278099c517SEd Tanous     std::variant<int64_t, std::nullptr_t>*,
4288099c517SEd Tanous     std::variant<uint64_t, std::nullptr_t>*,
4298099c517SEd Tanous     std::variant<double, std::nullptr_t>*,
4308099c517SEd Tanous     std::variant<bool, std::nullptr_t>*,
431ea2e6eecSWilly Tu     std::vector<uint8_t>*,
432ea2e6eecSWilly Tu     std::vector<uint16_t>*,
433ea2e6eecSWilly Tu     std::vector<int16_t>*,
434ea2e6eecSWilly Tu     std::vector<uint32_t>*,
435ea2e6eecSWilly Tu     std::vector<int32_t>*,
436ea2e6eecSWilly Tu     std::vector<uint64_t>*,
437ea2e6eecSWilly Tu     std::vector<int64_t>*,
438ea2e6eecSWilly Tu     //std::vector<bool>*,
439ea2e6eecSWilly Tu     std::vector<double>*,
440ea2e6eecSWilly Tu     std::vector<std::string>*,
441b6164cbeSEd Tanous     std::vector<nlohmann::json::object_t>*,
442ea2e6eecSWilly Tu     std::optional<uint8_t>*,
443ea2e6eecSWilly Tu     std::optional<uint16_t>*,
444ea2e6eecSWilly Tu     std::optional<int16_t>*,
445ea2e6eecSWilly Tu     std::optional<uint32_t>*,
446ea2e6eecSWilly Tu     std::optional<int32_t>*,
447ea2e6eecSWilly Tu     std::optional<uint64_t>*,
448ea2e6eecSWilly Tu     std::optional<int64_t>*,
449ea2e6eecSWilly Tu     std::optional<bool>*,
450ea2e6eecSWilly Tu     std::optional<double>*,
451ea2e6eecSWilly Tu     std::optional<std::string>*,
452b6164cbeSEd Tanous     std::optional<nlohmann::json::object_t>*,
453ea2e6eecSWilly Tu     std::optional<std::vector<uint8_t>>*,
454ea2e6eecSWilly Tu     std::optional<std::vector<uint16_t>>*,
455ea2e6eecSWilly Tu     std::optional<std::vector<int16_t>>*,
456ea2e6eecSWilly Tu     std::optional<std::vector<uint32_t>>*,
457ea2e6eecSWilly Tu     std::optional<std::vector<int32_t>>*,
458ea2e6eecSWilly Tu     std::optional<std::vector<uint64_t>>*,
459ea2e6eecSWilly Tu     std::optional<std::vector<int64_t>>*,
460ea2e6eecSWilly Tu     //std::optional<std::vector<bool>>*,
461ea2e6eecSWilly Tu     std::optional<std::vector<double>>*,
462ea2e6eecSWilly Tu     std::optional<std::vector<std::string>>*,
4638099c517SEd Tanous     std::optional<std::vector<nlohmann::json::object_t>>*,
4648099c517SEd Tanous     std::optional<std::variant<std::string, std::nullptr_t>>*,
4658099c517SEd Tanous     std::optional<std::variant<uint8_t, std::nullptr_t>>*,
4668099c517SEd Tanous     std::optional<std::variant<int16_t, std::nullptr_t>>*,
4678099c517SEd Tanous     std::optional<std::variant<uint16_t, std::nullptr_t>>*,
4688099c517SEd Tanous     std::optional<std::variant<int32_t, std::nullptr_t>>*,
4698099c517SEd Tanous     std::optional<std::variant<uint32_t, std::nullptr_t>>*,
4708099c517SEd Tanous     std::optional<std::variant<int64_t, std::nullptr_t>>*,
4718099c517SEd Tanous     std::optional<std::variant<uint64_t, std::nullptr_t>>*,
4728099c517SEd Tanous     std::optional<std::variant<double, std::nullptr_t>>*,
4738099c517SEd Tanous     std::optional<std::variant<bool, std::nullptr_t>>*,
4748099c517SEd Tanous     std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*,
475ed4de7a8SEd Tanous     std::optional<std::vector<std::variant<std::string, nlohmann::json::object_t, std::nullptr_t>>>*,
476ed4de7a8SEd Tanous 
477ed4de7a8SEd Tanous     // Note, these types are kept for historical completeness, but should not be used,
478ed4de7a8SEd Tanous     // As they do not provide object type safety.  Instead, rely on nlohmann::json::object_t
479ed4de7a8SEd Tanous     // Will be removed Q2 2025
480ed4de7a8SEd Tanous     nlohmann::json*,
481ed4de7a8SEd Tanous     std::optional<std::vector<nlohmann::json>>*,
482ed4de7a8SEd Tanous     std::vector<nlohmann::json>*,
483ed4de7a8SEd Tanous     std::optional<nlohmann::json>*
484ea2e6eecSWilly Tu >;
485ea2e6eecSWilly Tu // clang-format on
486ea2e6eecSWilly Tu 
487ea2e6eecSWilly Tu struct PerUnpack
488ea2e6eecSWilly Tu {
489ea2e6eecSWilly Tu     std::string_view key;
490ea2e6eecSWilly Tu     UnpackVariant value;
491ea2e6eecSWilly Tu     bool complete = false;
492ea2e6eecSWilly Tu };
493ea2e6eecSWilly Tu 
494ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
495b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack);
496b6164cbeSEd Tanous 
497b6164cbeSEd Tanous inline bool readJsonHelperObject(nlohmann::json::object_t& obj,
498b6164cbeSEd Tanous                                  crow::Response& res,
499ea2e6eecSWilly Tu                                  std::span<PerUnpack> toUnpack)
5009712f8acSEd Tanous {
50141352c24SSantosh Puranik     bool result = true;
502b6164cbeSEd Tanous     for (auto& item : obj)
5039712f8acSEd Tanous     {
504ea2e6eecSWilly Tu         size_t unpackIndex = 0;
505ea2e6eecSWilly Tu         for (; unpackIndex < toUnpack.size(); unpackIndex++)
506ea2e6eecSWilly Tu         {
507ea2e6eecSWilly Tu             PerUnpack& unpackSpec = toUnpack[unpackIndex];
508ea2e6eecSWilly Tu             std::string_view key = unpackSpec.key;
509ea2e6eecSWilly Tu             size_t keysplitIndex = key.find('/');
510ea2e6eecSWilly Tu             std::string_view leftover;
511ea2e6eecSWilly Tu             if (keysplitIndex != std::string_view::npos)
512ea2e6eecSWilly Tu             {
513ea2e6eecSWilly Tu                 leftover = key.substr(keysplitIndex + 1);
514ea2e6eecSWilly Tu                 key = key.substr(0, keysplitIndex);
515ea2e6eecSWilly Tu             }
516ea2e6eecSWilly Tu 
517d91415c4SEd Tanous             if (key != item.first || unpackSpec.complete)
518ea2e6eecSWilly Tu             {
519ea2e6eecSWilly Tu                 continue;
520ea2e6eecSWilly Tu             }
521ea2e6eecSWilly Tu 
522ea2e6eecSWilly Tu             // Sublevel key
523ea2e6eecSWilly Tu             if (!leftover.empty())
524ea2e6eecSWilly Tu             {
525ea2e6eecSWilly Tu                 // Include the slash in the key so we can compare later
526ea2e6eecSWilly Tu                 key = unpackSpec.key.substr(0, keysplitIndex + 1);
527ea2e6eecSWilly Tu                 nlohmann::json j;
528d91415c4SEd Tanous                 result = details::unpackValue<nlohmann::json>(item.second, key,
529ea2e6eecSWilly Tu                                                               res, j) &&
53041352c24SSantosh Puranik                          result;
53155f79e6fSEd Tanous                 if (!result)
532ea2e6eecSWilly Tu                 {
533ea2e6eecSWilly Tu                     return result;
5349712f8acSEd Tanous                 }
5359712f8acSEd Tanous 
536ea2e6eecSWilly Tu                 std::vector<PerUnpack> nextLevel;
537ea2e6eecSWilly Tu                 for (PerUnpack& p : toUnpack)
538ea2e6eecSWilly Tu                 {
539ea2e6eecSWilly Tu                     if (!p.key.starts_with(key))
540ea2e6eecSWilly Tu                     {
541ea2e6eecSWilly Tu                         continue;
542ea2e6eecSWilly Tu                     }
543ea2e6eecSWilly Tu                     std::string_view thisLeftover = p.key.substr(key.size());
544ea2e6eecSWilly Tu                     nextLevel.push_back({thisLeftover, p.value, false});
545ea2e6eecSWilly Tu                     p.complete = true;
5469712f8acSEd Tanous                 }
54777dd8813SKowalski, Kamil 
548ea2e6eecSWilly Tu                 result = readJsonHelper(j, res, nextLevel) && result;
549ea2e6eecSWilly Tu                 break;
550ea2e6eecSWilly Tu             }
551ea2e6eecSWilly Tu 
552bd79bce8SPatrick Williams             result =
553bd79bce8SPatrick Williams                 std::visit(
5545ea927bbSEd Tanous                     [&item, &unpackSpec, &res](auto& val) {
555ea2e6eecSWilly Tu                         using ContainedT =
556ea2e6eecSWilly Tu                             std::remove_pointer_t<std::decay_t<decltype(val)>>;
557ea2e6eecSWilly Tu                         return details::unpackValue<ContainedT>(
558d91415c4SEd Tanous                             item.second, unpackSpec.key, res, *val);
559ea2e6eecSWilly Tu                     },
560ea2e6eecSWilly Tu                     unpackSpec.value) &&
561ea2e6eecSWilly Tu                 result;
562ea2e6eecSWilly Tu 
563ea2e6eecSWilly Tu             unpackSpec.complete = true;
564ea2e6eecSWilly Tu             break;
565ea2e6eecSWilly Tu         }
566ea2e6eecSWilly Tu 
567ea2e6eecSWilly Tu         if (unpackIndex == toUnpack.size())
568ea2e6eecSWilly Tu         {
569d91415c4SEd Tanous             messages::propertyUnknown(res, item.first);
570ea2e6eecSWilly Tu             result = false;
571ea2e6eecSWilly Tu         }
572ea2e6eecSWilly Tu     }
573ea2e6eecSWilly Tu 
574ea2e6eecSWilly Tu     for (PerUnpack& perUnpack : toUnpack)
575ea2e6eecSWilly Tu     {
57655f79e6fSEd Tanous         if (!perUnpack.complete)
577ea2e6eecSWilly Tu         {
578ea2e6eecSWilly Tu             bool isOptional = std::visit(
5795ea927bbSEd Tanous                 [](auto& val) {
580ea2e6eecSWilly Tu                     using ContainedType =
581ea2e6eecSWilly Tu                         std::remove_pointer_t<std::decay_t<decltype(val)>>;
582ea2e6eecSWilly Tu                     return details::IsOptional<ContainedType>::value;
583ea2e6eecSWilly Tu                 },
584ea2e6eecSWilly Tu                 perUnpack.value);
585ea2e6eecSWilly Tu             if (isOptional)
586ea2e6eecSWilly Tu             {
587ea2e6eecSWilly Tu                 continue;
588ea2e6eecSWilly Tu             }
589ea2e6eecSWilly Tu             messages::propertyMissing(res, perUnpack.key);
590ea2e6eecSWilly Tu             result = false;
591ea2e6eecSWilly Tu         }
592ea2e6eecSWilly Tu     }
593ea2e6eecSWilly Tu     return result;
594ea2e6eecSWilly Tu }
595ea2e6eecSWilly Tu 
596b6164cbeSEd Tanous inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
597b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack)
598b6164cbeSEd Tanous {
599b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
600b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
601b6164cbeSEd Tanous     if (obj == nullptr)
602b6164cbeSEd Tanous     {
603b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
604b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
605b6164cbeSEd Tanous         return false;
606b6164cbeSEd Tanous     }
607b6164cbeSEd Tanous     return readJsonHelperObject(*obj, res, toUnpack);
608b6164cbeSEd Tanous }
609b6164cbeSEd Tanous 
61089492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {}
611ea2e6eecSWilly Tu 
612ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
613ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key,
6145ea927bbSEd Tanous                  FirstType&& first, UnpackTypes&&... in)
615ea2e6eecSWilly Tu {
616ea2e6eecSWilly Tu     if (toPack.empty())
617ea2e6eecSWilly Tu     {
618ea2e6eecSWilly Tu         return;
619ea2e6eecSWilly Tu     }
620ea2e6eecSWilly Tu     toPack[0].key = key;
621ea2e6eecSWilly Tu     toPack[0].value = &first;
622ea2e6eecSWilly Tu     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
623ea2e6eecSWilly Tu     packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...);
624ea2e6eecSWilly Tu }
625ea2e6eecSWilly Tu 
626ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
627b6164cbeSEd Tanous bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res,
628b6164cbeSEd Tanous                     std::string_view key, FirstType&& first,
629b6164cbeSEd Tanous                     UnpackTypes&&... in)
630ea2e6eecSWilly Tu {
631ea2e6eecSWilly Tu     const std::size_t n = sizeof...(UnpackTypes) + 2;
632ea2e6eecSWilly Tu     std::array<PerUnpack, n / 2> toUnpack2;
6335ea927bbSEd Tanous     packVariant(toUnpack2, key, std::forward<FirstType>(first),
6345ea927bbSEd Tanous                 std::forward<UnpackTypes&&>(in)...);
635b6164cbeSEd Tanous     return readJsonHelperObject(jsonRequest, res, toUnpack2);
636ea2e6eecSWilly Tu }
637ea2e6eecSWilly Tu 
638b6164cbeSEd Tanous template <typename FirstType, typename... UnpackTypes>
639b6164cbeSEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res,
640b6164cbeSEd Tanous               std::string_view key, FirstType&& first, UnpackTypes&&... in)
641b6164cbeSEd Tanous {
642b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
643b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
644b6164cbeSEd Tanous     if (obj == nullptr)
645b6164cbeSEd Tanous     {
646b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
647b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
648b6164cbeSEd Tanous         return false;
649b6164cbeSEd Tanous     }
6505be2b14aSEd Tanous     return readJsonObject(*obj, res, key, std::forward<FirstType>(first),
6515be2b14aSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
652b6164cbeSEd Tanous }
653b6164cbeSEd Tanous 
654*504af5a0SPatrick Williams inline std::optional<nlohmann::json::object_t> readJsonPatchHelper(
655*504af5a0SPatrick Williams     const crow::Request& req, crow::Response& res)
6560627a2c7SEd Tanous {
6570627a2c7SEd Tanous     nlohmann::json jsonRequest;
6580627a2c7SEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
6590627a2c7SEd Tanous     {
66062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
661ea2e6eecSWilly Tu         return std::nullopt;
6620627a2c7SEd Tanous     }
663357bb8f8SEd Tanous     nlohmann::json::object_t* object =
664357bb8f8SEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
665357bb8f8SEd Tanous     if (object == nullptr || object->empty())
66615ed6780SWilly Tu     {
66762598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
66815ed6780SWilly Tu         messages::emptyJSON(res);
669ea2e6eecSWilly Tu         return std::nullopt;
670ea2e6eecSWilly Tu     }
671357bb8f8SEd Tanous     std::erase_if(*object,
672357bb8f8SEd Tanous                   [](const std::pair<std::string, nlohmann::json>& item) {
673357bb8f8SEd Tanous                       return item.first.starts_with("@odata.");
674357bb8f8SEd Tanous                   });
675357bb8f8SEd Tanous     if (object->empty())
676357bb8f8SEd Tanous     {
677357bb8f8SEd Tanous         //  If the update request only contains OData annotations, the service
678357bb8f8SEd Tanous         //  should return the HTTP 400 Bad Request status code with the
679357bb8f8SEd Tanous         //  NoOperation message from the Base Message Registry, ...
680357bb8f8SEd Tanous         messages::noOperation(res);
681357bb8f8SEd Tanous         return std::nullopt;
682357bb8f8SEd Tanous     }
683357bb8f8SEd Tanous 
684b6164cbeSEd Tanous     return {std::move(*object)};
685ea2e6eecSWilly Tu }
686ea2e6eecSWilly Tu 
6873c9e6b1cSChandramohan Harkude inline const nlohmann::json* findNestedKey(std::string_view key,
6883c9e6b1cSChandramohan Harkude                                            const nlohmann::json& value)
6893c9e6b1cSChandramohan Harkude {
6903c9e6b1cSChandramohan Harkude     size_t keysplitIndex = key.find('/');
6913c9e6b1cSChandramohan Harkude     std::string_view leftover;
6923c9e6b1cSChandramohan Harkude     nlohmann::json::const_iterator it;
6933c9e6b1cSChandramohan Harkude     if (keysplitIndex != std::string_view::npos)
6943c9e6b1cSChandramohan Harkude     {
6953c9e6b1cSChandramohan Harkude         const nlohmann::json::object_t* obj =
6963c9e6b1cSChandramohan Harkude             value.get_ptr<const nlohmann::json::object_t*>();
6973c9e6b1cSChandramohan Harkude         if (obj == nullptr || obj->empty())
6983c9e6b1cSChandramohan Harkude         {
6993c9e6b1cSChandramohan Harkude             BMCWEB_LOG_ERROR("Requested key wasn't an object");
7003c9e6b1cSChandramohan Harkude             return nullptr;
7013c9e6b1cSChandramohan Harkude         }
7023c9e6b1cSChandramohan Harkude 
7033c9e6b1cSChandramohan Harkude         leftover = key.substr(keysplitIndex + 1);
7043c9e6b1cSChandramohan Harkude         std::string_view keypart = key.substr(0, keysplitIndex);
7053c9e6b1cSChandramohan Harkude         it = value.find(keypart);
7063c9e6b1cSChandramohan Harkude         if (it == value.end())
7073c9e6b1cSChandramohan Harkude         {
7083c9e6b1cSChandramohan Harkude             // Entry didn't have key
7093c9e6b1cSChandramohan Harkude             return nullptr;
7103c9e6b1cSChandramohan Harkude         }
7113c9e6b1cSChandramohan Harkude         return findNestedKey(leftover, it.value());
7123c9e6b1cSChandramohan Harkude     }
7133c9e6b1cSChandramohan Harkude 
7143c9e6b1cSChandramohan Harkude     it = value.find(key);
7153c9e6b1cSChandramohan Harkude     if (it == value.end())
7163c9e6b1cSChandramohan Harkude     {
7173c9e6b1cSChandramohan Harkude         return nullptr;
7183c9e6b1cSChandramohan Harkude     }
7193c9e6b1cSChandramohan Harkude     return &*it;
7203c9e6b1cSChandramohan Harkude }
7213c9e6b1cSChandramohan Harkude 
722ea2e6eecSWilly Tu template <typename... UnpackTypes>
723ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res,
724ea2e6eecSWilly Tu                    std::string_view key, UnpackTypes&&... in)
725ea2e6eecSWilly Tu {
72676b038f2SEd Tanous     std::optional<nlohmann::json::object_t> jsonRequest =
72776b038f2SEd Tanous         readJsonPatchHelper(req, res);
728e01d0c36SEd Tanous     if (!jsonRequest)
729ea2e6eecSWilly Tu     {
73015ed6780SWilly Tu         return false;
73115ed6780SWilly Tu     }
73276b038f2SEd Tanous     if (jsonRequest->empty())
733b6164cbeSEd Tanous     {
734b6164cbeSEd Tanous         messages::emptyJSON(res);
735b6164cbeSEd Tanous         return false;
736b6164cbeSEd Tanous     }
73715ed6780SWilly Tu 
73876b038f2SEd Tanous     return readJsonObject(*jsonRequest, res, key,
739b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
74015ed6780SWilly Tu }
74115ed6780SWilly Tu 
74215ed6780SWilly Tu template <typename... UnpackTypes>
74315ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res,
744ea2e6eecSWilly Tu                     const char* key, UnpackTypes&&... in)
74515ed6780SWilly Tu {
74615ed6780SWilly Tu     nlohmann::json jsonRequest;
74715ed6780SWilly Tu     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
74815ed6780SWilly Tu     {
74962598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
75015ed6780SWilly Tu         return false;
75115ed6780SWilly Tu     }
752b6164cbeSEd Tanous     nlohmann::json::object_t* object =
753b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
754b6164cbeSEd Tanous     if (object == nullptr)
755b6164cbeSEd Tanous     {
756b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
757b6164cbeSEd Tanous         messages::emptyJSON(res);
758b6164cbeSEd Tanous         return false;
759b6164cbeSEd Tanous     }
760b6164cbeSEd Tanous     return readJsonObject(*object, res, key,
761b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
7620627a2c7SEd Tanous }
763185444b1SNan Zhou 
764185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the
765185444b1SNan Zhou // @odata.id key
7664e196b9aSEd Tanous inline int objectKeyCmp(std::string_view key, const nlohmann::json& a,
7674e196b9aSEd Tanous                         const nlohmann::json& b)
768185444b1SNan Zhou {
769185444b1SNan Zhou     using object_t = nlohmann::json::object_t;
770185444b1SNan Zhou     const object_t* aObj = a.get_ptr<const object_t*>();
771185444b1SNan Zhou     const object_t* bObj = b.get_ptr<const object_t*>();
772185444b1SNan Zhou 
773185444b1SNan Zhou     if (aObj == nullptr)
774185444b1SNan Zhou     {
775185444b1SNan Zhou         if (bObj == nullptr)
776185444b1SNan Zhou         {
777185444b1SNan Zhou             return 0;
778185444b1SNan Zhou         }
779185444b1SNan Zhou         return -1;
780185444b1SNan Zhou     }
781185444b1SNan Zhou     if (bObj == nullptr)
782185444b1SNan Zhou     {
783185444b1SNan Zhou         return 1;
784185444b1SNan Zhou     }
7854e196b9aSEd Tanous     object_t::const_iterator aIt = aObj->find(key);
7864e196b9aSEd Tanous     object_t::const_iterator bIt = bObj->find(key);
7874e196b9aSEd Tanous     // If either object doesn't have the key, they get "sorted" to the
7884e196b9aSEd Tanous     // beginning.
789185444b1SNan Zhou     if (aIt == aObj->end())
790185444b1SNan Zhou     {
791185444b1SNan Zhou         if (bIt == bObj->end())
792185444b1SNan Zhou         {
793185444b1SNan Zhou             return 0;
794185444b1SNan Zhou         }
795185444b1SNan Zhou         return -1;
796185444b1SNan Zhou     }
797185444b1SNan Zhou     if (bIt == bObj->end())
798185444b1SNan Zhou     {
799185444b1SNan Zhou         return 1;
800185444b1SNan Zhou     }
801185444b1SNan Zhou     const nlohmann::json::string_t* nameA =
802185444b1SNan Zhou         aIt->second.get_ptr<const std::string*>();
803185444b1SNan Zhou     const nlohmann::json::string_t* nameB =
804185444b1SNan Zhou         bIt->second.get_ptr<const std::string*>();
805185444b1SNan Zhou     // If either object doesn't have a string as the key, they get "sorted" to
8064e196b9aSEd Tanous     // the beginning.
807185444b1SNan Zhou     if (nameA == nullptr)
808185444b1SNan Zhou     {
809185444b1SNan Zhou         if (nameB == nullptr)
810185444b1SNan Zhou         {
811185444b1SNan Zhou             return 0;
812185444b1SNan Zhou         }
813185444b1SNan Zhou         return -1;
814185444b1SNan Zhou     }
815185444b1SNan Zhou     if (nameB == nullptr)
816185444b1SNan Zhou     {
817185444b1SNan Zhou         return 1;
818185444b1SNan Zhou     }
819e7bcf475SJayanth Othayoth     if (key != "@odata.id")
820e7bcf475SJayanth Othayoth     {
821e7bcf475SJayanth Othayoth         return alphanumComp(*nameA, *nameB);
822e7bcf475SJayanth Othayoth     }
823185444b1SNan Zhou 
824e7bcf475SJayanth Othayoth     boost::system::result<boost::urls::url_view> aUrl =
825e7bcf475SJayanth Othayoth         boost::urls::parse_relative_ref(*nameA);
826e7bcf475SJayanth Othayoth     boost::system::result<boost::urls::url_view> bUrl =
827e7bcf475SJayanth Othayoth         boost::urls::parse_relative_ref(*nameB);
828e7bcf475SJayanth Othayoth     if (!aUrl)
829185444b1SNan Zhou     {
830e7bcf475SJayanth Othayoth         if (!bUrl)
831185444b1SNan Zhou         {
832185444b1SNan Zhou             return 0;
833185444b1SNan Zhou         }
834185444b1SNan Zhou         return -1;
835185444b1SNan Zhou     }
836e7bcf475SJayanth Othayoth     if (!bUrl)
837e7bcf475SJayanth Othayoth     {
838e7bcf475SJayanth Othayoth         return 1;
839e7bcf475SJayanth Othayoth     }
840e7bcf475SJayanth Othayoth 
841e7bcf475SJayanth Othayoth     auto segmentsAIt = aUrl->segments().begin();
842e7bcf475SJayanth Othayoth     auto segmentsBIt = bUrl->segments().begin();
843e7bcf475SJayanth Othayoth 
844e7bcf475SJayanth Othayoth     while (true)
845e7bcf475SJayanth Othayoth     {
846e7bcf475SJayanth Othayoth         if (segmentsAIt == aUrl->segments().end())
847e7bcf475SJayanth Othayoth         {
848e7bcf475SJayanth Othayoth             if (segmentsBIt == bUrl->segments().end())
849e7bcf475SJayanth Othayoth             {
850e7bcf475SJayanth Othayoth                 return 0;
851e7bcf475SJayanth Othayoth             }
852e7bcf475SJayanth Othayoth             return -1;
853e7bcf475SJayanth Othayoth         }
854e7bcf475SJayanth Othayoth         if (segmentsBIt == bUrl->segments().end())
855185444b1SNan Zhou         {
856185444b1SNan Zhou             return 1;
857185444b1SNan Zhou         }
858185444b1SNan Zhou         int res = alphanumComp(*segmentsAIt, *segmentsBIt);
859185444b1SNan Zhou         if (res != 0)
860185444b1SNan Zhou         {
861185444b1SNan Zhou             return res;
862185444b1SNan Zhou         }
863185444b1SNan Zhou 
864185444b1SNan Zhou         segmentsAIt++;
865185444b1SNan Zhou         segmentsBIt++;
866185444b1SNan Zhou     }
867e7bcf475SJayanth Othayoth     return 0;
868185444b1SNan Zhou };
869185444b1SNan Zhou 
8704e196b9aSEd Tanous // kept for backward compatibility
8714e196b9aSEd Tanous inline int odataObjectCmp(const nlohmann::json& left,
8724e196b9aSEd Tanous                           const nlohmann::json& right)
8734e196b9aSEd Tanous {
8744e196b9aSEd Tanous     return objectKeyCmp("@odata.id", left, right);
8754e196b9aSEd Tanous }
8764e196b9aSEd Tanous 
877185444b1SNan Zhou struct ODataObjectLess
878185444b1SNan Zhou {
8794e196b9aSEd Tanous     std::string_view key;
8804e196b9aSEd Tanous 
8814e196b9aSEd Tanous     explicit ODataObjectLess(std::string_view keyIn) : key(keyIn) {}
8824e196b9aSEd Tanous 
883185444b1SNan Zhou     bool operator()(const nlohmann::json& left,
884185444b1SNan Zhou                     const nlohmann::json& right) const
885185444b1SNan Zhou     {
8864e196b9aSEd Tanous         return objectKeyCmp(key, left, right) < 0;
887185444b1SNan Zhou     }
888185444b1SNan Zhou };
889185444b1SNan Zhou 
890185444b1SNan Zhou // Sort the JSON array by |element[key]|.
891185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller
892185444b1SNan Zhou // those whose |element[key]| is string.
8934e196b9aSEd Tanous inline void sortJsonArrayByKey(nlohmann::json::array_t& array,
8944e196b9aSEd Tanous                                std::string_view key)
8954e196b9aSEd Tanous {
8964e196b9aSEd Tanous     std::ranges::sort(array, ODataObjectLess(key));
8974e196b9aSEd Tanous }
8984e196b9aSEd Tanous 
8994e196b9aSEd Tanous // Sort the JSON array by |element[key]|.
9004e196b9aSEd Tanous // Elements without |key| or type of |element[key]| is not string are smaller
9014e196b9aSEd Tanous // those whose |element[key]| is string.
902185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array)
903185444b1SNan Zhou {
9044e196b9aSEd Tanous     std::ranges::sort(array, ODataObjectLess("@odata.id"));
905185444b1SNan Zhou }
906185444b1SNan Zhou 
9078a7c4b47SNan Zhou // Returns the estimated size of the JSON value
9088a7c4b47SNan Zhou // The implementation walks through every key and every value, accumulates the
9098a7c4b47SNan Zhou //  total size of keys and values.
9108a7c4b47SNan Zhou // Ideally, we should use a custom allocator that nlohmann JSON supports.
9118a7c4b47SNan Zhou 
9128a7c4b47SNan Zhou // Assumption made:
9138a7c4b47SNan Zhou //  1. number: 8 characters
9148a7c4b47SNan Zhou //  2. boolean: 5 characters (False)
9158a7c4b47SNan Zhou //  3. string: len(str) + 2 characters (quote)
9168a7c4b47SNan Zhou //  4. bytes: len(bytes) characters
9178a7c4b47SNan Zhou //  5. null: 4 characters (null)
9188a7c4b47SNan Zhou uint64_t getEstimatedJsonSize(const nlohmann::json& root);
9198a7c4b47SNan Zhou 
92077dd8813SKowalski, Kamil } // namespace json_util
92177dd8813SKowalski, Kamil } // namespace redfish
922