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