xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision 4e196b9add89508953dcdaf32d7c22c7f17d26f6)
177dd8813SKowalski, Kamil /*
26be832e2SEd Tanous Copyright (c) 2018 Intel Corporation
36be832e2SEd Tanous 
46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License");
56be832e2SEd Tanous you may not use this file except in compliance with the License.
66be832e2SEd Tanous You may obtain a copy of the License at
76be832e2SEd Tanous 
86be832e2SEd Tanous       http://www.apache.org/licenses/LICENSE-2.0
96be832e2SEd Tanous 
106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software
116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS,
126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136be832e2SEd Tanous See the License for the specific language governing permissions and
146be832e2SEd Tanous limitations under the License.
1577dd8813SKowalski, Kamil */
1677dd8813SKowalski, Kamil #pragma once
179712f8acSEd Tanous 
18d5c80ad9SNan Zhou #include "error_messages.hpp"
198a7c4b47SNan Zhou #include "http_connection.hpp"
20d5c80ad9SNan Zhou #include "http_request.hpp"
21d5c80ad9SNan Zhou #include "http_response.hpp"
22185444b1SNan Zhou #include "human_sort.hpp"
23d5c80ad9SNan Zhou #include "logging.hpp"
24faf100f9SEd Tanous 
25faf100f9SEd Tanous #include <nlohmann/json.hpp>
260627a2c7SEd Tanous 
27185444b1SNan Zhou #include <algorithm>
28d5c80ad9SNan Zhou #include <array>
29d5c80ad9SNan Zhou #include <cmath>
30d5c80ad9SNan Zhou #include <cstddef>
31d5c80ad9SNan Zhou #include <cstdint>
32d5c80ad9SNan Zhou #include <limits>
33d5c80ad9SNan Zhou #include <map>
34d5c80ad9SNan Zhou #include <optional>
353544d2a7SEd Tanous #include <ranges>
36ea2e6eecSWilly Tu #include <span>
37d5c80ad9SNan Zhou #include <string>
38d5c80ad9SNan Zhou #include <string_view>
39d5c80ad9SNan Zhou #include <type_traits>
40d5c80ad9SNan Zhou #include <utility>
41d5c80ad9SNan Zhou #include <variant>
42d5c80ad9SNan Zhou #include <vector>
43d5c80ad9SNan Zhou 
441e75e1ddSNan Zhou // IWYU pragma: no_forward_declare crow::Request
451214b7e7SGunnar Mills 
461abe55efSEd Tanous namespace redfish
471abe55efSEd Tanous {
481abe55efSEd Tanous 
491abe55efSEd Tanous namespace json_util
501abe55efSEd Tanous {
5177dd8813SKowalski, Kamil 
5277dd8813SKowalski, Kamil /**
5377dd8813SKowalski, Kamil  * @brief Processes request to extract JSON from its body. If it fails, adds
5477dd8813SKowalski, Kamil  *       MalformedJSON message to response and ends it.
5577dd8813SKowalski, Kamil  *
5677dd8813SKowalski, Kamil  * @param[io]  res       Response object
5777dd8813SKowalski, Kamil  * @param[in]  req       Request object
5877dd8813SKowalski, Kamil  * @param[out] reqJson   JSON object extracted from request's body
5977dd8813SKowalski, Kamil  *
6077dd8813SKowalski, Kamil  * @return true if JSON is valid, false when JSON is invalid and response has
6177dd8813SKowalski, Kamil  *         been filled with message and ended.
6277dd8813SKowalski, Kamil  */
6355c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req,
6477dd8813SKowalski, Kamil                             nlohmann::json& reqJson);
659712f8acSEd Tanous namespace details
669712f8acSEd Tanous {
67771cfa0fSJason M. Bills 
681214b7e7SGunnar Mills template <typename Type>
692c70f800SEd Tanous struct IsOptional : std::false_type
701214b7e7SGunnar Mills {};
719712f8acSEd Tanous 
72771cfa0fSJason M. Bills template <typename Type>
732c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type
741214b7e7SGunnar Mills {};
759712f8acSEd Tanous 
76771cfa0fSJason M. Bills template <typename Type>
772c70f800SEd Tanous struct IsVector : std::false_type
781214b7e7SGunnar Mills {};
79b1556427SEd Tanous 
801214b7e7SGunnar Mills template <typename Type>
812c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type
821214b7e7SGunnar Mills {};
83b1556427SEd Tanous 
841214b7e7SGunnar Mills template <typename Type>
852c70f800SEd Tanous struct IsStdArray : std::false_type
861214b7e7SGunnar Mills {};
87318226c2SJames Feist 
88318226c2SJames Feist template <typename Type, std::size_t size>
892c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type
901214b7e7SGunnar Mills {};
91318226c2SJames Feist 
928099c517SEd Tanous template <typename Type>
938099c517SEd Tanous struct IsVariant : std::false_type
948099c517SEd Tanous {};
958099c517SEd Tanous 
968099c517SEd Tanous template <typename... Types>
978099c517SEd Tanous struct IsVariant<std::variant<Types...>> : std::true_type
988099c517SEd Tanous {};
998099c517SEd Tanous 
100471a5eb8SAppaRao Puli enum class UnpackErrorCode
101471a5eb8SAppaRao Puli {
102471a5eb8SAppaRao Puli     success,
103471a5eb8SAppaRao Puli     invalidType,
104471a5eb8SAppaRao Puli     outOfRange
105471a5eb8SAppaRao Puli };
106471a5eb8SAppaRao Puli 
107a6acbb31SJames Feist template <typename ToType, typename FromType>
108c09966bdSEd Tanous bool checkRange(const FromType& from [[maybe_unused]],
109c09966bdSEd Tanous                 std::string_view key [[maybe_unused]])
110a6acbb31SJames Feist {
111a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<ToType>)
112a6acbb31SJames Feist     {
113ee344e0fSEd Tanous         if (std::isnan(from))
114a6acbb31SJames Feist         {
11562598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was NAN", key);
116a6acbb31SJames Feist             return false;
117a6acbb31SJames Feist         }
118a6acbb31SJames Feist     }
119c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::max() <
120c09966bdSEd Tanous                   std::numeric_limits<FromType>::max())
121c09966bdSEd Tanous     {
122c09966bdSEd Tanous         if (from > std::numeric_limits<ToType>::max())
123c09966bdSEd Tanous         {
124c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key,
125c09966bdSEd Tanous                              std::numeric_limits<FromType>::max());
126c09966bdSEd Tanous             return false;
127c09966bdSEd Tanous         }
128c09966bdSEd Tanous     }
129c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::lowest() >
130c09966bdSEd Tanous                   std::numeric_limits<FromType>::lowest())
131c09966bdSEd Tanous     {
132c09966bdSEd Tanous         if (from < std::numeric_limits<ToType>::lowest())
133c09966bdSEd Tanous         {
134c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key,
135c09966bdSEd Tanous                              std::numeric_limits<FromType>::lowest());
136c09966bdSEd Tanous             return false;
137c09966bdSEd Tanous         }
138c09966bdSEd Tanous     }
139a6acbb31SJames Feist 
140a6acbb31SJames Feist     return true;
141a6acbb31SJames Feist }
142a6acbb31SJames Feist 
143771cfa0fSJason M. Bills template <typename Type>
144471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
1458099c517SEd Tanous                                          std::string_view key, Type& value);
1468099c517SEd Tanous 
1478099c517SEd Tanous template <std::size_t Index = 0, typename... Args>
1488099c517SEd Tanous UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key,
1498099c517SEd Tanous                                    std::variant<Args...>& v)
1508099c517SEd Tanous {
1518099c517SEd Tanous     if constexpr (Index < std::variant_size_v<std::variant<Args...>>)
1528099c517SEd Tanous     {
153ed4de7a8SEd Tanous         std::variant_alternative_t<Index, std::variant<Args...>> type{};
1548099c517SEd Tanous         UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type);
1558099c517SEd Tanous         if (unpack == UnpackErrorCode::success)
1568099c517SEd Tanous         {
1578099c517SEd Tanous             v = std::move(type);
1588099c517SEd Tanous             return unpack;
1598099c517SEd Tanous         }
1608099c517SEd Tanous 
1618099c517SEd Tanous         return unpackValueVariant<Index + 1, Args...>(j, key, v);
1628099c517SEd Tanous     }
1638099c517SEd Tanous     return UnpackErrorCode::invalidType;
1648099c517SEd Tanous }
1658099c517SEd Tanous 
1668099c517SEd Tanous template <typename Type>
1678099c517SEd Tanous UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
168ea2e6eecSWilly Tu                                          std::string_view key, Type& value)
169771cfa0fSJason M. Bills {
170471a5eb8SAppaRao Puli     UnpackErrorCode ret = UnpackErrorCode::success;
17141352c24SSantosh Puranik 
172a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<Type>)
173771cfa0fSJason M. Bills     {
174a6acbb31SJames Feist         double helper = 0;
175a6acbb31SJames Feist         double* jsonPtr = jsonValue.get_ptr<double*>();
176771cfa0fSJason M. Bills 
177771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
178771cfa0fSJason M. Bills         {
179a6acbb31SJames Feist             int64_t* intPtr = jsonValue.get_ptr<int64_t*>();
180a6acbb31SJames Feist             if (intPtr != nullptr)
181771cfa0fSJason M. Bills             {
182a6acbb31SJames Feist                 helper = static_cast<double>(*intPtr);
183a6acbb31SJames Feist                 jsonPtr = &helper;
184771cfa0fSJason M. Bills             }
185a6acbb31SJames Feist         }
1865eb2bef2SAppaRao Puli         if (jsonPtr == nullptr)
1875eb2bef2SAppaRao Puli         {
188471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
1895eb2bef2SAppaRao Puli         }
190cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
191771cfa0fSJason M. Bills         {
192471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
193771cfa0fSJason M. Bills         }
194771cfa0fSJason M. Bills         value = static_cast<Type>(*jsonPtr);
195771cfa0fSJason M. Bills     }
196a6acbb31SJames Feist 
197a6acbb31SJames Feist     else if constexpr (std::is_signed_v<Type>)
198a6acbb31SJames Feist     {
199a6acbb31SJames Feist         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
200271584abSEd Tanous         if (jsonPtr == nullptr)
201271584abSEd Tanous         {
202471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
203271584abSEd Tanous         }
204cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
205a6acbb31SJames Feist         {
206471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
207a6acbb31SJames Feist         }
208a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
209a6acbb31SJames Feist     }
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         {
217471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
218271584abSEd Tanous         }
219cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
220a6acbb31SJames Feist         {
221471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
222a6acbb31SJames Feist         }
223a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
224a6acbb31SJames Feist     }
225a6acbb31SJames Feist 
2260627a2c7SEd Tanous     else if constexpr (std::is_same_v<nlohmann::json, Type>)
2270627a2c7SEd Tanous     {
2280627a2c7SEd Tanous         value = std::move(jsonValue);
2290627a2c7SEd Tanous     }
2308099c517SEd Tanous     else if constexpr (std::is_same_v<std::nullptr_t, Type>)
2318099c517SEd Tanous     {
2328099c517SEd Tanous         if (!jsonValue.is_null())
2338099c517SEd Tanous         {
2348099c517SEd Tanous             return UnpackErrorCode::invalidType;
2358099c517SEd Tanous         }
2368099c517SEd Tanous     }
237ed4de7a8SEd Tanous     else if constexpr (IsVector<Type>::value)
238ed4de7a8SEd Tanous     {
239ed4de7a8SEd Tanous         nlohmann::json::object_t* obj =
240ed4de7a8SEd Tanous             jsonValue.get_ptr<nlohmann::json::object_t*>();
241ed4de7a8SEd Tanous         if (obj == nullptr)
242ed4de7a8SEd Tanous         {
243ed4de7a8SEd Tanous             return UnpackErrorCode::invalidType;
244ed4de7a8SEd Tanous         }
245ed4de7a8SEd Tanous 
246ed4de7a8SEd Tanous         for (const auto& val : *obj)
247ed4de7a8SEd Tanous         {
248ed4de7a8SEd Tanous             value.emplace_back();
249ed4de7a8SEd Tanous             ret = unpackValueWithErrorCode<typename Type::value_type>(
250ed4de7a8SEd Tanous                       val, key, value.back()) &&
251ed4de7a8SEd Tanous                   ret;
252ed4de7a8SEd Tanous         }
253ed4de7a8SEd Tanous     }
254471a5eb8SAppaRao Puli     else
255471a5eb8SAppaRao Puli     {
256471a5eb8SAppaRao Puli         using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
257471a5eb8SAppaRao Puli         JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
258471a5eb8SAppaRao Puli         if (jsonPtr == nullptr)
259471a5eb8SAppaRao Puli         {
26062598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key,
26162598e31SEd Tanous                              jsonValue.type_name());
262471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
263471a5eb8SAppaRao Puli         }
264471a5eb8SAppaRao Puli         value = std::move(*jsonPtr);
265471a5eb8SAppaRao Puli     }
266471a5eb8SAppaRao Puli     return ret;
267471a5eb8SAppaRao Puli }
268471a5eb8SAppaRao Puli 
269471a5eb8SAppaRao Puli template <typename Type>
270ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key,
271471a5eb8SAppaRao Puli                  crow::Response& res, Type& value)
272471a5eb8SAppaRao Puli {
273471a5eb8SAppaRao Puli     bool ret = true;
274471a5eb8SAppaRao Puli 
2752c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
276471a5eb8SAppaRao Puli     {
277471a5eb8SAppaRao Puli         value.emplace();
278471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
279471a5eb8SAppaRao Puli                                                      *value) &&
280471a5eb8SAppaRao Puli               ret;
281471a5eb8SAppaRao Puli     }
2822c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
283318226c2SJames Feist     {
2840bdda665SEd Tanous         nlohmann::json::array_t* arr =
2850bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
2860bdda665SEd Tanous         if (arr == nullptr)
287318226c2SJames Feist         {
2882e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
28941352c24SSantosh Puranik             return false;
290318226c2SJames Feist         }
291318226c2SJames Feist         if (jsonValue.size() != value.size())
292318226c2SJames Feist         {
2932e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
29441352c24SSantosh Puranik             return false;
295318226c2SJames Feist         }
296318226c2SJames Feist         size_t index = 0;
2970bdda665SEd Tanous         for (auto& val : *arr)
298318226c2SJames Feist         {
2990bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
30041352c24SSantosh Puranik                                                          value[index++]) &&
30141352c24SSantosh Puranik                   ret;
302318226c2SJames Feist         }
303318226c2SJames Feist     }
3042c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
305b1556427SEd Tanous     {
3060bdda665SEd Tanous         nlohmann::json::array_t* arr =
3070bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3080bdda665SEd Tanous         if (arr == nullptr)
309b1556427SEd Tanous         {
3102e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
31141352c24SSantosh Puranik             return false;
312b1556427SEd Tanous         }
313b1556427SEd Tanous 
3140bdda665SEd Tanous         for (auto& val : *arr)
315b1556427SEd Tanous         {
316b1556427SEd Tanous             value.emplace_back();
3170bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
31841352c24SSantosh Puranik                                                          value.back()) &&
31941352c24SSantosh Puranik                   ret;
320b1556427SEd Tanous         }
321b1556427SEd Tanous     }
3228099c517SEd Tanous     else if constexpr (IsVariant<Type>::value)
3238099c517SEd Tanous     {
3248099c517SEd Tanous         UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value);
3258099c517SEd Tanous         if (ec != UnpackErrorCode::success)
3268099c517SEd Tanous         {
3278099c517SEd Tanous             if (ec == UnpackErrorCode::invalidType)
3288099c517SEd Tanous             {
3298099c517SEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
3308099c517SEd Tanous             }
3318099c517SEd Tanous             else if (ec == UnpackErrorCode::outOfRange)
3328099c517SEd Tanous             {
3338099c517SEd Tanous                 messages::propertyValueNotInList(res, jsonValue, key);
3348099c517SEd Tanous             }
3358099c517SEd Tanous             return false;
3368099c517SEd Tanous         }
3378099c517SEd Tanous     }
338771cfa0fSJason M. Bills     else
339771cfa0fSJason M. Bills     {
340471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
341471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
342771cfa0fSJason M. Bills         {
343471a5eb8SAppaRao Puli             if (ec == UnpackErrorCode::invalidType)
344471a5eb8SAppaRao Puli             {
3452e8c4bdaSEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
346471a5eb8SAppaRao Puli             }
347471a5eb8SAppaRao Puli             else if (ec == UnpackErrorCode::outOfRange)
348471a5eb8SAppaRao Puli             {
349e2616cc5SEd Tanous                 messages::propertyValueNotInList(res, jsonValue, key);
350471a5eb8SAppaRao Puli             }
35141352c24SSantosh Puranik             return false;
352771cfa0fSJason M. Bills         }
353771cfa0fSJason M. Bills     }
354471a5eb8SAppaRao Puli 
355471a5eb8SAppaRao Puli     return ret;
356471a5eb8SAppaRao Puli }
357471a5eb8SAppaRao Puli 
358471a5eb8SAppaRao Puli template <typename Type>
359ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value)
360471a5eb8SAppaRao Puli {
361471a5eb8SAppaRao Puli     bool ret = true;
3622c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
363471a5eb8SAppaRao Puli     {
364471a5eb8SAppaRao Puli         value.emplace();
365471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) &&
366471a5eb8SAppaRao Puli               ret;
367471a5eb8SAppaRao Puli     }
3682c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
369471a5eb8SAppaRao Puli     {
3700bdda665SEd Tanous         nlohmann::json::array_t* arr =
3710bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3720bdda665SEd Tanous         if (arr == nullptr)
373471a5eb8SAppaRao Puli         {
374471a5eb8SAppaRao Puli             return false;
375471a5eb8SAppaRao Puli         }
376471a5eb8SAppaRao Puli         if (jsonValue.size() != value.size())
377471a5eb8SAppaRao Puli         {
378471a5eb8SAppaRao Puli             return false;
379471a5eb8SAppaRao Puli         }
380471a5eb8SAppaRao Puli         size_t index = 0;
3810bdda665SEd Tanous         for (const auto& val : *arr)
382471a5eb8SAppaRao Puli         {
3830bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
384471a5eb8SAppaRao Puli                                                          value[index++]) &&
385471a5eb8SAppaRao Puli                   ret;
386471a5eb8SAppaRao Puli         }
387471a5eb8SAppaRao Puli     }
3882c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
389471a5eb8SAppaRao Puli     {
3900bdda665SEd Tanous         nlohmann::json::array_t* arr =
3910bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3920bdda665SEd Tanous         if (arr == nullptr)
393471a5eb8SAppaRao Puli         {
394471a5eb8SAppaRao Puli             return false;
395471a5eb8SAppaRao Puli         }
396471a5eb8SAppaRao Puli 
3970bdda665SEd Tanous         for (const auto& val : *arr)
398471a5eb8SAppaRao Puli         {
399471a5eb8SAppaRao Puli             value.emplace_back();
4000bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
401471a5eb8SAppaRao Puli                                                          value.back()) &&
402471a5eb8SAppaRao Puli                   ret;
403471a5eb8SAppaRao Puli         }
404471a5eb8SAppaRao Puli     }
405471a5eb8SAppaRao Puli     else
406471a5eb8SAppaRao Puli     {
407471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
408471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
409471a5eb8SAppaRao Puli         {
410471a5eb8SAppaRao Puli             return false;
411471a5eb8SAppaRao Puli         }
412471a5eb8SAppaRao Puli     }
413471a5eb8SAppaRao Puli 
41441352c24SSantosh Puranik     return ret;
415771cfa0fSJason M. Bills }
4169712f8acSEd Tanous } // namespace details
4179712f8acSEd Tanous 
418ea2e6eecSWilly Tu // clang-format off
419ea2e6eecSWilly Tu using UnpackVariant = std::variant<
420ea2e6eecSWilly Tu     uint8_t*,
421ea2e6eecSWilly Tu     uint16_t*,
422ea2e6eecSWilly Tu     int16_t*,
423ea2e6eecSWilly Tu     uint32_t*,
424ea2e6eecSWilly Tu     int32_t*,
425ea2e6eecSWilly Tu     uint64_t*,
426ea2e6eecSWilly Tu     int64_t*,
427ea2e6eecSWilly Tu     bool*,
428ea2e6eecSWilly Tu     double*,
429ea2e6eecSWilly Tu     std::string*,
430b6164cbeSEd Tanous     nlohmann::json::object_t*,
4318099c517SEd Tanous     std::variant<std::string, std::nullptr_t>*,
4328099c517SEd Tanous     std::variant<uint8_t, std::nullptr_t>*,
4338099c517SEd Tanous     std::variant<int16_t, std::nullptr_t>*,
4348099c517SEd Tanous     std::variant<uint16_t, std::nullptr_t>*,
4358099c517SEd Tanous     std::variant<int32_t, std::nullptr_t>*,
4368099c517SEd Tanous     std::variant<uint32_t, std::nullptr_t>*,
4378099c517SEd Tanous     std::variant<int64_t, std::nullptr_t>*,
4388099c517SEd Tanous     std::variant<uint64_t, std::nullptr_t>*,
4398099c517SEd Tanous     std::variant<double, std::nullptr_t>*,
4408099c517SEd Tanous     std::variant<bool, std::nullptr_t>*,
441ea2e6eecSWilly Tu     std::vector<uint8_t>*,
442ea2e6eecSWilly Tu     std::vector<uint16_t>*,
443ea2e6eecSWilly Tu     std::vector<int16_t>*,
444ea2e6eecSWilly Tu     std::vector<uint32_t>*,
445ea2e6eecSWilly Tu     std::vector<int32_t>*,
446ea2e6eecSWilly Tu     std::vector<uint64_t>*,
447ea2e6eecSWilly Tu     std::vector<int64_t>*,
448ea2e6eecSWilly Tu     //std::vector<bool>*,
449ea2e6eecSWilly Tu     std::vector<double>*,
450ea2e6eecSWilly Tu     std::vector<std::string>*,
451b6164cbeSEd Tanous     std::vector<nlohmann::json::object_t>*,
452ea2e6eecSWilly Tu     std::optional<uint8_t>*,
453ea2e6eecSWilly Tu     std::optional<uint16_t>*,
454ea2e6eecSWilly Tu     std::optional<int16_t>*,
455ea2e6eecSWilly Tu     std::optional<uint32_t>*,
456ea2e6eecSWilly Tu     std::optional<int32_t>*,
457ea2e6eecSWilly Tu     std::optional<uint64_t>*,
458ea2e6eecSWilly Tu     std::optional<int64_t>*,
459ea2e6eecSWilly Tu     std::optional<bool>*,
460ea2e6eecSWilly Tu     std::optional<double>*,
461ea2e6eecSWilly Tu     std::optional<std::string>*,
462b6164cbeSEd Tanous     std::optional<nlohmann::json::object_t>*,
463ea2e6eecSWilly Tu     std::optional<std::vector<uint8_t>>*,
464ea2e6eecSWilly Tu     std::optional<std::vector<uint16_t>>*,
465ea2e6eecSWilly Tu     std::optional<std::vector<int16_t>>*,
466ea2e6eecSWilly Tu     std::optional<std::vector<uint32_t>>*,
467ea2e6eecSWilly Tu     std::optional<std::vector<int32_t>>*,
468ea2e6eecSWilly Tu     std::optional<std::vector<uint64_t>>*,
469ea2e6eecSWilly Tu     std::optional<std::vector<int64_t>>*,
470ea2e6eecSWilly Tu     //std::optional<std::vector<bool>>*,
471ea2e6eecSWilly Tu     std::optional<std::vector<double>>*,
472ea2e6eecSWilly Tu     std::optional<std::vector<std::string>>*,
4738099c517SEd Tanous     std::optional<std::vector<nlohmann::json::object_t>>*,
4748099c517SEd Tanous     std::optional<std::variant<std::string, std::nullptr_t>>*,
4758099c517SEd Tanous     std::optional<std::variant<uint8_t, std::nullptr_t>>*,
4768099c517SEd Tanous     std::optional<std::variant<int16_t, std::nullptr_t>>*,
4778099c517SEd Tanous     std::optional<std::variant<uint16_t, std::nullptr_t>>*,
4788099c517SEd Tanous     std::optional<std::variant<int32_t, std::nullptr_t>>*,
4798099c517SEd Tanous     std::optional<std::variant<uint32_t, std::nullptr_t>>*,
4808099c517SEd Tanous     std::optional<std::variant<int64_t, std::nullptr_t>>*,
4818099c517SEd Tanous     std::optional<std::variant<uint64_t, std::nullptr_t>>*,
4828099c517SEd Tanous     std::optional<std::variant<double, std::nullptr_t>>*,
4838099c517SEd Tanous     std::optional<std::variant<bool, std::nullptr_t>>*,
4848099c517SEd Tanous     std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*,
485ed4de7a8SEd Tanous     std::optional<std::vector<std::variant<std::string, nlohmann::json::object_t, std::nullptr_t>>>*,
486ed4de7a8SEd Tanous 
487ed4de7a8SEd Tanous     // Note, these types are kept for historical completeness, but should not be used,
488ed4de7a8SEd Tanous     // As they do not provide object type safety.  Instead, rely on nlohmann::json::object_t
489ed4de7a8SEd Tanous     // Will be removed Q2 2025
490ed4de7a8SEd Tanous     nlohmann::json*,
491ed4de7a8SEd Tanous     std::optional<std::vector<nlohmann::json>>*,
492ed4de7a8SEd Tanous     std::vector<nlohmann::json>*,
493ed4de7a8SEd Tanous     std::optional<nlohmann::json>*
494ea2e6eecSWilly Tu >;
495ea2e6eecSWilly Tu // clang-format on
496ea2e6eecSWilly Tu 
497ea2e6eecSWilly Tu struct PerUnpack
498ea2e6eecSWilly Tu {
499ea2e6eecSWilly Tu     std::string_view key;
500ea2e6eecSWilly Tu     UnpackVariant value;
501ea2e6eecSWilly Tu     bool complete = false;
502ea2e6eecSWilly Tu };
503ea2e6eecSWilly Tu 
504ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
505b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack);
506b6164cbeSEd Tanous 
507b6164cbeSEd Tanous inline bool readJsonHelperObject(nlohmann::json::object_t& obj,
508b6164cbeSEd Tanous                                  crow::Response& res,
509ea2e6eecSWilly Tu                                  std::span<PerUnpack> toUnpack)
5109712f8acSEd Tanous {
51141352c24SSantosh Puranik     bool result = true;
512b6164cbeSEd Tanous     for (auto& item : obj)
5139712f8acSEd Tanous     {
514ea2e6eecSWilly Tu         size_t unpackIndex = 0;
515ea2e6eecSWilly Tu         for (; unpackIndex < toUnpack.size(); unpackIndex++)
516ea2e6eecSWilly Tu         {
517ea2e6eecSWilly Tu             PerUnpack& unpackSpec = toUnpack[unpackIndex];
518ea2e6eecSWilly Tu             std::string_view key = unpackSpec.key;
519ea2e6eecSWilly Tu             size_t keysplitIndex = key.find('/');
520ea2e6eecSWilly Tu             std::string_view leftover;
521ea2e6eecSWilly Tu             if (keysplitIndex != std::string_view::npos)
522ea2e6eecSWilly Tu             {
523ea2e6eecSWilly Tu                 leftover = key.substr(keysplitIndex + 1);
524ea2e6eecSWilly Tu                 key = key.substr(0, keysplitIndex);
525ea2e6eecSWilly Tu             }
526ea2e6eecSWilly Tu 
527d91415c4SEd Tanous             if (key != item.first || unpackSpec.complete)
528ea2e6eecSWilly Tu             {
529ea2e6eecSWilly Tu                 continue;
530ea2e6eecSWilly Tu             }
531ea2e6eecSWilly Tu 
532ea2e6eecSWilly Tu             // Sublevel key
533ea2e6eecSWilly Tu             if (!leftover.empty())
534ea2e6eecSWilly Tu             {
535ea2e6eecSWilly Tu                 // Include the slash in the key so we can compare later
536ea2e6eecSWilly Tu                 key = unpackSpec.key.substr(0, keysplitIndex + 1);
537ea2e6eecSWilly Tu                 nlohmann::json j;
538d91415c4SEd Tanous                 result = details::unpackValue<nlohmann::json>(item.second, key,
539ea2e6eecSWilly Tu                                                               res, j) &&
54041352c24SSantosh Puranik                          result;
54155f79e6fSEd Tanous                 if (!result)
542ea2e6eecSWilly Tu                 {
543ea2e6eecSWilly Tu                     return result;
5449712f8acSEd Tanous                 }
5459712f8acSEd Tanous 
546ea2e6eecSWilly Tu                 std::vector<PerUnpack> nextLevel;
547ea2e6eecSWilly Tu                 for (PerUnpack& p : toUnpack)
548ea2e6eecSWilly Tu                 {
549ea2e6eecSWilly Tu                     if (!p.key.starts_with(key))
550ea2e6eecSWilly Tu                     {
551ea2e6eecSWilly Tu                         continue;
552ea2e6eecSWilly Tu                     }
553ea2e6eecSWilly Tu                     std::string_view thisLeftover = p.key.substr(key.size());
554ea2e6eecSWilly Tu                     nextLevel.push_back({thisLeftover, p.value, false});
555ea2e6eecSWilly Tu                     p.complete = true;
5569712f8acSEd Tanous                 }
55777dd8813SKowalski, Kamil 
558ea2e6eecSWilly Tu                 result = readJsonHelper(j, res, nextLevel) && result;
559ea2e6eecSWilly Tu                 break;
560ea2e6eecSWilly Tu             }
561ea2e6eecSWilly Tu 
562bd79bce8SPatrick Williams             result =
563bd79bce8SPatrick Williams                 std::visit(
5645ea927bbSEd Tanous                     [&item, &unpackSpec, &res](auto& val) {
565ea2e6eecSWilly Tu                         using ContainedT =
566ea2e6eecSWilly Tu                             std::remove_pointer_t<std::decay_t<decltype(val)>>;
567ea2e6eecSWilly Tu                         return details::unpackValue<ContainedT>(
568d91415c4SEd Tanous                             item.second, unpackSpec.key, res, *val);
569ea2e6eecSWilly Tu                     },
570ea2e6eecSWilly Tu                     unpackSpec.value) &&
571ea2e6eecSWilly Tu                 result;
572ea2e6eecSWilly Tu 
573ea2e6eecSWilly Tu             unpackSpec.complete = true;
574ea2e6eecSWilly Tu             break;
575ea2e6eecSWilly Tu         }
576ea2e6eecSWilly Tu 
577ea2e6eecSWilly Tu         if (unpackIndex == toUnpack.size())
578ea2e6eecSWilly Tu         {
579d91415c4SEd Tanous             messages::propertyUnknown(res, item.first);
580ea2e6eecSWilly Tu             result = false;
581ea2e6eecSWilly Tu         }
582ea2e6eecSWilly Tu     }
583ea2e6eecSWilly Tu 
584ea2e6eecSWilly Tu     for (PerUnpack& perUnpack : toUnpack)
585ea2e6eecSWilly Tu     {
58655f79e6fSEd Tanous         if (!perUnpack.complete)
587ea2e6eecSWilly Tu         {
588ea2e6eecSWilly Tu             bool isOptional = std::visit(
5895ea927bbSEd Tanous                 [](auto& val) {
590ea2e6eecSWilly Tu                     using ContainedType =
591ea2e6eecSWilly Tu                         std::remove_pointer_t<std::decay_t<decltype(val)>>;
592ea2e6eecSWilly Tu                     return details::IsOptional<ContainedType>::value;
593ea2e6eecSWilly Tu                 },
594ea2e6eecSWilly Tu                 perUnpack.value);
595ea2e6eecSWilly Tu             if (isOptional)
596ea2e6eecSWilly Tu             {
597ea2e6eecSWilly Tu                 continue;
598ea2e6eecSWilly Tu             }
599ea2e6eecSWilly Tu             messages::propertyMissing(res, perUnpack.key);
600ea2e6eecSWilly Tu             result = false;
601ea2e6eecSWilly Tu         }
602ea2e6eecSWilly Tu     }
603ea2e6eecSWilly Tu     return result;
604ea2e6eecSWilly Tu }
605ea2e6eecSWilly Tu 
606b6164cbeSEd Tanous inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
607b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack)
608b6164cbeSEd Tanous {
609b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
610b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
611b6164cbeSEd Tanous     if (obj == nullptr)
612b6164cbeSEd Tanous     {
613b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
614b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
615b6164cbeSEd Tanous         return false;
616b6164cbeSEd Tanous     }
617b6164cbeSEd Tanous     return readJsonHelperObject(*obj, res, toUnpack);
618b6164cbeSEd Tanous }
619b6164cbeSEd Tanous 
62089492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {}
621ea2e6eecSWilly Tu 
622ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
623ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key,
6245ea927bbSEd Tanous                  FirstType&& first, UnpackTypes&&... in)
625ea2e6eecSWilly Tu {
626ea2e6eecSWilly Tu     if (toPack.empty())
627ea2e6eecSWilly Tu     {
628ea2e6eecSWilly Tu         return;
629ea2e6eecSWilly Tu     }
630ea2e6eecSWilly Tu     toPack[0].key = key;
631ea2e6eecSWilly Tu     toPack[0].value = &first;
632ea2e6eecSWilly Tu     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
633ea2e6eecSWilly Tu     packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...);
634ea2e6eecSWilly Tu }
635ea2e6eecSWilly Tu 
636ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
637b6164cbeSEd Tanous bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res,
638b6164cbeSEd Tanous                     std::string_view key, FirstType&& first,
639b6164cbeSEd Tanous                     UnpackTypes&&... in)
640ea2e6eecSWilly Tu {
641ea2e6eecSWilly Tu     const std::size_t n = sizeof...(UnpackTypes) + 2;
642ea2e6eecSWilly Tu     std::array<PerUnpack, n / 2> toUnpack2;
6435ea927bbSEd Tanous     packVariant(toUnpack2, key, std::forward<FirstType>(first),
6445ea927bbSEd Tanous                 std::forward<UnpackTypes&&>(in)...);
645b6164cbeSEd Tanous     return readJsonHelperObject(jsonRequest, res, toUnpack2);
646ea2e6eecSWilly Tu }
647ea2e6eecSWilly Tu 
648b6164cbeSEd Tanous template <typename FirstType, typename... UnpackTypes>
649b6164cbeSEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res,
650b6164cbeSEd Tanous               std::string_view key, FirstType&& first, UnpackTypes&&... in)
651b6164cbeSEd Tanous {
652b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
653b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
654b6164cbeSEd Tanous     if (obj == nullptr)
655b6164cbeSEd Tanous     {
656b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
657b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
658b6164cbeSEd Tanous         return false;
659b6164cbeSEd Tanous     }
6605be2b14aSEd Tanous     return readJsonObject(*obj, res, key, std::forward<FirstType>(first),
6615be2b14aSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
662b6164cbeSEd Tanous }
663b6164cbeSEd Tanous 
664b6164cbeSEd Tanous inline std::optional<nlohmann::json::object_t>
665ea2e6eecSWilly Tu     readJsonPatchHelper(const crow::Request& req, crow::Response& res)
6660627a2c7SEd Tanous {
6670627a2c7SEd Tanous     nlohmann::json jsonRequest;
6680627a2c7SEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
6690627a2c7SEd Tanous     {
67062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
671ea2e6eecSWilly Tu         return std::nullopt;
6720627a2c7SEd Tanous     }
673357bb8f8SEd Tanous     nlohmann::json::object_t* object =
674357bb8f8SEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
675357bb8f8SEd Tanous     if (object == nullptr || object->empty())
67615ed6780SWilly Tu     {
67762598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
67815ed6780SWilly Tu         messages::emptyJSON(res);
679ea2e6eecSWilly Tu         return std::nullopt;
680ea2e6eecSWilly Tu     }
681357bb8f8SEd Tanous     std::erase_if(*object,
682357bb8f8SEd Tanous                   [](const std::pair<std::string, nlohmann::json>& item) {
683357bb8f8SEd Tanous                       return item.first.starts_with("@odata.");
684357bb8f8SEd Tanous                   });
685357bb8f8SEd Tanous     if (object->empty())
686357bb8f8SEd Tanous     {
687357bb8f8SEd Tanous         //  If the update request only contains OData annotations, the service
688357bb8f8SEd Tanous         //  should return the HTTP 400 Bad Request status code with the
689357bb8f8SEd Tanous         //  NoOperation message from the Base Message Registry, ...
690357bb8f8SEd Tanous         messages::noOperation(res);
691357bb8f8SEd Tanous         return std::nullopt;
692357bb8f8SEd Tanous     }
693357bb8f8SEd Tanous 
694b6164cbeSEd Tanous     return {std::move(*object)};
695ea2e6eecSWilly Tu }
696ea2e6eecSWilly Tu 
697ea2e6eecSWilly Tu template <typename... UnpackTypes>
698ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res,
699ea2e6eecSWilly Tu                    std::string_view key, UnpackTypes&&... in)
700ea2e6eecSWilly Tu {
70176b038f2SEd Tanous     std::optional<nlohmann::json::object_t> jsonRequest =
70276b038f2SEd Tanous         readJsonPatchHelper(req, res);
703e01d0c36SEd Tanous     if (!jsonRequest)
704ea2e6eecSWilly Tu     {
70515ed6780SWilly Tu         return false;
70615ed6780SWilly Tu     }
70776b038f2SEd Tanous     if (jsonRequest->empty())
708b6164cbeSEd Tanous     {
709b6164cbeSEd Tanous         messages::emptyJSON(res);
710b6164cbeSEd Tanous         return false;
711b6164cbeSEd Tanous     }
71215ed6780SWilly Tu 
71376b038f2SEd Tanous     return readJsonObject(*jsonRequest, res, key,
714b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
71515ed6780SWilly Tu }
71615ed6780SWilly Tu 
71715ed6780SWilly Tu template <typename... UnpackTypes>
71815ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res,
719ea2e6eecSWilly Tu                     const char* key, UnpackTypes&&... in)
72015ed6780SWilly Tu {
72115ed6780SWilly Tu     nlohmann::json jsonRequest;
72215ed6780SWilly Tu     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
72315ed6780SWilly Tu     {
72462598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
72515ed6780SWilly Tu         return false;
72615ed6780SWilly Tu     }
727b6164cbeSEd Tanous     nlohmann::json::object_t* object =
728b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
729b6164cbeSEd Tanous     if (object == nullptr)
730b6164cbeSEd Tanous     {
731b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
732b6164cbeSEd Tanous         messages::emptyJSON(res);
733b6164cbeSEd Tanous         return false;
734b6164cbeSEd Tanous     }
735b6164cbeSEd Tanous     return readJsonObject(*object, res, key,
736b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
7370627a2c7SEd Tanous }
738185444b1SNan Zhou 
739185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the
740185444b1SNan Zhou // @odata.id key
741*4e196b9aSEd Tanous inline int objectKeyCmp(std::string_view key, const nlohmann::json& a,
742*4e196b9aSEd Tanous                         const nlohmann::json& b)
743185444b1SNan Zhou {
744185444b1SNan Zhou     using object_t = nlohmann::json::object_t;
745185444b1SNan Zhou     const object_t* aObj = a.get_ptr<const object_t*>();
746185444b1SNan Zhou     const object_t* bObj = b.get_ptr<const object_t*>();
747185444b1SNan Zhou 
748185444b1SNan Zhou     if (aObj == nullptr)
749185444b1SNan Zhou     {
750185444b1SNan Zhou         if (bObj == nullptr)
751185444b1SNan Zhou         {
752185444b1SNan Zhou             return 0;
753185444b1SNan Zhou         }
754185444b1SNan Zhou         return -1;
755185444b1SNan Zhou     }
756185444b1SNan Zhou     if (bObj == nullptr)
757185444b1SNan Zhou     {
758185444b1SNan Zhou         return 1;
759185444b1SNan Zhou     }
760*4e196b9aSEd Tanous     object_t::const_iterator aIt = aObj->find(key);
761*4e196b9aSEd Tanous     object_t::const_iterator bIt = bObj->find(key);
762*4e196b9aSEd Tanous     // If either object doesn't have the key, they get "sorted" to the
763*4e196b9aSEd Tanous     // beginning.
764185444b1SNan Zhou     if (aIt == aObj->end())
765185444b1SNan Zhou     {
766185444b1SNan Zhou         if (bIt == bObj->end())
767185444b1SNan Zhou         {
768185444b1SNan Zhou             return 0;
769185444b1SNan Zhou         }
770185444b1SNan Zhou         return -1;
771185444b1SNan Zhou     }
772185444b1SNan Zhou     if (bIt == bObj->end())
773185444b1SNan Zhou     {
774185444b1SNan Zhou         return 1;
775185444b1SNan Zhou     }
776185444b1SNan Zhou     const nlohmann::json::string_t* nameA =
777185444b1SNan Zhou         aIt->second.get_ptr<const std::string*>();
778185444b1SNan Zhou     const nlohmann::json::string_t* nameB =
779185444b1SNan Zhou         bIt->second.get_ptr<const std::string*>();
780185444b1SNan Zhou     // If either object doesn't have a string as the key, they get "sorted" to
781*4e196b9aSEd Tanous     // the beginning.
782185444b1SNan Zhou     if (nameA == nullptr)
783185444b1SNan Zhou     {
784185444b1SNan Zhou         if (nameB == nullptr)
785185444b1SNan Zhou         {
786185444b1SNan Zhou             return 0;
787185444b1SNan Zhou         }
788185444b1SNan Zhou         return -1;
789185444b1SNan Zhou     }
790185444b1SNan Zhou     if (nameB == nullptr)
791185444b1SNan Zhou     {
792185444b1SNan Zhou         return 1;
793185444b1SNan Zhou     }
794185444b1SNan Zhou     boost::urls::url_view aUrl(*nameA);
795185444b1SNan Zhou     boost::urls::url_view bUrl(*nameB);
796185444b1SNan Zhou     auto segmentsAIt = aUrl.segments().begin();
797185444b1SNan Zhou     auto segmentsBIt = bUrl.segments().begin();
798185444b1SNan Zhou 
799185444b1SNan Zhou     while (true)
800185444b1SNan Zhou     {
801185444b1SNan Zhou         if (segmentsAIt == aUrl.segments().end())
802185444b1SNan Zhou         {
803185444b1SNan Zhou             if (segmentsBIt == bUrl.segments().end())
804185444b1SNan Zhou             {
805185444b1SNan Zhou                 return 0;
806185444b1SNan Zhou             }
807185444b1SNan Zhou             return -1;
808185444b1SNan Zhou         }
809185444b1SNan Zhou         if (segmentsBIt == bUrl.segments().end())
810185444b1SNan Zhou         {
811185444b1SNan Zhou             return 1;
812185444b1SNan Zhou         }
813185444b1SNan Zhou         int res = alphanumComp(*segmentsAIt, *segmentsBIt);
814185444b1SNan Zhou         if (res != 0)
815185444b1SNan Zhou         {
816185444b1SNan Zhou             return res;
817185444b1SNan Zhou         }
818185444b1SNan Zhou 
819185444b1SNan Zhou         segmentsAIt++;
820185444b1SNan Zhou         segmentsBIt++;
821185444b1SNan Zhou     }
822185444b1SNan Zhou };
823185444b1SNan Zhou 
824*4e196b9aSEd Tanous // kept for backward compatibility
825*4e196b9aSEd Tanous inline int odataObjectCmp(const nlohmann::json& left,
826*4e196b9aSEd Tanous                           const nlohmann::json& right)
827*4e196b9aSEd Tanous {
828*4e196b9aSEd Tanous     return objectKeyCmp("@odata.id", left, right);
829*4e196b9aSEd Tanous }
830*4e196b9aSEd Tanous 
831185444b1SNan Zhou struct ODataObjectLess
832185444b1SNan Zhou {
833*4e196b9aSEd Tanous     std::string_view key;
834*4e196b9aSEd Tanous 
835*4e196b9aSEd Tanous     explicit ODataObjectLess(std::string_view keyIn) : key(keyIn) {}
836*4e196b9aSEd Tanous 
837185444b1SNan Zhou     bool operator()(const nlohmann::json& left,
838185444b1SNan Zhou                     const nlohmann::json& right) const
839185444b1SNan Zhou     {
840*4e196b9aSEd Tanous         return objectKeyCmp(key, left, right) < 0;
841185444b1SNan Zhou     }
842185444b1SNan Zhou };
843185444b1SNan Zhou 
844185444b1SNan Zhou // Sort the JSON array by |element[key]|.
845185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller
846185444b1SNan Zhou // those whose |element[key]| is string.
847*4e196b9aSEd Tanous inline void sortJsonArrayByKey(nlohmann::json::array_t& array,
848*4e196b9aSEd Tanous                                std::string_view key)
849*4e196b9aSEd Tanous {
850*4e196b9aSEd Tanous     std::ranges::sort(array, ODataObjectLess(key));
851*4e196b9aSEd Tanous }
852*4e196b9aSEd Tanous 
853*4e196b9aSEd Tanous // Sort the JSON array by |element[key]|.
854*4e196b9aSEd Tanous // Elements without |key| or type of |element[key]| is not string are smaller
855*4e196b9aSEd Tanous // those whose |element[key]| is string.
856185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array)
857185444b1SNan Zhou {
858*4e196b9aSEd Tanous     std::ranges::sort(array, ODataObjectLess("@odata.id"));
859185444b1SNan Zhou }
860185444b1SNan Zhou 
8618a7c4b47SNan Zhou // Returns the estimated size of the JSON value
8628a7c4b47SNan Zhou // The implementation walks through every key and every value, accumulates the
8638a7c4b47SNan Zhou //  total size of keys and values.
8648a7c4b47SNan Zhou // Ideally, we should use a custom allocator that nlohmann JSON supports.
8658a7c4b47SNan Zhou 
8668a7c4b47SNan Zhou // Assumption made:
8678a7c4b47SNan Zhou //  1. number: 8 characters
8688a7c4b47SNan Zhou //  2. boolean: 5 characters (False)
8698a7c4b47SNan Zhou //  3. string: len(str) + 2 characters (quote)
8708a7c4b47SNan Zhou //  4. bytes: len(bytes) characters
8718a7c4b47SNan Zhou //  5. null: 4 characters (null)
8728a7c4b47SNan Zhou uint64_t getEstimatedJsonSize(const nlohmann::json& root);
8738a7c4b47SNan Zhou 
87477dd8813SKowalski, Kamil } // namespace json_util
87577dd8813SKowalski, Kamil } // namespace redfish
876