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