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 93471a5eb8SAppaRao Puli enum class UnpackErrorCode 94471a5eb8SAppaRao Puli { 95471a5eb8SAppaRao Puli success, 96471a5eb8SAppaRao Puli invalidType, 97471a5eb8SAppaRao Puli outOfRange 98471a5eb8SAppaRao Puli }; 99471a5eb8SAppaRao Puli 100a6acbb31SJames Feist template <typename ToType, typename FromType> 101ea2e6eecSWilly Tu bool checkRange(const FromType& from, std::string_view key) 102a6acbb31SJames Feist { 103ee344e0fSEd Tanous if (from > std::numeric_limits<ToType>::max()) 104a6acbb31SJames Feist { 10562598e31SEd Tanous BMCWEB_LOG_DEBUG("Value for key {} was greater than max: {}", key, 10662598e31SEd Tanous __PRETTY_FUNCTION__); 107a6acbb31SJames Feist return false; 108a6acbb31SJames Feist } 109ee344e0fSEd Tanous if (from < std::numeric_limits<ToType>::lowest()) 110a6acbb31SJames Feist { 11162598e31SEd Tanous BMCWEB_LOG_DEBUG("Value for key {} was less than min: {}", key, 11262598e31SEd Tanous __PRETTY_FUNCTION__); 113a6acbb31SJames Feist return false; 114a6acbb31SJames Feist } 115a6acbb31SJames Feist if constexpr (std::is_floating_point_v<ToType>) 116a6acbb31SJames Feist { 117ee344e0fSEd Tanous if (std::isnan(from)) 118a6acbb31SJames Feist { 11962598e31SEd Tanous BMCWEB_LOG_DEBUG("Value for key {} was NAN", key); 120a6acbb31SJames Feist return false; 121a6acbb31SJames Feist } 122a6acbb31SJames Feist } 123a6acbb31SJames Feist 124a6acbb31SJames Feist return true; 125a6acbb31SJames Feist } 126a6acbb31SJames Feist 127771cfa0fSJason M. Bills template <typename Type> 128471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 129ea2e6eecSWilly Tu std::string_view key, Type& value) 130771cfa0fSJason M. Bills { 131471a5eb8SAppaRao Puli UnpackErrorCode ret = UnpackErrorCode::success; 13241352c24SSantosh Puranik 133a6acbb31SJames Feist if constexpr (std::is_floating_point_v<Type>) 134771cfa0fSJason M. Bills { 135a6acbb31SJames Feist double helper = 0; 136a6acbb31SJames Feist double* jsonPtr = jsonValue.get_ptr<double*>(); 137771cfa0fSJason M. Bills 138771cfa0fSJason M. Bills if (jsonPtr == nullptr) 139771cfa0fSJason M. Bills { 140a6acbb31SJames Feist int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 141a6acbb31SJames Feist if (intPtr != nullptr) 142771cfa0fSJason M. Bills { 143a6acbb31SJames Feist helper = static_cast<double>(*intPtr); 144a6acbb31SJames Feist jsonPtr = &helper; 145771cfa0fSJason M. Bills } 146a6acbb31SJames Feist } 1475eb2bef2SAppaRao Puli if (jsonPtr == nullptr) 1485eb2bef2SAppaRao Puli { 149471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 1505eb2bef2SAppaRao Puli } 151cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 152771cfa0fSJason M. Bills { 153471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 154771cfa0fSJason M. Bills } 155771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 156771cfa0fSJason M. Bills } 157a6acbb31SJames Feist 158a6acbb31SJames Feist else if constexpr (std::is_signed_v<Type>) 159a6acbb31SJames Feist { 160a6acbb31SJames Feist int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 161271584abSEd Tanous if (jsonPtr == nullptr) 162271584abSEd Tanous { 163471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 164271584abSEd Tanous } 165cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 166a6acbb31SJames Feist { 167471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 168a6acbb31SJames Feist } 169a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 170a6acbb31SJames Feist } 171a6acbb31SJames Feist 1728102ddbaSAppaRao Puli else if constexpr ((std::is_unsigned_v<Type>)&&( 1738102ddbaSAppaRao Puli !std::is_same_v<bool, Type>)) 174a6acbb31SJames Feist { 175a6acbb31SJames Feist uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 176271584abSEd Tanous if (jsonPtr == nullptr) 177271584abSEd Tanous { 178471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 179271584abSEd Tanous } 180cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 181a6acbb31SJames Feist { 182471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 183a6acbb31SJames Feist } 184a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 185a6acbb31SJames Feist } 186a6acbb31SJames Feist 1870627a2c7SEd Tanous else if constexpr (std::is_same_v<nlohmann::json, Type>) 1880627a2c7SEd Tanous { 1890627a2c7SEd Tanous value = std::move(jsonValue); 1900627a2c7SEd Tanous } 191471a5eb8SAppaRao Puli else 192471a5eb8SAppaRao Puli { 193471a5eb8SAppaRao Puli using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 194471a5eb8SAppaRao Puli JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 195471a5eb8SAppaRao Puli if (jsonPtr == nullptr) 196471a5eb8SAppaRao Puli { 19762598e31SEd Tanous BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key, 19862598e31SEd Tanous jsonValue.type_name()); 199471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 200471a5eb8SAppaRao Puli } 201471a5eb8SAppaRao Puli value = std::move(*jsonPtr); 202471a5eb8SAppaRao Puli } 203471a5eb8SAppaRao Puli return ret; 204471a5eb8SAppaRao Puli } 205471a5eb8SAppaRao Puli 206471a5eb8SAppaRao Puli template <typename Type> 207ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, 208471a5eb8SAppaRao Puli crow::Response& res, Type& value) 209471a5eb8SAppaRao Puli { 210471a5eb8SAppaRao Puli bool ret = true; 211471a5eb8SAppaRao Puli 2122c70f800SEd Tanous if constexpr (IsOptional<Type>::value) 213471a5eb8SAppaRao Puli { 214471a5eb8SAppaRao Puli value.emplace(); 215471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 216471a5eb8SAppaRao Puli *value) && 217471a5eb8SAppaRao Puli ret; 218471a5eb8SAppaRao Puli } 2192c70f800SEd Tanous else if constexpr (IsStdArray<Type>::value) 220318226c2SJames Feist { 221318226c2SJames Feist if (!jsonValue.is_array()) 222318226c2SJames Feist { 2232e8c4bdaSEd Tanous messages::propertyValueTypeError(res, res.jsonValue, key); 22441352c24SSantosh Puranik return false; 225318226c2SJames Feist } 226318226c2SJames Feist if (jsonValue.size() != value.size()) 227318226c2SJames Feist { 2282e8c4bdaSEd Tanous messages::propertyValueTypeError(res, res.jsonValue, key); 22941352c24SSantosh Puranik return false; 230318226c2SJames Feist } 231318226c2SJames Feist size_t index = 0; 232318226c2SJames Feist for (const auto& val : jsonValue.items()) 233318226c2SJames Feist { 23441352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 23541352c24SSantosh Puranik value[index++]) && 23641352c24SSantosh Puranik ret; 237318226c2SJames Feist } 238318226c2SJames Feist } 2392c70f800SEd Tanous else if constexpr (IsVector<Type>::value) 240b1556427SEd Tanous { 241b1556427SEd Tanous if (!jsonValue.is_array()) 242b1556427SEd Tanous { 2432e8c4bdaSEd Tanous messages::propertyValueTypeError(res, res.jsonValue, key); 24441352c24SSantosh Puranik return false; 245b1556427SEd Tanous } 246b1556427SEd Tanous 247b1556427SEd Tanous for (const auto& val : jsonValue.items()) 248b1556427SEd Tanous { 249b1556427SEd Tanous value.emplace_back(); 25041352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 25141352c24SSantosh Puranik value.back()) && 25241352c24SSantosh Puranik ret; 253b1556427SEd Tanous } 254b1556427SEd Tanous } 255771cfa0fSJason M. Bills else 256771cfa0fSJason M. Bills { 257471a5eb8SAppaRao Puli UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 258471a5eb8SAppaRao Puli if (ec != UnpackErrorCode::success) 259771cfa0fSJason M. Bills { 260471a5eb8SAppaRao Puli if (ec == UnpackErrorCode::invalidType) 261471a5eb8SAppaRao Puli { 2622e8c4bdaSEd Tanous messages::propertyValueTypeError(res, jsonValue, key); 263471a5eb8SAppaRao Puli } 264471a5eb8SAppaRao Puli else if (ec == UnpackErrorCode::outOfRange) 265471a5eb8SAppaRao Puli { 266e2616cc5SEd Tanous messages::propertyValueNotInList(res, jsonValue, key); 267471a5eb8SAppaRao Puli } 26841352c24SSantosh Puranik return false; 269771cfa0fSJason M. Bills } 270771cfa0fSJason M. Bills } 271471a5eb8SAppaRao Puli 272471a5eb8SAppaRao Puli return ret; 273471a5eb8SAppaRao Puli } 274471a5eb8SAppaRao Puli 275471a5eb8SAppaRao Puli template <typename Type> 276ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value) 277471a5eb8SAppaRao Puli { 278471a5eb8SAppaRao Puli bool ret = true; 2792c70f800SEd Tanous if constexpr (IsOptional<Type>::value) 280471a5eb8SAppaRao Puli { 281471a5eb8SAppaRao Puli value.emplace(); 282471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) && 283471a5eb8SAppaRao Puli ret; 284471a5eb8SAppaRao Puli } 2852c70f800SEd Tanous else if constexpr (IsStdArray<Type>::value) 286471a5eb8SAppaRao Puli { 287471a5eb8SAppaRao Puli if (!jsonValue.is_array()) 288471a5eb8SAppaRao Puli { 289471a5eb8SAppaRao Puli return false; 290471a5eb8SAppaRao Puli } 291471a5eb8SAppaRao Puli if (jsonValue.size() != value.size()) 292471a5eb8SAppaRao Puli { 293471a5eb8SAppaRao Puli return false; 294471a5eb8SAppaRao Puli } 295471a5eb8SAppaRao Puli size_t index = 0; 296471a5eb8SAppaRao Puli for (const auto& val : jsonValue.items()) 297471a5eb8SAppaRao Puli { 298471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(val.value(), key, 299471a5eb8SAppaRao Puli value[index++]) && 300471a5eb8SAppaRao Puli ret; 301471a5eb8SAppaRao Puli } 302471a5eb8SAppaRao Puli } 3032c70f800SEd Tanous else if constexpr (IsVector<Type>::value) 304471a5eb8SAppaRao Puli { 305471a5eb8SAppaRao Puli if (!jsonValue.is_array()) 306471a5eb8SAppaRao Puli { 307471a5eb8SAppaRao Puli return false; 308471a5eb8SAppaRao Puli } 309471a5eb8SAppaRao Puli 310471a5eb8SAppaRao Puli for (const auto& val : jsonValue.items()) 311471a5eb8SAppaRao Puli { 312471a5eb8SAppaRao Puli value.emplace_back(); 313471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(val.value(), key, 314471a5eb8SAppaRao Puli value.back()) && 315471a5eb8SAppaRao Puli ret; 316471a5eb8SAppaRao Puli } 317471a5eb8SAppaRao Puli } 318471a5eb8SAppaRao Puli else 319471a5eb8SAppaRao Puli { 320471a5eb8SAppaRao Puli UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 321471a5eb8SAppaRao Puli if (ec != UnpackErrorCode::success) 322471a5eb8SAppaRao Puli { 323471a5eb8SAppaRao Puli return false; 324471a5eb8SAppaRao Puli } 325471a5eb8SAppaRao Puli } 326471a5eb8SAppaRao Puli 32741352c24SSantosh Puranik return ret; 328771cfa0fSJason M. Bills } 3299712f8acSEd Tanous } // namespace details 3309712f8acSEd Tanous 331ea2e6eecSWilly Tu // clang-format off 332ea2e6eecSWilly Tu using UnpackVariant = std::variant< 333ea2e6eecSWilly Tu uint8_t*, 334ea2e6eecSWilly Tu uint16_t*, 335ea2e6eecSWilly Tu int16_t*, 336ea2e6eecSWilly Tu uint32_t*, 337ea2e6eecSWilly Tu int32_t*, 338ea2e6eecSWilly Tu uint64_t*, 339ea2e6eecSWilly Tu int64_t*, 340ea2e6eecSWilly Tu bool*, 341ea2e6eecSWilly Tu double*, 342ea2e6eecSWilly Tu std::string*, 343ea2e6eecSWilly Tu nlohmann::json*, 344b6164cbeSEd Tanous nlohmann::json::object_t*, 345ea2e6eecSWilly Tu std::vector<uint8_t>*, 346ea2e6eecSWilly Tu std::vector<uint16_t>*, 347ea2e6eecSWilly Tu std::vector<int16_t>*, 348ea2e6eecSWilly Tu std::vector<uint32_t>*, 349ea2e6eecSWilly Tu std::vector<int32_t>*, 350ea2e6eecSWilly Tu std::vector<uint64_t>*, 351ea2e6eecSWilly Tu std::vector<int64_t>*, 352ea2e6eecSWilly Tu //std::vector<bool>*, 353ea2e6eecSWilly Tu std::vector<double>*, 354ea2e6eecSWilly Tu std::vector<std::string>*, 355ea2e6eecSWilly Tu std::vector<nlohmann::json>*, 356b6164cbeSEd Tanous std::vector<nlohmann::json::object_t>*, 357ea2e6eecSWilly Tu std::optional<uint8_t>*, 358ea2e6eecSWilly Tu std::optional<uint16_t>*, 359ea2e6eecSWilly Tu std::optional<int16_t>*, 360ea2e6eecSWilly Tu std::optional<uint32_t>*, 361ea2e6eecSWilly Tu std::optional<int32_t>*, 362ea2e6eecSWilly Tu std::optional<uint64_t>*, 363ea2e6eecSWilly Tu std::optional<int64_t>*, 364ea2e6eecSWilly Tu std::optional<bool>*, 365ea2e6eecSWilly Tu std::optional<double>*, 366ea2e6eecSWilly Tu std::optional<std::string>*, 367ea2e6eecSWilly Tu std::optional<nlohmann::json>*, 368b6164cbeSEd Tanous std::optional<nlohmann::json::object_t>*, 369ea2e6eecSWilly Tu std::optional<std::vector<uint8_t>>*, 370ea2e6eecSWilly Tu std::optional<std::vector<uint16_t>>*, 371ea2e6eecSWilly Tu std::optional<std::vector<int16_t>>*, 372ea2e6eecSWilly Tu std::optional<std::vector<uint32_t>>*, 373ea2e6eecSWilly Tu std::optional<std::vector<int32_t>>*, 374ea2e6eecSWilly Tu std::optional<std::vector<uint64_t>>*, 375ea2e6eecSWilly Tu std::optional<std::vector<int64_t>>*, 376ea2e6eecSWilly Tu //std::optional<std::vector<bool>>*, 377ea2e6eecSWilly Tu std::optional<std::vector<double>>*, 378ea2e6eecSWilly Tu std::optional<std::vector<std::string>>*, 379b6164cbeSEd Tanous std::optional<std::vector<nlohmann::json>>*, 380b6164cbeSEd Tanous std::optional<std::vector<nlohmann::json::object_t>>* 381ea2e6eecSWilly Tu >; 382ea2e6eecSWilly Tu // clang-format on 383ea2e6eecSWilly Tu 384ea2e6eecSWilly Tu struct PerUnpack 385ea2e6eecSWilly Tu { 386ea2e6eecSWilly Tu std::string_view key; 387ea2e6eecSWilly Tu UnpackVariant value; 388ea2e6eecSWilly Tu bool complete = false; 389ea2e6eecSWilly Tu }; 390ea2e6eecSWilly Tu 391ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 392b6164cbeSEd Tanous std::span<PerUnpack> toUnpack); 393b6164cbeSEd Tanous 394b6164cbeSEd Tanous inline bool readJsonHelperObject(nlohmann::json::object_t& obj, 395b6164cbeSEd Tanous crow::Response& res, 396ea2e6eecSWilly Tu std::span<PerUnpack> toUnpack) 3979712f8acSEd Tanous { 39841352c24SSantosh Puranik bool result = true; 399b6164cbeSEd Tanous for (auto& item : obj) 4009712f8acSEd Tanous { 401ea2e6eecSWilly Tu size_t unpackIndex = 0; 402ea2e6eecSWilly Tu for (; unpackIndex < toUnpack.size(); unpackIndex++) 403ea2e6eecSWilly Tu { 404ea2e6eecSWilly Tu PerUnpack& unpackSpec = toUnpack[unpackIndex]; 405ea2e6eecSWilly Tu std::string_view key = unpackSpec.key; 406ea2e6eecSWilly Tu size_t keysplitIndex = key.find('/'); 407ea2e6eecSWilly Tu std::string_view leftover; 408ea2e6eecSWilly Tu if (keysplitIndex != std::string_view::npos) 409ea2e6eecSWilly Tu { 410ea2e6eecSWilly Tu leftover = key.substr(keysplitIndex + 1); 411ea2e6eecSWilly Tu key = key.substr(0, keysplitIndex); 412ea2e6eecSWilly Tu } 413ea2e6eecSWilly Tu 414d91415c4SEd Tanous if (key != item.first || unpackSpec.complete) 415ea2e6eecSWilly Tu { 416ea2e6eecSWilly Tu continue; 417ea2e6eecSWilly Tu } 418ea2e6eecSWilly Tu 419ea2e6eecSWilly Tu // Sublevel key 420ea2e6eecSWilly Tu if (!leftover.empty()) 421ea2e6eecSWilly Tu { 422ea2e6eecSWilly Tu // Include the slash in the key so we can compare later 423ea2e6eecSWilly Tu key = unpackSpec.key.substr(0, keysplitIndex + 1); 424ea2e6eecSWilly Tu nlohmann::json j; 425d91415c4SEd Tanous result = details::unpackValue<nlohmann::json>(item.second, key, 426ea2e6eecSWilly Tu res, j) && 42741352c24SSantosh Puranik result; 42855f79e6fSEd Tanous if (!result) 429ea2e6eecSWilly Tu { 430ea2e6eecSWilly Tu return result; 4319712f8acSEd Tanous } 4329712f8acSEd Tanous 433ea2e6eecSWilly Tu std::vector<PerUnpack> nextLevel; 434ea2e6eecSWilly Tu for (PerUnpack& p : toUnpack) 435ea2e6eecSWilly Tu { 436ea2e6eecSWilly Tu if (!p.key.starts_with(key)) 437ea2e6eecSWilly Tu { 438ea2e6eecSWilly Tu continue; 439ea2e6eecSWilly Tu } 440ea2e6eecSWilly Tu std::string_view thisLeftover = p.key.substr(key.size()); 441ea2e6eecSWilly Tu nextLevel.push_back({thisLeftover, p.value, false}); 442ea2e6eecSWilly Tu p.complete = true; 4439712f8acSEd Tanous } 44477dd8813SKowalski, Kamil 445ea2e6eecSWilly Tu result = readJsonHelper(j, res, nextLevel) && result; 446ea2e6eecSWilly Tu break; 447ea2e6eecSWilly Tu } 448ea2e6eecSWilly Tu 449002d39b4SEd Tanous result = std::visit( 450ea2e6eecSWilly Tu [&item, &unpackSpec, &res](auto&& val) { 451ea2e6eecSWilly Tu using ContainedT = 452ea2e6eecSWilly Tu std::remove_pointer_t<std::decay_t<decltype(val)>>; 453ea2e6eecSWilly Tu return details::unpackValue<ContainedT>( 454d91415c4SEd Tanous item.second, unpackSpec.key, res, *val); 455ea2e6eecSWilly Tu }, 456ea2e6eecSWilly Tu unpackSpec.value) && 457ea2e6eecSWilly Tu result; 458ea2e6eecSWilly Tu 459ea2e6eecSWilly Tu unpackSpec.complete = true; 460ea2e6eecSWilly Tu break; 461ea2e6eecSWilly Tu } 462ea2e6eecSWilly Tu 463ea2e6eecSWilly Tu if (unpackIndex == toUnpack.size()) 464ea2e6eecSWilly Tu { 465d91415c4SEd Tanous messages::propertyUnknown(res, item.first); 466ea2e6eecSWilly Tu result = false; 467ea2e6eecSWilly Tu } 468ea2e6eecSWilly Tu } 469ea2e6eecSWilly Tu 470ea2e6eecSWilly Tu for (PerUnpack& perUnpack : toUnpack) 471ea2e6eecSWilly Tu { 47255f79e6fSEd Tanous if (!perUnpack.complete) 473ea2e6eecSWilly Tu { 474ea2e6eecSWilly Tu bool isOptional = std::visit( 475ea2e6eecSWilly Tu [](auto&& val) { 476ea2e6eecSWilly Tu using ContainedType = 477ea2e6eecSWilly Tu std::remove_pointer_t<std::decay_t<decltype(val)>>; 478ea2e6eecSWilly Tu return details::IsOptional<ContainedType>::value; 479ea2e6eecSWilly Tu }, 480ea2e6eecSWilly Tu perUnpack.value); 481ea2e6eecSWilly Tu if (isOptional) 482ea2e6eecSWilly Tu { 483ea2e6eecSWilly Tu continue; 484ea2e6eecSWilly Tu } 485ea2e6eecSWilly Tu messages::propertyMissing(res, perUnpack.key); 486ea2e6eecSWilly Tu result = false; 487ea2e6eecSWilly Tu } 488ea2e6eecSWilly Tu } 489ea2e6eecSWilly Tu return result; 490ea2e6eecSWilly Tu } 491ea2e6eecSWilly Tu 492b6164cbeSEd Tanous inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 493b6164cbeSEd Tanous std::span<PerUnpack> toUnpack) 494b6164cbeSEd Tanous { 495b6164cbeSEd Tanous nlohmann::json::object_t* obj = 496b6164cbeSEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 497b6164cbeSEd Tanous if (obj == nullptr) 498b6164cbeSEd Tanous { 499b6164cbeSEd Tanous BMCWEB_LOG_DEBUG("Json value is not an object"); 500b6164cbeSEd Tanous messages::unrecognizedRequestBody(res); 501b6164cbeSEd Tanous return false; 502b6164cbeSEd Tanous } 503b6164cbeSEd Tanous return readJsonHelperObject(*obj, res, toUnpack); 504b6164cbeSEd Tanous } 505b6164cbeSEd Tanous 50689492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {} 507ea2e6eecSWilly Tu 508ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes> 509ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key, 510ea2e6eecSWilly Tu FirstType& first, UnpackTypes&&... in) 511ea2e6eecSWilly Tu { 512ea2e6eecSWilly Tu if (toPack.empty()) 513ea2e6eecSWilly Tu { 514ea2e6eecSWilly Tu return; 515ea2e6eecSWilly Tu } 516ea2e6eecSWilly Tu toPack[0].key = key; 517ea2e6eecSWilly Tu toPack[0].value = &first; 518ea2e6eecSWilly Tu // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 519ea2e6eecSWilly Tu packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...); 520ea2e6eecSWilly Tu } 521ea2e6eecSWilly Tu 522ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes> 523b6164cbeSEd Tanous bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res, 524b6164cbeSEd Tanous std::string_view key, FirstType&& first, 525b6164cbeSEd Tanous UnpackTypes&&... in) 526ea2e6eecSWilly Tu { 527ea2e6eecSWilly Tu const std::size_t n = sizeof...(UnpackTypes) + 2; 528ea2e6eecSWilly Tu std::array<PerUnpack, n / 2> toUnpack2; 529ea2e6eecSWilly Tu packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...); 530b6164cbeSEd Tanous return readJsonHelperObject(jsonRequest, res, toUnpack2); 531ea2e6eecSWilly Tu } 532ea2e6eecSWilly Tu 533b6164cbeSEd Tanous template <typename FirstType, typename... UnpackTypes> 534b6164cbeSEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res, 535b6164cbeSEd Tanous std::string_view key, FirstType&& first, UnpackTypes&&... in) 536b6164cbeSEd Tanous { 537b6164cbeSEd Tanous nlohmann::json::object_t* obj = 538b6164cbeSEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 539b6164cbeSEd Tanous if (obj == nullptr) 540b6164cbeSEd Tanous { 541b6164cbeSEd Tanous BMCWEB_LOG_DEBUG("Json value is not an object"); 542b6164cbeSEd Tanous messages::unrecognizedRequestBody(res); 543b6164cbeSEd Tanous return false; 544b6164cbeSEd Tanous } 545*5be2b14aSEd Tanous return readJsonObject(*obj, res, key, std::forward<FirstType>(first), 546*5be2b14aSEd Tanous std::forward<UnpackTypes&&>(in)...); 547b6164cbeSEd Tanous } 548b6164cbeSEd Tanous 549b6164cbeSEd Tanous inline std::optional<nlohmann::json::object_t> 550ea2e6eecSWilly Tu readJsonPatchHelper(const crow::Request& req, crow::Response& res) 5510627a2c7SEd Tanous { 5520627a2c7SEd Tanous nlohmann::json jsonRequest; 5530627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 5540627a2c7SEd Tanous { 55562598e31SEd Tanous BMCWEB_LOG_DEBUG("Json value not readable"); 556ea2e6eecSWilly Tu return std::nullopt; 5570627a2c7SEd Tanous } 558357bb8f8SEd Tanous nlohmann::json::object_t* object = 559357bb8f8SEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 560357bb8f8SEd Tanous if (object == nullptr || object->empty()) 56115ed6780SWilly Tu { 56262598e31SEd Tanous BMCWEB_LOG_DEBUG("Json value is empty"); 56315ed6780SWilly Tu messages::emptyJSON(res); 564ea2e6eecSWilly Tu return std::nullopt; 565ea2e6eecSWilly Tu } 566357bb8f8SEd Tanous std::erase_if(*object, 567357bb8f8SEd Tanous [](const std::pair<std::string, nlohmann::json>& item) { 568357bb8f8SEd Tanous return item.first.starts_with("@odata."); 569357bb8f8SEd Tanous }); 570357bb8f8SEd Tanous if (object->empty()) 571357bb8f8SEd Tanous { 572357bb8f8SEd Tanous // If the update request only contains OData annotations, the service 573357bb8f8SEd Tanous // should return the HTTP 400 Bad Request status code with the 574357bb8f8SEd Tanous // NoOperation message from the Base Message Registry, ... 575357bb8f8SEd Tanous messages::noOperation(res); 576357bb8f8SEd Tanous return std::nullopt; 577357bb8f8SEd Tanous } 578357bb8f8SEd Tanous 579b6164cbeSEd Tanous return {std::move(*object)}; 580ea2e6eecSWilly Tu } 581ea2e6eecSWilly Tu 582ea2e6eecSWilly Tu template <typename... UnpackTypes> 583ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res, 584ea2e6eecSWilly Tu std::string_view key, UnpackTypes&&... in) 585ea2e6eecSWilly Tu { 586ea2e6eecSWilly Tu std::optional<nlohmann::json> jsonRequest = readJsonPatchHelper(req, res); 587e01d0c36SEd Tanous if (!jsonRequest) 588ea2e6eecSWilly Tu { 58915ed6780SWilly Tu return false; 59015ed6780SWilly Tu } 591b6164cbeSEd Tanous nlohmann::json::object_t* object = 592b6164cbeSEd Tanous jsonRequest->get_ptr<nlohmann::json::object_t*>(); 593b6164cbeSEd Tanous if (object == nullptr) 594b6164cbeSEd Tanous { 595b6164cbeSEd Tanous BMCWEB_LOG_DEBUG("Json value is empty"); 596b6164cbeSEd Tanous messages::emptyJSON(res); 597b6164cbeSEd Tanous return false; 598b6164cbeSEd Tanous } 59915ed6780SWilly Tu 600b6164cbeSEd Tanous return readJsonObject(*object, res, key, 601b6164cbeSEd Tanous std::forward<UnpackTypes&&>(in)...); 60215ed6780SWilly Tu } 60315ed6780SWilly Tu 60415ed6780SWilly Tu template <typename... UnpackTypes> 60515ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res, 606ea2e6eecSWilly Tu const char* key, UnpackTypes&&... in) 60715ed6780SWilly Tu { 60815ed6780SWilly Tu nlohmann::json jsonRequest; 60915ed6780SWilly Tu if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 61015ed6780SWilly Tu { 61162598e31SEd Tanous BMCWEB_LOG_DEBUG("Json value not readable"); 61215ed6780SWilly Tu return false; 61315ed6780SWilly Tu } 614b6164cbeSEd Tanous nlohmann::json::object_t* object = 615b6164cbeSEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 616b6164cbeSEd Tanous if (object == nullptr) 617b6164cbeSEd Tanous { 618b6164cbeSEd Tanous BMCWEB_LOG_DEBUG("Json value is empty"); 619b6164cbeSEd Tanous messages::emptyJSON(res); 620b6164cbeSEd Tanous return false; 621b6164cbeSEd Tanous } 622b6164cbeSEd Tanous return readJsonObject(*object, res, key, 623b6164cbeSEd Tanous std::forward<UnpackTypes&&>(in)...); 6240627a2c7SEd Tanous } 625185444b1SNan Zhou 626185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the 627185444b1SNan Zhou // @odata.id key 628185444b1SNan Zhou inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b) 629185444b1SNan Zhou { 630185444b1SNan Zhou using object_t = nlohmann::json::object_t; 631185444b1SNan Zhou const object_t* aObj = a.get_ptr<const object_t*>(); 632185444b1SNan Zhou const object_t* bObj = b.get_ptr<const object_t*>(); 633185444b1SNan Zhou 634185444b1SNan Zhou if (aObj == nullptr) 635185444b1SNan Zhou { 636185444b1SNan Zhou if (bObj == nullptr) 637185444b1SNan Zhou { 638185444b1SNan Zhou return 0; 639185444b1SNan Zhou } 640185444b1SNan Zhou return -1; 641185444b1SNan Zhou } 642185444b1SNan Zhou if (bObj == nullptr) 643185444b1SNan Zhou { 644185444b1SNan Zhou return 1; 645185444b1SNan Zhou } 646185444b1SNan Zhou object_t::const_iterator aIt = aObj->find("@odata.id"); 647185444b1SNan Zhou object_t::const_iterator bIt = bObj->find("@odata.id"); 648185444b1SNan Zhou // If either object doesn't have the key, they get "sorted" to the end. 649185444b1SNan Zhou if (aIt == aObj->end()) 650185444b1SNan Zhou { 651185444b1SNan Zhou if (bIt == bObj->end()) 652185444b1SNan Zhou { 653185444b1SNan Zhou return 0; 654185444b1SNan Zhou } 655185444b1SNan Zhou return -1; 656185444b1SNan Zhou } 657185444b1SNan Zhou if (bIt == bObj->end()) 658185444b1SNan Zhou { 659185444b1SNan Zhou return 1; 660185444b1SNan Zhou } 661185444b1SNan Zhou const nlohmann::json::string_t* nameA = 662185444b1SNan Zhou aIt->second.get_ptr<const std::string*>(); 663185444b1SNan Zhou const nlohmann::json::string_t* nameB = 664185444b1SNan Zhou bIt->second.get_ptr<const std::string*>(); 665185444b1SNan Zhou // If either object doesn't have a string as the key, they get "sorted" to 666185444b1SNan Zhou // the end. 667185444b1SNan Zhou if (nameA == nullptr) 668185444b1SNan Zhou { 669185444b1SNan Zhou if (nameB == nullptr) 670185444b1SNan Zhou { 671185444b1SNan Zhou return 0; 672185444b1SNan Zhou } 673185444b1SNan Zhou return -1; 674185444b1SNan Zhou } 675185444b1SNan Zhou if (nameB == nullptr) 676185444b1SNan Zhou { 677185444b1SNan Zhou return 1; 678185444b1SNan Zhou } 679185444b1SNan Zhou boost::urls::url_view aUrl(*nameA); 680185444b1SNan Zhou boost::urls::url_view bUrl(*nameB); 681185444b1SNan Zhou auto segmentsAIt = aUrl.segments().begin(); 682185444b1SNan Zhou auto segmentsBIt = bUrl.segments().begin(); 683185444b1SNan Zhou 684185444b1SNan Zhou while (true) 685185444b1SNan Zhou { 686185444b1SNan Zhou if (segmentsAIt == aUrl.segments().end()) 687185444b1SNan Zhou { 688185444b1SNan Zhou if (segmentsBIt == bUrl.segments().end()) 689185444b1SNan Zhou { 690185444b1SNan Zhou return 0; 691185444b1SNan Zhou } 692185444b1SNan Zhou return -1; 693185444b1SNan Zhou } 694185444b1SNan Zhou if (segmentsBIt == bUrl.segments().end()) 695185444b1SNan Zhou { 696185444b1SNan Zhou return 1; 697185444b1SNan Zhou } 698185444b1SNan Zhou int res = alphanumComp(*segmentsAIt, *segmentsBIt); 699185444b1SNan Zhou if (res != 0) 700185444b1SNan Zhou { 701185444b1SNan Zhou return res; 702185444b1SNan Zhou } 703185444b1SNan Zhou 704185444b1SNan Zhou segmentsAIt++; 705185444b1SNan Zhou segmentsBIt++; 706185444b1SNan Zhou } 707185444b1SNan Zhou }; 708185444b1SNan Zhou 709185444b1SNan Zhou struct ODataObjectLess 710185444b1SNan Zhou { 711185444b1SNan Zhou bool operator()(const nlohmann::json& left, 712185444b1SNan Zhou const nlohmann::json& right) const 713185444b1SNan Zhou { 714185444b1SNan Zhou return odataObjectCmp(left, right) < 0; 715185444b1SNan Zhou } 716185444b1SNan Zhou }; 717185444b1SNan Zhou 718185444b1SNan Zhou // Sort the JSON array by |element[key]|. 719185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller 720185444b1SNan Zhou // those whose |element[key]| is string. 721185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array) 722185444b1SNan Zhou { 7233544d2a7SEd Tanous std::ranges::sort(array, ODataObjectLess()); 724185444b1SNan Zhou } 725185444b1SNan Zhou 7268a7c4b47SNan Zhou // Returns the estimated size of the JSON value 7278a7c4b47SNan Zhou // The implementation walks through every key and every value, accumulates the 7288a7c4b47SNan Zhou // total size of keys and values. 7298a7c4b47SNan Zhou // Ideally, we should use a custom allocator that nlohmann JSON supports. 7308a7c4b47SNan Zhou 7318a7c4b47SNan Zhou // Assumption made: 7328a7c4b47SNan Zhou // 1. number: 8 characters 7338a7c4b47SNan Zhou // 2. boolean: 5 characters (False) 7348a7c4b47SNan Zhou // 3. string: len(str) + 2 characters (quote) 7358a7c4b47SNan Zhou // 4. bytes: len(bytes) characters 7368a7c4b47SNan Zhou // 5. null: 4 characters (null) 7378a7c4b47SNan Zhou uint64_t getEstimatedJsonSize(const nlohmann::json& root); 7388a7c4b47SNan Zhou 73977dd8813SKowalski, Kamil } // namespace json_util 74077dd8813SKowalski, Kamil } // namespace redfish 741