177dd8813SKowalski, Kamil /* 277dd8813SKowalski, Kamil // Copyright (c) 2018 Intel Corporation 377dd8813SKowalski, Kamil // 477dd8813SKowalski, Kamil // Licensed under the Apache License, Version 2.0 (the "License"); 577dd8813SKowalski, Kamil // you may not use this file except in compliance with the License. 677dd8813SKowalski, Kamil // You may obtain a copy of the License at 777dd8813SKowalski, Kamil // 877dd8813SKowalski, Kamil // http://www.apache.org/licenses/LICENSE-2.0 977dd8813SKowalski, Kamil // 1077dd8813SKowalski, Kamil // Unless required by applicable law or agreed to in writing, software 1177dd8813SKowalski, Kamil // distributed under the License is distributed on an "AS IS" BASIS, 1277dd8813SKowalski, Kamil // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1377dd8813SKowalski, Kamil // See the License for the specific language governing permissions and 1477dd8813SKowalski, Kamil // 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 44d5c80ad9SNan Zhou // IWYU pragma: no_include <stdint.h> 451e75e1ddSNan Zhou // IWYU pragma: no_forward_declare crow::Request 461214b7e7SGunnar Mills 471abe55efSEd Tanous namespace redfish 481abe55efSEd Tanous { 491abe55efSEd Tanous 501abe55efSEd Tanous namespace json_util 511abe55efSEd Tanous { 5277dd8813SKowalski, Kamil 5377dd8813SKowalski, Kamil /** 5477dd8813SKowalski, Kamil * @brief Processes request to extract JSON from its body. If it fails, adds 5577dd8813SKowalski, Kamil * MalformedJSON message to response and ends it. 5677dd8813SKowalski, Kamil * 5777dd8813SKowalski, Kamil * @param[io] res Response object 5877dd8813SKowalski, Kamil * @param[in] req Request object 5977dd8813SKowalski, Kamil * @param[out] reqJson JSON object extracted from request's body 6077dd8813SKowalski, Kamil * 6177dd8813SKowalski, Kamil * @return true if JSON is valid, false when JSON is invalid and response has 6277dd8813SKowalski, Kamil * been filled with message and ended. 6377dd8813SKowalski, Kamil */ 6455c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 6577dd8813SKowalski, Kamil nlohmann::json& reqJson); 669712f8acSEd Tanous namespace details 679712f8acSEd Tanous { 68771cfa0fSJason M. Bills 691214b7e7SGunnar Mills template <typename Type> 702c70f800SEd Tanous struct IsOptional : std::false_type 711214b7e7SGunnar Mills {}; 729712f8acSEd Tanous 73771cfa0fSJason M. Bills template <typename Type> 742c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type 751214b7e7SGunnar Mills {}; 769712f8acSEd Tanous 77771cfa0fSJason M. Bills template <typename Type> 782c70f800SEd Tanous struct IsVector : std::false_type 791214b7e7SGunnar Mills {}; 80b1556427SEd Tanous 811214b7e7SGunnar Mills template <typename Type> 822c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type 831214b7e7SGunnar Mills {}; 84b1556427SEd Tanous 851214b7e7SGunnar Mills template <typename Type> 862c70f800SEd Tanous struct IsStdArray : std::false_type 871214b7e7SGunnar Mills {}; 88318226c2SJames Feist 89318226c2SJames Feist template <typename Type, std::size_t size> 902c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type 911214b7e7SGunnar Mills {}; 92318226c2SJames Feist 938099c517SEd Tanous template <typename Type> 948099c517SEd Tanous struct IsVariant : std::false_type 958099c517SEd Tanous {}; 968099c517SEd Tanous 978099c517SEd Tanous template <typename... Types> 988099c517SEd Tanous struct IsVariant<std::variant<Types...>> : std::true_type 998099c517SEd Tanous {}; 1008099c517SEd Tanous 101471a5eb8SAppaRao Puli enum class UnpackErrorCode 102471a5eb8SAppaRao Puli { 103471a5eb8SAppaRao Puli success, 104471a5eb8SAppaRao Puli invalidType, 105471a5eb8SAppaRao Puli outOfRange 106471a5eb8SAppaRao Puli }; 107471a5eb8SAppaRao Puli 108a6acbb31SJames Feist template <typename ToType, typename FromType> 109ea2e6eecSWilly Tu bool checkRange(const FromType& from, std::string_view key) 110a6acbb31SJames Feist { 111ee344e0fSEd Tanous if (from > std::numeric_limits<ToType>::max()) 112a6acbb31SJames Feist { 11362598e31SEd Tanous BMCWEB_LOG_DEBUG("Value for key {} was greater than max: {}", key, 11462598e31SEd Tanous __PRETTY_FUNCTION__); 115a6acbb31SJames Feist return false; 116a6acbb31SJames Feist } 117ee344e0fSEd Tanous if (from < std::numeric_limits<ToType>::lowest()) 118a6acbb31SJames Feist { 11962598e31SEd Tanous BMCWEB_LOG_DEBUG("Value for key {} was less than min: {}", key, 12062598e31SEd Tanous __PRETTY_FUNCTION__); 121a6acbb31SJames Feist return false; 122a6acbb31SJames Feist } 123a6acbb31SJames Feist if constexpr (std::is_floating_point_v<ToType>) 124a6acbb31SJames Feist { 125ee344e0fSEd Tanous if (std::isnan(from)) 126a6acbb31SJames Feist { 12762598e31SEd Tanous BMCWEB_LOG_DEBUG("Value for key {} was NAN", key); 128a6acbb31SJames Feist return false; 129a6acbb31SJames Feist } 130a6acbb31SJames Feist } 131a6acbb31SJames Feist 132a6acbb31SJames Feist return true; 133a6acbb31SJames Feist } 134a6acbb31SJames Feist 135771cfa0fSJason M. Bills template <typename Type> 136471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 1378099c517SEd Tanous std::string_view key, Type& value); 1388099c517SEd Tanous 1398099c517SEd Tanous template <std::size_t Index = 0, typename... Args> 1408099c517SEd Tanous UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key, 1418099c517SEd Tanous std::variant<Args...>& v) 1428099c517SEd Tanous { 1438099c517SEd Tanous if constexpr (Index < std::variant_size_v<std::variant<Args...>>) 1448099c517SEd Tanous { 1458099c517SEd Tanous std::variant_alternative_t<Index, std::variant<Args...>> type; 1468099c517SEd Tanous UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type); 1478099c517SEd Tanous if (unpack == UnpackErrorCode::success) 1488099c517SEd Tanous { 1498099c517SEd Tanous v = std::move(type); 1508099c517SEd Tanous return unpack; 1518099c517SEd Tanous } 1528099c517SEd Tanous 1538099c517SEd Tanous return unpackValueVariant<Index + 1, Args...>(j, key, v); 1548099c517SEd Tanous } 1558099c517SEd Tanous return UnpackErrorCode::invalidType; 1568099c517SEd Tanous } 1578099c517SEd Tanous 1588099c517SEd Tanous template <typename Type> 1598099c517SEd Tanous UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 160ea2e6eecSWilly Tu std::string_view key, Type& value) 161771cfa0fSJason M. Bills { 162471a5eb8SAppaRao Puli UnpackErrorCode ret = UnpackErrorCode::success; 16341352c24SSantosh Puranik 164a6acbb31SJames Feist if constexpr (std::is_floating_point_v<Type>) 165771cfa0fSJason M. Bills { 166a6acbb31SJames Feist double helper = 0; 167a6acbb31SJames Feist double* jsonPtr = jsonValue.get_ptr<double*>(); 168771cfa0fSJason M. Bills 169771cfa0fSJason M. Bills if (jsonPtr == nullptr) 170771cfa0fSJason M. Bills { 171a6acbb31SJames Feist int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 172a6acbb31SJames Feist if (intPtr != nullptr) 173771cfa0fSJason M. Bills { 174a6acbb31SJames Feist helper = static_cast<double>(*intPtr); 175a6acbb31SJames Feist jsonPtr = &helper; 176771cfa0fSJason M. Bills } 177a6acbb31SJames Feist } 1785eb2bef2SAppaRao Puli if (jsonPtr == nullptr) 1795eb2bef2SAppaRao Puli { 180471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 1815eb2bef2SAppaRao Puli } 182cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 183771cfa0fSJason M. Bills { 184471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 185771cfa0fSJason M. Bills } 186771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 187771cfa0fSJason M. Bills } 188a6acbb31SJames Feist 189a6acbb31SJames Feist else if constexpr (std::is_signed_v<Type>) 190a6acbb31SJames Feist { 191a6acbb31SJames Feist int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 192271584abSEd Tanous if (jsonPtr == nullptr) 193271584abSEd Tanous { 194471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 195271584abSEd Tanous } 196cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 197a6acbb31SJames Feist { 198471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 199a6acbb31SJames Feist } 200a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 201a6acbb31SJames Feist } 202a6acbb31SJames Feist 2038102ddbaSAppaRao Puli else if constexpr ((std::is_unsigned_v<Type>)&&( 2048102ddbaSAppaRao Puli !std::is_same_v<bool, Type>)) 205a6acbb31SJames Feist { 206a6acbb31SJames Feist uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 207271584abSEd Tanous if (jsonPtr == nullptr) 208271584abSEd Tanous { 209471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 210271584abSEd Tanous } 211cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 212a6acbb31SJames Feist { 213471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 214a6acbb31SJames Feist } 215a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 216a6acbb31SJames Feist } 217a6acbb31SJames Feist 2180627a2c7SEd Tanous else if constexpr (std::is_same_v<nlohmann::json, Type>) 2190627a2c7SEd Tanous { 2200627a2c7SEd Tanous value = std::move(jsonValue); 2210627a2c7SEd Tanous } 2228099c517SEd Tanous else if constexpr (std::is_same_v<std::nullptr_t, Type>) 2238099c517SEd Tanous { 2248099c517SEd Tanous if (!jsonValue.is_null()) 2258099c517SEd Tanous { 2268099c517SEd Tanous return UnpackErrorCode::invalidType; 2278099c517SEd Tanous } 2288099c517SEd Tanous } 229471a5eb8SAppaRao Puli else 230471a5eb8SAppaRao Puli { 231471a5eb8SAppaRao Puli using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 232471a5eb8SAppaRao Puli JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 233471a5eb8SAppaRao Puli if (jsonPtr == nullptr) 234471a5eb8SAppaRao Puli { 23562598e31SEd Tanous BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key, 23662598e31SEd Tanous jsonValue.type_name()); 237471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 238471a5eb8SAppaRao Puli } 239471a5eb8SAppaRao Puli value = std::move(*jsonPtr); 240471a5eb8SAppaRao Puli } 241471a5eb8SAppaRao Puli return ret; 242471a5eb8SAppaRao Puli } 243471a5eb8SAppaRao Puli 244471a5eb8SAppaRao Puli template <typename Type> 245ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, 246471a5eb8SAppaRao Puli crow::Response& res, Type& value) 247471a5eb8SAppaRao Puli { 248471a5eb8SAppaRao Puli bool ret = true; 249471a5eb8SAppaRao Puli 2502c70f800SEd Tanous if constexpr (IsOptional<Type>::value) 251471a5eb8SAppaRao Puli { 252471a5eb8SAppaRao Puli value.emplace(); 253471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 254471a5eb8SAppaRao Puli *value) && 255471a5eb8SAppaRao Puli ret; 256471a5eb8SAppaRao Puli } 2572c70f800SEd Tanous else if constexpr (IsStdArray<Type>::value) 258318226c2SJames Feist { 259318226c2SJames Feist if (!jsonValue.is_array()) 260318226c2SJames Feist { 2612e8c4bdaSEd Tanous messages::propertyValueTypeError(res, res.jsonValue, key); 26241352c24SSantosh Puranik return false; 263318226c2SJames Feist } 264318226c2SJames Feist if (jsonValue.size() != value.size()) 265318226c2SJames Feist { 2662e8c4bdaSEd Tanous messages::propertyValueTypeError(res, res.jsonValue, key); 26741352c24SSantosh Puranik return false; 268318226c2SJames Feist } 269318226c2SJames Feist size_t index = 0; 270318226c2SJames Feist for (const auto& val : jsonValue.items()) 271318226c2SJames Feist { 27241352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 27341352c24SSantosh Puranik value[index++]) && 27441352c24SSantosh Puranik ret; 275318226c2SJames Feist } 276318226c2SJames Feist } 2772c70f800SEd Tanous else if constexpr (IsVector<Type>::value) 278b1556427SEd Tanous { 279b1556427SEd Tanous if (!jsonValue.is_array()) 280b1556427SEd Tanous { 2812e8c4bdaSEd Tanous messages::propertyValueTypeError(res, res.jsonValue, key); 28241352c24SSantosh Puranik return false; 283b1556427SEd Tanous } 284b1556427SEd Tanous 285b1556427SEd Tanous for (const auto& val : jsonValue.items()) 286b1556427SEd Tanous { 287b1556427SEd Tanous value.emplace_back(); 28841352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 28941352c24SSantosh Puranik value.back()) && 29041352c24SSantosh Puranik ret; 291b1556427SEd Tanous } 292b1556427SEd Tanous } 2938099c517SEd Tanous else if constexpr (IsVariant<Type>::value) 2948099c517SEd Tanous { 2958099c517SEd Tanous UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value); 2968099c517SEd Tanous if (ec != UnpackErrorCode::success) 2978099c517SEd Tanous { 2988099c517SEd Tanous if (ec == UnpackErrorCode::invalidType) 2998099c517SEd Tanous { 3008099c517SEd Tanous messages::propertyValueTypeError(res, jsonValue, key); 3018099c517SEd Tanous } 3028099c517SEd Tanous else if (ec == UnpackErrorCode::outOfRange) 3038099c517SEd Tanous { 3048099c517SEd Tanous messages::propertyValueNotInList(res, jsonValue, key); 3058099c517SEd Tanous } 3068099c517SEd Tanous return false; 3078099c517SEd Tanous } 3088099c517SEd Tanous } 309771cfa0fSJason M. Bills else 310771cfa0fSJason M. Bills { 311471a5eb8SAppaRao Puli UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 312471a5eb8SAppaRao Puli if (ec != UnpackErrorCode::success) 313771cfa0fSJason M. Bills { 314471a5eb8SAppaRao Puli if (ec == UnpackErrorCode::invalidType) 315471a5eb8SAppaRao Puli { 3162e8c4bdaSEd Tanous messages::propertyValueTypeError(res, jsonValue, key); 317471a5eb8SAppaRao Puli } 318471a5eb8SAppaRao Puli else if (ec == UnpackErrorCode::outOfRange) 319471a5eb8SAppaRao Puli { 320e2616cc5SEd Tanous messages::propertyValueNotInList(res, jsonValue, key); 321471a5eb8SAppaRao Puli } 32241352c24SSantosh Puranik return false; 323771cfa0fSJason M. Bills } 324771cfa0fSJason M. Bills } 325471a5eb8SAppaRao Puli 326471a5eb8SAppaRao Puli return ret; 327471a5eb8SAppaRao Puli } 328471a5eb8SAppaRao Puli 329471a5eb8SAppaRao Puli template <typename Type> 330ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value) 331471a5eb8SAppaRao Puli { 332471a5eb8SAppaRao Puli bool ret = true; 3332c70f800SEd Tanous if constexpr (IsOptional<Type>::value) 334471a5eb8SAppaRao Puli { 335471a5eb8SAppaRao Puli value.emplace(); 336471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) && 337471a5eb8SAppaRao Puli ret; 338471a5eb8SAppaRao Puli } 3392c70f800SEd Tanous else if constexpr (IsStdArray<Type>::value) 340471a5eb8SAppaRao Puli { 341471a5eb8SAppaRao Puli if (!jsonValue.is_array()) 342471a5eb8SAppaRao Puli { 343471a5eb8SAppaRao Puli return false; 344471a5eb8SAppaRao Puli } 345471a5eb8SAppaRao Puli if (jsonValue.size() != value.size()) 346471a5eb8SAppaRao Puli { 347471a5eb8SAppaRao Puli return false; 348471a5eb8SAppaRao Puli } 349471a5eb8SAppaRao Puli size_t index = 0; 350471a5eb8SAppaRao Puli for (const auto& val : jsonValue.items()) 351471a5eb8SAppaRao Puli { 352471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(val.value(), key, 353471a5eb8SAppaRao Puli value[index++]) && 354471a5eb8SAppaRao Puli ret; 355471a5eb8SAppaRao Puli } 356471a5eb8SAppaRao Puli } 3572c70f800SEd Tanous else if constexpr (IsVector<Type>::value) 358471a5eb8SAppaRao Puli { 359471a5eb8SAppaRao Puli if (!jsonValue.is_array()) 360471a5eb8SAppaRao Puli { 361471a5eb8SAppaRao Puli return false; 362471a5eb8SAppaRao Puli } 363471a5eb8SAppaRao Puli 364471a5eb8SAppaRao Puli for (const auto& val : jsonValue.items()) 365471a5eb8SAppaRao Puli { 366471a5eb8SAppaRao Puli value.emplace_back(); 367471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(val.value(), key, 368471a5eb8SAppaRao Puli value.back()) && 369471a5eb8SAppaRao Puli ret; 370471a5eb8SAppaRao Puli } 371471a5eb8SAppaRao Puli } 372471a5eb8SAppaRao Puli else 373471a5eb8SAppaRao Puli { 374471a5eb8SAppaRao Puli UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 375471a5eb8SAppaRao Puli if (ec != UnpackErrorCode::success) 376471a5eb8SAppaRao Puli { 377471a5eb8SAppaRao Puli return false; 378471a5eb8SAppaRao Puli } 379471a5eb8SAppaRao Puli } 380471a5eb8SAppaRao Puli 38141352c24SSantosh Puranik return ret; 382771cfa0fSJason M. Bills } 3839712f8acSEd Tanous } // namespace details 3849712f8acSEd Tanous 385ea2e6eecSWilly Tu // clang-format off 386ea2e6eecSWilly Tu using UnpackVariant = std::variant< 387ea2e6eecSWilly Tu uint8_t*, 388ea2e6eecSWilly Tu uint16_t*, 389ea2e6eecSWilly Tu int16_t*, 390ea2e6eecSWilly Tu uint32_t*, 391ea2e6eecSWilly Tu int32_t*, 392ea2e6eecSWilly Tu uint64_t*, 393ea2e6eecSWilly Tu int64_t*, 394ea2e6eecSWilly Tu bool*, 395ea2e6eecSWilly Tu double*, 396ea2e6eecSWilly Tu std::string*, 397ea2e6eecSWilly Tu nlohmann::json*, 398b6164cbeSEd Tanous nlohmann::json::object_t*, 3998099c517SEd Tanous std::variant<std::string, std::nullptr_t>*, 4008099c517SEd Tanous std::variant<uint8_t, std::nullptr_t>*, 4018099c517SEd Tanous std::variant<int16_t, std::nullptr_t>*, 4028099c517SEd Tanous std::variant<uint16_t, std::nullptr_t>*, 4038099c517SEd Tanous std::variant<int32_t, std::nullptr_t>*, 4048099c517SEd Tanous std::variant<uint32_t, std::nullptr_t>*, 4058099c517SEd Tanous std::variant<int64_t, std::nullptr_t>*, 4068099c517SEd Tanous std::variant<uint64_t, std::nullptr_t>*, 4078099c517SEd Tanous std::variant<double, std::nullptr_t>*, 4088099c517SEd Tanous std::variant<bool, std::nullptr_t>*, 409ea2e6eecSWilly Tu std::vector<uint8_t>*, 410ea2e6eecSWilly Tu std::vector<uint16_t>*, 411ea2e6eecSWilly Tu std::vector<int16_t>*, 412ea2e6eecSWilly Tu std::vector<uint32_t>*, 413ea2e6eecSWilly Tu std::vector<int32_t>*, 414ea2e6eecSWilly Tu std::vector<uint64_t>*, 415ea2e6eecSWilly Tu std::vector<int64_t>*, 416ea2e6eecSWilly Tu //std::vector<bool>*, 417ea2e6eecSWilly Tu std::vector<double>*, 418ea2e6eecSWilly Tu std::vector<std::string>*, 419ea2e6eecSWilly Tu std::vector<nlohmann::json>*, 420b6164cbeSEd Tanous std::vector<nlohmann::json::object_t>*, 421ea2e6eecSWilly Tu std::optional<uint8_t>*, 422ea2e6eecSWilly Tu std::optional<uint16_t>*, 423ea2e6eecSWilly Tu std::optional<int16_t>*, 424ea2e6eecSWilly Tu std::optional<uint32_t>*, 425ea2e6eecSWilly Tu std::optional<int32_t>*, 426ea2e6eecSWilly Tu std::optional<uint64_t>*, 427ea2e6eecSWilly Tu std::optional<int64_t>*, 428ea2e6eecSWilly Tu std::optional<bool>*, 429ea2e6eecSWilly Tu std::optional<double>*, 430ea2e6eecSWilly Tu std::optional<std::string>*, 431ea2e6eecSWilly Tu std::optional<nlohmann::json>*, 432b6164cbeSEd Tanous std::optional<nlohmann::json::object_t>*, 433ea2e6eecSWilly Tu std::optional<std::vector<uint8_t>>*, 434ea2e6eecSWilly Tu std::optional<std::vector<uint16_t>>*, 435ea2e6eecSWilly Tu std::optional<std::vector<int16_t>>*, 436ea2e6eecSWilly Tu std::optional<std::vector<uint32_t>>*, 437ea2e6eecSWilly Tu std::optional<std::vector<int32_t>>*, 438ea2e6eecSWilly Tu std::optional<std::vector<uint64_t>>*, 439ea2e6eecSWilly Tu std::optional<std::vector<int64_t>>*, 440ea2e6eecSWilly Tu //std::optional<std::vector<bool>>*, 441ea2e6eecSWilly Tu std::optional<std::vector<double>>*, 442ea2e6eecSWilly Tu std::optional<std::vector<std::string>>*, 443b6164cbeSEd Tanous std::optional<std::vector<nlohmann::json>>*, 4448099c517SEd Tanous std::optional<std::vector<nlohmann::json::object_t>>*, 4458099c517SEd Tanous std::optional<std::variant<std::string, std::nullptr_t>>*, 4468099c517SEd Tanous std::optional<std::variant<uint8_t, std::nullptr_t>>*, 4478099c517SEd Tanous std::optional<std::variant<int16_t, std::nullptr_t>>*, 4488099c517SEd Tanous std::optional<std::variant<uint16_t, std::nullptr_t>>*, 4498099c517SEd Tanous std::optional<std::variant<int32_t, std::nullptr_t>>*, 4508099c517SEd Tanous std::optional<std::variant<uint32_t, std::nullptr_t>>*, 4518099c517SEd Tanous std::optional<std::variant<int64_t, std::nullptr_t>>*, 4528099c517SEd Tanous std::optional<std::variant<uint64_t, std::nullptr_t>>*, 4538099c517SEd Tanous std::optional<std::variant<double, std::nullptr_t>>*, 4548099c517SEd Tanous std::optional<std::variant<bool, std::nullptr_t>>*, 4558099c517SEd Tanous std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*, 4568099c517SEd Tanous std::optional<std::variant<nlohmann::json::object_t, std::nullptr_t>>* 457ea2e6eecSWilly Tu >; 458ea2e6eecSWilly Tu // clang-format on 459ea2e6eecSWilly Tu 460ea2e6eecSWilly Tu struct PerUnpack 461ea2e6eecSWilly Tu { 462ea2e6eecSWilly Tu std::string_view key; 463ea2e6eecSWilly Tu UnpackVariant value; 464ea2e6eecSWilly Tu bool complete = false; 465ea2e6eecSWilly Tu }; 466ea2e6eecSWilly Tu 467ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 468b6164cbeSEd Tanous std::span<PerUnpack> toUnpack); 469b6164cbeSEd Tanous 470b6164cbeSEd Tanous inline bool readJsonHelperObject(nlohmann::json::object_t& obj, 471b6164cbeSEd Tanous crow::Response& res, 472ea2e6eecSWilly Tu std::span<PerUnpack> toUnpack) 4739712f8acSEd Tanous { 47441352c24SSantosh Puranik bool result = true; 475b6164cbeSEd Tanous for (auto& item : obj) 4769712f8acSEd Tanous { 477ea2e6eecSWilly Tu size_t unpackIndex = 0; 478ea2e6eecSWilly Tu for (; unpackIndex < toUnpack.size(); unpackIndex++) 479ea2e6eecSWilly Tu { 480ea2e6eecSWilly Tu PerUnpack& unpackSpec = toUnpack[unpackIndex]; 481ea2e6eecSWilly Tu std::string_view key = unpackSpec.key; 482ea2e6eecSWilly Tu size_t keysplitIndex = key.find('/'); 483ea2e6eecSWilly Tu std::string_view leftover; 484ea2e6eecSWilly Tu if (keysplitIndex != std::string_view::npos) 485ea2e6eecSWilly Tu { 486ea2e6eecSWilly Tu leftover = key.substr(keysplitIndex + 1); 487ea2e6eecSWilly Tu key = key.substr(0, keysplitIndex); 488ea2e6eecSWilly Tu } 489ea2e6eecSWilly Tu 490d91415c4SEd Tanous if (key != item.first || unpackSpec.complete) 491ea2e6eecSWilly Tu { 492ea2e6eecSWilly Tu continue; 493ea2e6eecSWilly Tu } 494ea2e6eecSWilly Tu 495ea2e6eecSWilly Tu // Sublevel key 496ea2e6eecSWilly Tu if (!leftover.empty()) 497ea2e6eecSWilly Tu { 498ea2e6eecSWilly Tu // Include the slash in the key so we can compare later 499ea2e6eecSWilly Tu key = unpackSpec.key.substr(0, keysplitIndex + 1); 500ea2e6eecSWilly Tu nlohmann::json j; 501d91415c4SEd Tanous result = details::unpackValue<nlohmann::json>(item.second, key, 502ea2e6eecSWilly Tu res, j) && 50341352c24SSantosh Puranik result; 50455f79e6fSEd Tanous if (!result) 505ea2e6eecSWilly Tu { 506ea2e6eecSWilly Tu return result; 5079712f8acSEd Tanous } 5089712f8acSEd Tanous 509ea2e6eecSWilly Tu std::vector<PerUnpack> nextLevel; 510ea2e6eecSWilly Tu for (PerUnpack& p : toUnpack) 511ea2e6eecSWilly Tu { 512ea2e6eecSWilly Tu if (!p.key.starts_with(key)) 513ea2e6eecSWilly Tu { 514ea2e6eecSWilly Tu continue; 515ea2e6eecSWilly Tu } 516ea2e6eecSWilly Tu std::string_view thisLeftover = p.key.substr(key.size()); 517ea2e6eecSWilly Tu nextLevel.push_back({thisLeftover, p.value, false}); 518ea2e6eecSWilly Tu p.complete = true; 5199712f8acSEd Tanous } 52077dd8813SKowalski, Kamil 521ea2e6eecSWilly Tu result = readJsonHelper(j, res, nextLevel) && result; 522ea2e6eecSWilly Tu break; 523ea2e6eecSWilly Tu } 524ea2e6eecSWilly Tu 525002d39b4SEd Tanous result = std::visit( 526ea2e6eecSWilly Tu [&item, &unpackSpec, &res](auto&& val) { 527ea2e6eecSWilly Tu using ContainedT = 528ea2e6eecSWilly Tu std::remove_pointer_t<std::decay_t<decltype(val)>>; 529ea2e6eecSWilly Tu return details::unpackValue<ContainedT>( 530d91415c4SEd Tanous item.second, unpackSpec.key, res, *val); 531ea2e6eecSWilly Tu }, 532ea2e6eecSWilly Tu unpackSpec.value) && 533ea2e6eecSWilly Tu result; 534ea2e6eecSWilly Tu 535ea2e6eecSWilly Tu unpackSpec.complete = true; 536ea2e6eecSWilly Tu break; 537ea2e6eecSWilly Tu } 538ea2e6eecSWilly Tu 539ea2e6eecSWilly Tu if (unpackIndex == toUnpack.size()) 540ea2e6eecSWilly Tu { 541d91415c4SEd Tanous messages::propertyUnknown(res, item.first); 542ea2e6eecSWilly Tu result = false; 543ea2e6eecSWilly Tu } 544ea2e6eecSWilly Tu } 545ea2e6eecSWilly Tu 546ea2e6eecSWilly Tu for (PerUnpack& perUnpack : toUnpack) 547ea2e6eecSWilly Tu { 54855f79e6fSEd Tanous if (!perUnpack.complete) 549ea2e6eecSWilly Tu { 550ea2e6eecSWilly Tu bool isOptional = std::visit( 551ea2e6eecSWilly Tu [](auto&& val) { 552ea2e6eecSWilly Tu using ContainedType = 553ea2e6eecSWilly Tu std::remove_pointer_t<std::decay_t<decltype(val)>>; 554ea2e6eecSWilly Tu return details::IsOptional<ContainedType>::value; 555ea2e6eecSWilly Tu }, 556ea2e6eecSWilly Tu perUnpack.value); 557ea2e6eecSWilly Tu if (isOptional) 558ea2e6eecSWilly Tu { 559ea2e6eecSWilly Tu continue; 560ea2e6eecSWilly Tu } 561ea2e6eecSWilly Tu messages::propertyMissing(res, perUnpack.key); 562ea2e6eecSWilly Tu result = false; 563ea2e6eecSWilly Tu } 564ea2e6eecSWilly Tu } 565ea2e6eecSWilly Tu return result; 566ea2e6eecSWilly Tu } 567ea2e6eecSWilly Tu 568b6164cbeSEd Tanous inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 569b6164cbeSEd Tanous std::span<PerUnpack> toUnpack) 570b6164cbeSEd Tanous { 571b6164cbeSEd Tanous nlohmann::json::object_t* obj = 572b6164cbeSEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 573b6164cbeSEd Tanous if (obj == nullptr) 574b6164cbeSEd Tanous { 575b6164cbeSEd Tanous BMCWEB_LOG_DEBUG("Json value is not an object"); 576b6164cbeSEd Tanous messages::unrecognizedRequestBody(res); 577b6164cbeSEd Tanous return false; 578b6164cbeSEd Tanous } 579b6164cbeSEd Tanous return readJsonHelperObject(*obj, res, toUnpack); 580b6164cbeSEd Tanous } 581b6164cbeSEd Tanous 58289492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {} 583ea2e6eecSWilly Tu 584ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes> 585ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key, 586ea2e6eecSWilly Tu FirstType& first, UnpackTypes&&... in) 587ea2e6eecSWilly Tu { 588ea2e6eecSWilly Tu if (toPack.empty()) 589ea2e6eecSWilly Tu { 590ea2e6eecSWilly Tu return; 591ea2e6eecSWilly Tu } 592ea2e6eecSWilly Tu toPack[0].key = key; 593ea2e6eecSWilly Tu toPack[0].value = &first; 594ea2e6eecSWilly Tu // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 595ea2e6eecSWilly Tu packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...); 596ea2e6eecSWilly Tu } 597ea2e6eecSWilly Tu 598ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes> 599b6164cbeSEd Tanous bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res, 600b6164cbeSEd Tanous std::string_view key, FirstType&& first, 601b6164cbeSEd Tanous UnpackTypes&&... in) 602ea2e6eecSWilly Tu { 603ea2e6eecSWilly Tu const std::size_t n = sizeof...(UnpackTypes) + 2; 604ea2e6eecSWilly Tu std::array<PerUnpack, n / 2> toUnpack2; 605ea2e6eecSWilly Tu packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...); 606b6164cbeSEd Tanous return readJsonHelperObject(jsonRequest, res, toUnpack2); 607ea2e6eecSWilly Tu } 608ea2e6eecSWilly Tu 609b6164cbeSEd Tanous template <typename FirstType, typename... UnpackTypes> 610b6164cbeSEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res, 611b6164cbeSEd Tanous std::string_view key, FirstType&& first, UnpackTypes&&... in) 612b6164cbeSEd Tanous { 613b6164cbeSEd Tanous nlohmann::json::object_t* obj = 614b6164cbeSEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 615b6164cbeSEd Tanous if (obj == nullptr) 616b6164cbeSEd Tanous { 617b6164cbeSEd Tanous BMCWEB_LOG_DEBUG("Json value is not an object"); 618b6164cbeSEd Tanous messages::unrecognizedRequestBody(res); 619b6164cbeSEd Tanous return false; 620b6164cbeSEd Tanous } 6215be2b14aSEd Tanous return readJsonObject(*obj, res, key, std::forward<FirstType>(first), 6225be2b14aSEd Tanous std::forward<UnpackTypes&&>(in)...); 623b6164cbeSEd Tanous } 624b6164cbeSEd Tanous 625b6164cbeSEd Tanous inline std::optional<nlohmann::json::object_t> 626ea2e6eecSWilly Tu readJsonPatchHelper(const crow::Request& req, crow::Response& res) 6270627a2c7SEd Tanous { 6280627a2c7SEd Tanous nlohmann::json jsonRequest; 6290627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 6300627a2c7SEd Tanous { 63162598e31SEd Tanous BMCWEB_LOG_DEBUG("Json value not readable"); 632ea2e6eecSWilly Tu return std::nullopt; 6330627a2c7SEd Tanous } 634357bb8f8SEd Tanous nlohmann::json::object_t* object = 635357bb8f8SEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 636357bb8f8SEd Tanous if (object == nullptr || object->empty()) 63715ed6780SWilly Tu { 63862598e31SEd Tanous BMCWEB_LOG_DEBUG("Json value is empty"); 63915ed6780SWilly Tu messages::emptyJSON(res); 640ea2e6eecSWilly Tu return std::nullopt; 641ea2e6eecSWilly Tu } 642357bb8f8SEd Tanous std::erase_if(*object, 643357bb8f8SEd Tanous [](const std::pair<std::string, nlohmann::json>& item) { 644357bb8f8SEd Tanous return item.first.starts_with("@odata."); 645357bb8f8SEd Tanous }); 646357bb8f8SEd Tanous if (object->empty()) 647357bb8f8SEd Tanous { 648357bb8f8SEd Tanous // If the update request only contains OData annotations, the service 649357bb8f8SEd Tanous // should return the HTTP 400 Bad Request status code with the 650357bb8f8SEd Tanous // NoOperation message from the Base Message Registry, ... 651357bb8f8SEd Tanous messages::noOperation(res); 652357bb8f8SEd Tanous return std::nullopt; 653357bb8f8SEd Tanous } 654357bb8f8SEd Tanous 655b6164cbeSEd Tanous return {std::move(*object)}; 656ea2e6eecSWilly Tu } 657ea2e6eecSWilly Tu 658ea2e6eecSWilly Tu template <typename... UnpackTypes> 659ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res, 660ea2e6eecSWilly Tu std::string_view key, UnpackTypes&&... in) 661ea2e6eecSWilly Tu { 662*76b038f2SEd Tanous std::optional<nlohmann::json::object_t> jsonRequest = 663*76b038f2SEd Tanous readJsonPatchHelper(req, res); 664e01d0c36SEd Tanous if (!jsonRequest) 665ea2e6eecSWilly Tu { 66615ed6780SWilly Tu return false; 66715ed6780SWilly Tu } 668*76b038f2SEd Tanous if (jsonRequest->empty()) 669b6164cbeSEd Tanous { 670b6164cbeSEd Tanous messages::emptyJSON(res); 671b6164cbeSEd Tanous return false; 672b6164cbeSEd Tanous } 67315ed6780SWilly Tu 674*76b038f2SEd Tanous return readJsonObject(*jsonRequest, res, key, 675b6164cbeSEd Tanous std::forward<UnpackTypes&&>(in)...); 67615ed6780SWilly Tu } 67715ed6780SWilly Tu 67815ed6780SWilly Tu template <typename... UnpackTypes> 67915ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res, 680ea2e6eecSWilly Tu const char* key, UnpackTypes&&... in) 68115ed6780SWilly Tu { 68215ed6780SWilly Tu nlohmann::json jsonRequest; 68315ed6780SWilly Tu if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 68415ed6780SWilly Tu { 68562598e31SEd Tanous BMCWEB_LOG_DEBUG("Json value not readable"); 68615ed6780SWilly Tu return false; 68715ed6780SWilly Tu } 688b6164cbeSEd Tanous nlohmann::json::object_t* object = 689b6164cbeSEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 690b6164cbeSEd Tanous if (object == nullptr) 691b6164cbeSEd Tanous { 692b6164cbeSEd Tanous BMCWEB_LOG_DEBUG("Json value is empty"); 693b6164cbeSEd Tanous messages::emptyJSON(res); 694b6164cbeSEd Tanous return false; 695b6164cbeSEd Tanous } 696b6164cbeSEd Tanous return readJsonObject(*object, res, key, 697b6164cbeSEd Tanous std::forward<UnpackTypes&&>(in)...); 6980627a2c7SEd Tanous } 699185444b1SNan Zhou 700185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the 701185444b1SNan Zhou // @odata.id key 702185444b1SNan Zhou inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b) 703185444b1SNan Zhou { 704185444b1SNan Zhou using object_t = nlohmann::json::object_t; 705185444b1SNan Zhou const object_t* aObj = a.get_ptr<const object_t*>(); 706185444b1SNan Zhou const object_t* bObj = b.get_ptr<const object_t*>(); 707185444b1SNan Zhou 708185444b1SNan Zhou if (aObj == nullptr) 709185444b1SNan Zhou { 710185444b1SNan Zhou if (bObj == nullptr) 711185444b1SNan Zhou { 712185444b1SNan Zhou return 0; 713185444b1SNan Zhou } 714185444b1SNan Zhou return -1; 715185444b1SNan Zhou } 716185444b1SNan Zhou if (bObj == nullptr) 717185444b1SNan Zhou { 718185444b1SNan Zhou return 1; 719185444b1SNan Zhou } 720185444b1SNan Zhou object_t::const_iterator aIt = aObj->find("@odata.id"); 721185444b1SNan Zhou object_t::const_iterator bIt = bObj->find("@odata.id"); 722185444b1SNan Zhou // If either object doesn't have the key, they get "sorted" to the end. 723185444b1SNan Zhou if (aIt == aObj->end()) 724185444b1SNan Zhou { 725185444b1SNan Zhou if (bIt == bObj->end()) 726185444b1SNan Zhou { 727185444b1SNan Zhou return 0; 728185444b1SNan Zhou } 729185444b1SNan Zhou return -1; 730185444b1SNan Zhou } 731185444b1SNan Zhou if (bIt == bObj->end()) 732185444b1SNan Zhou { 733185444b1SNan Zhou return 1; 734185444b1SNan Zhou } 735185444b1SNan Zhou const nlohmann::json::string_t* nameA = 736185444b1SNan Zhou aIt->second.get_ptr<const std::string*>(); 737185444b1SNan Zhou const nlohmann::json::string_t* nameB = 738185444b1SNan Zhou bIt->second.get_ptr<const std::string*>(); 739185444b1SNan Zhou // If either object doesn't have a string as the key, they get "sorted" to 740185444b1SNan Zhou // the end. 741185444b1SNan Zhou if (nameA == nullptr) 742185444b1SNan Zhou { 743185444b1SNan Zhou if (nameB == nullptr) 744185444b1SNan Zhou { 745185444b1SNan Zhou return 0; 746185444b1SNan Zhou } 747185444b1SNan Zhou return -1; 748185444b1SNan Zhou } 749185444b1SNan Zhou if (nameB == nullptr) 750185444b1SNan Zhou { 751185444b1SNan Zhou return 1; 752185444b1SNan Zhou } 753185444b1SNan Zhou boost::urls::url_view aUrl(*nameA); 754185444b1SNan Zhou boost::urls::url_view bUrl(*nameB); 755185444b1SNan Zhou auto segmentsAIt = aUrl.segments().begin(); 756185444b1SNan Zhou auto segmentsBIt = bUrl.segments().begin(); 757185444b1SNan Zhou 758185444b1SNan Zhou while (true) 759185444b1SNan Zhou { 760185444b1SNan Zhou if (segmentsAIt == aUrl.segments().end()) 761185444b1SNan Zhou { 762185444b1SNan Zhou if (segmentsBIt == bUrl.segments().end()) 763185444b1SNan Zhou { 764185444b1SNan Zhou return 0; 765185444b1SNan Zhou } 766185444b1SNan Zhou return -1; 767185444b1SNan Zhou } 768185444b1SNan Zhou if (segmentsBIt == bUrl.segments().end()) 769185444b1SNan Zhou { 770185444b1SNan Zhou return 1; 771185444b1SNan Zhou } 772185444b1SNan Zhou int res = alphanumComp(*segmentsAIt, *segmentsBIt); 773185444b1SNan Zhou if (res != 0) 774185444b1SNan Zhou { 775185444b1SNan Zhou return res; 776185444b1SNan Zhou } 777185444b1SNan Zhou 778185444b1SNan Zhou segmentsAIt++; 779185444b1SNan Zhou segmentsBIt++; 780185444b1SNan Zhou } 781185444b1SNan Zhou }; 782185444b1SNan Zhou 783185444b1SNan Zhou struct ODataObjectLess 784185444b1SNan Zhou { 785185444b1SNan Zhou bool operator()(const nlohmann::json& left, 786185444b1SNan Zhou const nlohmann::json& right) const 787185444b1SNan Zhou { 788185444b1SNan Zhou return odataObjectCmp(left, right) < 0; 789185444b1SNan Zhou } 790185444b1SNan Zhou }; 791185444b1SNan Zhou 792185444b1SNan Zhou // Sort the JSON array by |element[key]|. 793185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller 794185444b1SNan Zhou // those whose |element[key]| is string. 795185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array) 796185444b1SNan Zhou { 7973544d2a7SEd Tanous std::ranges::sort(array, ODataObjectLess()); 798185444b1SNan Zhou } 799185444b1SNan Zhou 8008a7c4b47SNan Zhou // Returns the estimated size of the JSON value 8018a7c4b47SNan Zhou // The implementation walks through every key and every value, accumulates the 8028a7c4b47SNan Zhou // total size of keys and values. 8038a7c4b47SNan Zhou // Ideally, we should use a custom allocator that nlohmann JSON supports. 8048a7c4b47SNan Zhou 8058a7c4b47SNan Zhou // Assumption made: 8068a7c4b47SNan Zhou // 1. number: 8 characters 8078a7c4b47SNan Zhou // 2. boolean: 5 characters (False) 8088a7c4b47SNan Zhou // 3. string: len(str) + 2 characters (quote) 8098a7c4b47SNan Zhou // 4. bytes: len(bytes) characters 8108a7c4b47SNan Zhou // 5. null: 4 characters (null) 8118a7c4b47SNan Zhou uint64_t getEstimatedJsonSize(const nlohmann::json& root); 8128a7c4b47SNan Zhou 81377dd8813SKowalski, Kamil } // namespace json_util 81477dd8813SKowalski, Kamil } // namespace redfish 815