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" 19d5c80ad9SNan Zhou #include "http_request.hpp" 20d5c80ad9SNan Zhou #include "http_response.hpp" 21d5c80ad9SNan Zhou #include "logging.hpp" 22d5c80ad9SNan Zhou #include "nlohmann/json.hpp" 230627a2c7SEd Tanous 24d5c80ad9SNan Zhou #include <array> 25d5c80ad9SNan Zhou #include <cmath> 26d5c80ad9SNan Zhou #include <cstddef> 27d5c80ad9SNan Zhou #include <cstdint> 28d5c80ad9SNan Zhou #include <limits> 29d5c80ad9SNan Zhou #include <map> 30d5c80ad9SNan Zhou #include <optional> 31ea2e6eecSWilly Tu #include <span> 32d5c80ad9SNan Zhou #include <string> 33d5c80ad9SNan Zhou #include <string_view> 34d5c80ad9SNan Zhou #include <type_traits> 35d5c80ad9SNan Zhou #include <utility> 36d5c80ad9SNan Zhou #include <variant> 37d5c80ad9SNan Zhou #include <vector> 38d5c80ad9SNan Zhou 39d5c80ad9SNan Zhou // IWYU pragma: no_include <stdint.h> 401e75e1ddSNan Zhou // IWYU pragma: no_forward_declare crow::Request 411214b7e7SGunnar Mills 421abe55efSEd Tanous namespace redfish 431abe55efSEd Tanous { 441abe55efSEd Tanous 451abe55efSEd Tanous namespace json_util 461abe55efSEd Tanous { 4777dd8813SKowalski, Kamil 4877dd8813SKowalski, Kamil /** 4977dd8813SKowalski, Kamil * @brief Processes request to extract JSON from its body. If it fails, adds 5077dd8813SKowalski, Kamil * MalformedJSON message to response and ends it. 5177dd8813SKowalski, Kamil * 5277dd8813SKowalski, Kamil * @param[io] res Response object 5377dd8813SKowalski, Kamil * @param[in] req Request object 5477dd8813SKowalski, Kamil * @param[out] reqJson JSON object extracted from request's body 5577dd8813SKowalski, Kamil * 5677dd8813SKowalski, Kamil * @return true if JSON is valid, false when JSON is invalid and response has 5777dd8813SKowalski, Kamil * been filled with message and ended. 5877dd8813SKowalski, Kamil */ 5955c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 6077dd8813SKowalski, Kamil nlohmann::json& reqJson); 619712f8acSEd Tanous namespace details 629712f8acSEd Tanous { 63771cfa0fSJason M. Bills 641214b7e7SGunnar Mills template <typename Type> 652c70f800SEd Tanous struct IsOptional : std::false_type 661214b7e7SGunnar Mills {}; 679712f8acSEd Tanous 68771cfa0fSJason M. Bills template <typename Type> 692c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type 701214b7e7SGunnar Mills {}; 719712f8acSEd Tanous 72771cfa0fSJason M. Bills template <typename Type> 732c70f800SEd Tanous struct IsVector : std::false_type 741214b7e7SGunnar Mills {}; 75b1556427SEd Tanous 761214b7e7SGunnar Mills template <typename Type> 772c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type 781214b7e7SGunnar Mills {}; 79b1556427SEd Tanous 801214b7e7SGunnar Mills template <typename Type> 812c70f800SEd Tanous struct IsStdArray : std::false_type 821214b7e7SGunnar Mills {}; 83318226c2SJames Feist 84318226c2SJames Feist template <typename Type, std::size_t size> 852c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type 861214b7e7SGunnar Mills {}; 87318226c2SJames Feist 88471a5eb8SAppaRao Puli enum class UnpackErrorCode 89471a5eb8SAppaRao Puli { 90471a5eb8SAppaRao Puli success, 91471a5eb8SAppaRao Puli invalidType, 92471a5eb8SAppaRao Puli outOfRange 93471a5eb8SAppaRao Puli }; 94471a5eb8SAppaRao Puli 95a6acbb31SJames Feist template <typename ToType, typename FromType> 96ea2e6eecSWilly Tu bool checkRange(const FromType& from, std::string_view key) 97a6acbb31SJames Feist { 98ee344e0fSEd Tanous if (from > std::numeric_limits<ToType>::max()) 99a6acbb31SJames Feist { 100a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 101a6acbb31SJames Feist << " was greater than max: " << __PRETTY_FUNCTION__; 102a6acbb31SJames Feist return false; 103a6acbb31SJames Feist } 104ee344e0fSEd Tanous if (from < std::numeric_limits<ToType>::lowest()) 105a6acbb31SJames Feist { 106a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 107a6acbb31SJames Feist << " was less than min: " << __PRETTY_FUNCTION__; 108a6acbb31SJames Feist return false; 109a6acbb31SJames Feist } 110a6acbb31SJames Feist if constexpr (std::is_floating_point_v<ToType>) 111a6acbb31SJames Feist { 112ee344e0fSEd Tanous if (std::isnan(from)) 113a6acbb31SJames Feist { 114a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key << " was NAN"; 115a6acbb31SJames Feist return false; 116a6acbb31SJames Feist } 117a6acbb31SJames Feist } 118a6acbb31SJames Feist 119a6acbb31SJames Feist return true; 120a6acbb31SJames Feist } 121a6acbb31SJames Feist 122771cfa0fSJason M. Bills template <typename Type> 123471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 124ea2e6eecSWilly Tu std::string_view key, Type& value) 125771cfa0fSJason M. Bills { 126471a5eb8SAppaRao Puli UnpackErrorCode ret = UnpackErrorCode::success; 12741352c24SSantosh Puranik 128a6acbb31SJames Feist if constexpr (std::is_floating_point_v<Type>) 129771cfa0fSJason M. Bills { 130a6acbb31SJames Feist double helper = 0; 131a6acbb31SJames Feist double* jsonPtr = jsonValue.get_ptr<double*>(); 132771cfa0fSJason M. Bills 133771cfa0fSJason M. Bills if (jsonPtr == nullptr) 134771cfa0fSJason M. Bills { 135a6acbb31SJames Feist int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 136a6acbb31SJames Feist if (intPtr != nullptr) 137771cfa0fSJason M. Bills { 138a6acbb31SJames Feist helper = static_cast<double>(*intPtr); 139a6acbb31SJames Feist jsonPtr = &helper; 140771cfa0fSJason M. Bills } 141a6acbb31SJames Feist } 1425eb2bef2SAppaRao Puli if (jsonPtr == nullptr) 1435eb2bef2SAppaRao Puli { 144471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 1455eb2bef2SAppaRao Puli } 146cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 147771cfa0fSJason M. Bills { 148471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 149771cfa0fSJason M. Bills } 150771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 151771cfa0fSJason M. Bills } 152a6acbb31SJames Feist 153a6acbb31SJames Feist else if constexpr (std::is_signed_v<Type>) 154a6acbb31SJames Feist { 155a6acbb31SJames Feist int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 156271584abSEd Tanous if (jsonPtr == nullptr) 157271584abSEd Tanous { 158471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 159271584abSEd Tanous } 160cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 161a6acbb31SJames Feist { 162471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 163a6acbb31SJames Feist } 164a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 165a6acbb31SJames Feist } 166a6acbb31SJames Feist 1678102ddbaSAppaRao Puli else if constexpr ((std::is_unsigned_v<Type>)&&( 1688102ddbaSAppaRao Puli !std::is_same_v<bool, Type>)) 169a6acbb31SJames Feist { 170a6acbb31SJames Feist uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 171271584abSEd Tanous if (jsonPtr == nullptr) 172271584abSEd Tanous { 173471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 174271584abSEd Tanous } 175cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 176a6acbb31SJames Feist { 177471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 178a6acbb31SJames Feist } 179a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 180a6acbb31SJames Feist } 181a6acbb31SJames Feist 1820627a2c7SEd Tanous else if constexpr (std::is_same_v<nlohmann::json, Type>) 1830627a2c7SEd Tanous { 1840627a2c7SEd Tanous value = std::move(jsonValue); 1850627a2c7SEd Tanous } 186471a5eb8SAppaRao Puli else 187471a5eb8SAppaRao Puli { 188471a5eb8SAppaRao Puli using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 189471a5eb8SAppaRao Puli JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 190471a5eb8SAppaRao Puli if (jsonPtr == nullptr) 191471a5eb8SAppaRao Puli { 192471a5eb8SAppaRao Puli BMCWEB_LOG_DEBUG 193471a5eb8SAppaRao Puli << "Value for key " << key 194471a5eb8SAppaRao Puli << " was incorrect type: " << jsonValue.type_name(); 195471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 196471a5eb8SAppaRao Puli } 197471a5eb8SAppaRao Puli value = std::move(*jsonPtr); 198471a5eb8SAppaRao Puli } 199471a5eb8SAppaRao Puli return ret; 200471a5eb8SAppaRao Puli } 201471a5eb8SAppaRao Puli 202471a5eb8SAppaRao Puli template <typename Type> 203ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, 204471a5eb8SAppaRao Puli crow::Response& res, Type& value) 205471a5eb8SAppaRao Puli { 206471a5eb8SAppaRao Puli bool ret = true; 207471a5eb8SAppaRao Puli 2082c70f800SEd Tanous if constexpr (IsOptional<Type>::value) 209471a5eb8SAppaRao Puli { 210471a5eb8SAppaRao Puli value.emplace(); 211471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 212471a5eb8SAppaRao Puli *value) && 213471a5eb8SAppaRao Puli ret; 214471a5eb8SAppaRao Puli } 2152c70f800SEd Tanous else if constexpr (IsStdArray<Type>::value) 216318226c2SJames Feist { 217318226c2SJames Feist if (!jsonValue.is_array()) 218318226c2SJames Feist { 21971f52d96SEd Tanous messages::propertyValueTypeError( 22071f52d96SEd Tanous res, 22171f52d96SEd Tanous res.jsonValue.dump(2, ' ', true, 22271f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 22371f52d96SEd Tanous key); 22441352c24SSantosh Puranik return false; 225318226c2SJames Feist } 226318226c2SJames Feist if (jsonValue.size() != value.size()) 227318226c2SJames Feist { 22871f52d96SEd Tanous messages::propertyValueTypeError( 22971f52d96SEd Tanous res, 23071f52d96SEd Tanous res.jsonValue.dump(2, ' ', true, 23171f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 23271f52d96SEd Tanous key); 23341352c24SSantosh Puranik return false; 234318226c2SJames Feist } 235318226c2SJames Feist size_t index = 0; 236318226c2SJames Feist for (const auto& val : jsonValue.items()) 237318226c2SJames Feist { 23841352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 23941352c24SSantosh Puranik value[index++]) && 24041352c24SSantosh Puranik ret; 241318226c2SJames Feist } 242318226c2SJames Feist } 2432c70f800SEd Tanous else if constexpr (IsVector<Type>::value) 244b1556427SEd Tanous { 245b1556427SEd Tanous if (!jsonValue.is_array()) 246b1556427SEd Tanous { 24771f52d96SEd Tanous messages::propertyValueTypeError( 24871f52d96SEd Tanous res, 24971f52d96SEd Tanous res.jsonValue.dump(2, ' ', true, 25071f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 25171f52d96SEd Tanous key); 25241352c24SSantosh Puranik return false; 253b1556427SEd Tanous } 254b1556427SEd Tanous 255b1556427SEd Tanous for (const auto& val : jsonValue.items()) 256b1556427SEd Tanous { 257b1556427SEd Tanous value.emplace_back(); 25841352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 25941352c24SSantosh Puranik value.back()) && 26041352c24SSantosh Puranik ret; 261b1556427SEd Tanous } 262b1556427SEd Tanous } 263771cfa0fSJason M. Bills else 264771cfa0fSJason M. Bills { 265471a5eb8SAppaRao Puli UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 266471a5eb8SAppaRao Puli if (ec != UnpackErrorCode::success) 267771cfa0fSJason M. Bills { 268471a5eb8SAppaRao Puli if (ec == UnpackErrorCode::invalidType) 269471a5eb8SAppaRao Puli { 27071f52d96SEd Tanous messages::propertyValueTypeError( 27171f52d96SEd Tanous res, 27271f52d96SEd Tanous jsonValue.dump(2, ' ', true, 27371f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 27471f52d96SEd Tanous key); 275471a5eb8SAppaRao Puli } 276471a5eb8SAppaRao Puli else if (ec == UnpackErrorCode::outOfRange) 277471a5eb8SAppaRao Puli { 27871f52d96SEd Tanous messages::propertyValueNotInList( 27971f52d96SEd Tanous res, 28071f52d96SEd Tanous jsonValue.dump(2, ' ', true, 28171f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 28271f52d96SEd Tanous key); 283471a5eb8SAppaRao Puli } 28441352c24SSantosh Puranik return false; 285771cfa0fSJason M. Bills } 286771cfa0fSJason M. Bills } 287471a5eb8SAppaRao Puli 288471a5eb8SAppaRao Puli return ret; 289471a5eb8SAppaRao Puli } 290471a5eb8SAppaRao Puli 291471a5eb8SAppaRao Puli template <typename Type> 292ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value) 293471a5eb8SAppaRao Puli { 294471a5eb8SAppaRao Puli bool ret = true; 2952c70f800SEd Tanous if constexpr (IsOptional<Type>::value) 296471a5eb8SAppaRao Puli { 297471a5eb8SAppaRao Puli value.emplace(); 298471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) && 299471a5eb8SAppaRao Puli ret; 300471a5eb8SAppaRao Puli } 3012c70f800SEd Tanous else if constexpr (IsStdArray<Type>::value) 302471a5eb8SAppaRao Puli { 303471a5eb8SAppaRao Puli if (!jsonValue.is_array()) 304471a5eb8SAppaRao Puli { 305471a5eb8SAppaRao Puli return false; 306471a5eb8SAppaRao Puli } 307471a5eb8SAppaRao Puli if (jsonValue.size() != value.size()) 308471a5eb8SAppaRao Puli { 309471a5eb8SAppaRao Puli return false; 310471a5eb8SAppaRao Puli } 311471a5eb8SAppaRao Puli size_t index = 0; 312471a5eb8SAppaRao Puli for (const auto& val : jsonValue.items()) 313471a5eb8SAppaRao Puli { 314471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(val.value(), key, 315471a5eb8SAppaRao Puli value[index++]) && 316471a5eb8SAppaRao Puli ret; 317471a5eb8SAppaRao Puli } 318471a5eb8SAppaRao Puli } 3192c70f800SEd Tanous else if constexpr (IsVector<Type>::value) 320471a5eb8SAppaRao Puli { 321471a5eb8SAppaRao Puli if (!jsonValue.is_array()) 322471a5eb8SAppaRao Puli { 323471a5eb8SAppaRao Puli return false; 324471a5eb8SAppaRao Puli } 325471a5eb8SAppaRao Puli 326471a5eb8SAppaRao Puli for (const auto& val : jsonValue.items()) 327471a5eb8SAppaRao Puli { 328471a5eb8SAppaRao Puli value.emplace_back(); 329471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(val.value(), key, 330471a5eb8SAppaRao Puli value.back()) && 331471a5eb8SAppaRao Puli ret; 332471a5eb8SAppaRao Puli } 333471a5eb8SAppaRao Puli } 334471a5eb8SAppaRao Puli else 335471a5eb8SAppaRao Puli { 336471a5eb8SAppaRao Puli UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 337471a5eb8SAppaRao Puli if (ec != UnpackErrorCode::success) 338471a5eb8SAppaRao Puli { 339471a5eb8SAppaRao Puli return false; 340471a5eb8SAppaRao Puli } 341471a5eb8SAppaRao Puli } 342471a5eb8SAppaRao Puli 34341352c24SSantosh Puranik return ret; 344771cfa0fSJason M. Bills } 3459712f8acSEd Tanous } // namespace details 3469712f8acSEd Tanous 347ea2e6eecSWilly Tu // clang-format off 348ea2e6eecSWilly Tu using UnpackVariant = std::variant< 349ea2e6eecSWilly Tu uint8_t*, 350ea2e6eecSWilly Tu uint16_t*, 351ea2e6eecSWilly Tu int16_t*, 352ea2e6eecSWilly Tu uint32_t*, 353ea2e6eecSWilly Tu int32_t*, 354ea2e6eecSWilly Tu uint64_t*, 355ea2e6eecSWilly Tu int64_t*, 356ea2e6eecSWilly Tu bool*, 357ea2e6eecSWilly Tu double*, 358ea2e6eecSWilly Tu std::string*, 359ea2e6eecSWilly Tu nlohmann::json*, 360ea2e6eecSWilly Tu std::vector<uint8_t>*, 361ea2e6eecSWilly Tu std::vector<uint16_t>*, 362ea2e6eecSWilly Tu std::vector<int16_t>*, 363ea2e6eecSWilly Tu std::vector<uint32_t>*, 364ea2e6eecSWilly Tu std::vector<int32_t>*, 365ea2e6eecSWilly Tu std::vector<uint64_t>*, 366ea2e6eecSWilly Tu std::vector<int64_t>*, 367ea2e6eecSWilly Tu //std::vector<bool>*, 368ea2e6eecSWilly Tu std::vector<double>*, 369ea2e6eecSWilly Tu std::vector<std::string>*, 370ea2e6eecSWilly Tu std::vector<nlohmann::json>*, 371ea2e6eecSWilly Tu std::optional<uint8_t>*, 372ea2e6eecSWilly Tu std::optional<uint16_t>*, 373ea2e6eecSWilly Tu std::optional<int16_t>*, 374ea2e6eecSWilly Tu std::optional<uint32_t>*, 375ea2e6eecSWilly Tu std::optional<int32_t>*, 376ea2e6eecSWilly Tu std::optional<uint64_t>*, 377ea2e6eecSWilly Tu std::optional<int64_t>*, 378ea2e6eecSWilly Tu std::optional<bool>*, 379ea2e6eecSWilly Tu std::optional<double>*, 380ea2e6eecSWilly Tu std::optional<std::string>*, 381ea2e6eecSWilly Tu std::optional<nlohmann::json>*, 382ea2e6eecSWilly Tu std::optional<std::vector<uint8_t>>*, 383ea2e6eecSWilly Tu std::optional<std::vector<uint16_t>>*, 384ea2e6eecSWilly Tu std::optional<std::vector<int16_t>>*, 385ea2e6eecSWilly Tu std::optional<std::vector<uint32_t>>*, 386ea2e6eecSWilly Tu std::optional<std::vector<int32_t>>*, 387ea2e6eecSWilly Tu std::optional<std::vector<uint64_t>>*, 388ea2e6eecSWilly Tu std::optional<std::vector<int64_t>>*, 389ea2e6eecSWilly Tu //std::optional<std::vector<bool>>*, 390ea2e6eecSWilly Tu std::optional<std::vector<double>>*, 391ea2e6eecSWilly Tu std::optional<std::vector<std::string>>*, 392ea2e6eecSWilly Tu std::optional<std::vector<nlohmann::json>>* 393ea2e6eecSWilly Tu >; 394ea2e6eecSWilly Tu // clang-format on 395ea2e6eecSWilly Tu 396ea2e6eecSWilly Tu struct PerUnpack 397ea2e6eecSWilly Tu { 398ea2e6eecSWilly Tu std::string_view key; 399ea2e6eecSWilly Tu UnpackVariant value; 400ea2e6eecSWilly Tu bool complete = false; 401ea2e6eecSWilly Tu }; 402ea2e6eecSWilly Tu 403ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 404ea2e6eecSWilly Tu std::span<PerUnpack> toUnpack) 4059712f8acSEd Tanous { 40641352c24SSantosh Puranik bool result = true; 407d91415c4SEd Tanous nlohmann::json::object_t* obj = 408d91415c4SEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 409d91415c4SEd Tanous if (obj == nullptr) 4109712f8acSEd Tanous { 4119712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 412f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 4139712f8acSEd Tanous return false; 4149712f8acSEd Tanous } 415d91415c4SEd Tanous for (auto& item : *obj) 4169712f8acSEd Tanous { 417ea2e6eecSWilly Tu size_t unpackIndex = 0; 418ea2e6eecSWilly Tu for (; unpackIndex < toUnpack.size(); unpackIndex++) 419ea2e6eecSWilly Tu { 420ea2e6eecSWilly Tu PerUnpack& unpackSpec = toUnpack[unpackIndex]; 421ea2e6eecSWilly Tu std::string_view key = unpackSpec.key; 422ea2e6eecSWilly Tu size_t keysplitIndex = key.find('/'); 423ea2e6eecSWilly Tu std::string_view leftover; 424ea2e6eecSWilly Tu if (keysplitIndex != std::string_view::npos) 425ea2e6eecSWilly Tu { 426ea2e6eecSWilly Tu leftover = key.substr(keysplitIndex + 1); 427ea2e6eecSWilly Tu key = key.substr(0, keysplitIndex); 428ea2e6eecSWilly Tu } 429ea2e6eecSWilly Tu 430d91415c4SEd Tanous if (key != item.first || unpackSpec.complete) 431ea2e6eecSWilly Tu { 432ea2e6eecSWilly Tu continue; 433ea2e6eecSWilly Tu } 434ea2e6eecSWilly Tu 435ea2e6eecSWilly Tu // Sublevel key 436ea2e6eecSWilly Tu if (!leftover.empty()) 437ea2e6eecSWilly Tu { 438ea2e6eecSWilly Tu // Include the slash in the key so we can compare later 439ea2e6eecSWilly Tu key = unpackSpec.key.substr(0, keysplitIndex + 1); 440ea2e6eecSWilly Tu nlohmann::json j; 441d91415c4SEd Tanous result = details::unpackValue<nlohmann::json>(item.second, key, 442ea2e6eecSWilly Tu res, j) && 44341352c24SSantosh Puranik result; 44455f79e6fSEd Tanous if (!result) 445ea2e6eecSWilly Tu { 446ea2e6eecSWilly Tu return result; 4479712f8acSEd Tanous } 4489712f8acSEd Tanous 449ea2e6eecSWilly Tu std::vector<PerUnpack> nextLevel; 450ea2e6eecSWilly Tu for (PerUnpack& p : toUnpack) 451ea2e6eecSWilly Tu { 452ea2e6eecSWilly Tu if (!p.key.starts_with(key)) 453ea2e6eecSWilly Tu { 454ea2e6eecSWilly Tu continue; 455ea2e6eecSWilly Tu } 456ea2e6eecSWilly Tu std::string_view thisLeftover = p.key.substr(key.size()); 457ea2e6eecSWilly Tu nextLevel.push_back({thisLeftover, p.value, false}); 458ea2e6eecSWilly Tu p.complete = true; 4599712f8acSEd Tanous } 46077dd8813SKowalski, Kamil 461ea2e6eecSWilly Tu result = readJsonHelper(j, res, nextLevel) && result; 462ea2e6eecSWilly Tu break; 463ea2e6eecSWilly Tu } 464ea2e6eecSWilly Tu 465002d39b4SEd Tanous result = std::visit( 466ea2e6eecSWilly Tu [&item, &unpackSpec, &res](auto&& val) { 467ea2e6eecSWilly Tu using ContainedT = 468ea2e6eecSWilly Tu std::remove_pointer_t<std::decay_t<decltype(val)>>; 469ea2e6eecSWilly Tu return details::unpackValue<ContainedT>( 470d91415c4SEd Tanous item.second, unpackSpec.key, res, *val); 471ea2e6eecSWilly Tu }, 472ea2e6eecSWilly Tu unpackSpec.value) && 473ea2e6eecSWilly Tu result; 474ea2e6eecSWilly Tu 475ea2e6eecSWilly Tu unpackSpec.complete = true; 476ea2e6eecSWilly Tu break; 477ea2e6eecSWilly Tu } 478ea2e6eecSWilly Tu 479ea2e6eecSWilly Tu if (unpackIndex == toUnpack.size()) 480ea2e6eecSWilly Tu { 481d91415c4SEd Tanous messages::propertyUnknown(res, item.first); 482ea2e6eecSWilly Tu result = false; 483ea2e6eecSWilly Tu } 484ea2e6eecSWilly Tu } 485ea2e6eecSWilly Tu 486ea2e6eecSWilly Tu for (PerUnpack& perUnpack : toUnpack) 487ea2e6eecSWilly Tu { 48855f79e6fSEd Tanous if (!perUnpack.complete) 489ea2e6eecSWilly Tu { 490ea2e6eecSWilly Tu bool isOptional = std::visit( 491ea2e6eecSWilly Tu [](auto&& val) { 492ea2e6eecSWilly Tu using ContainedType = 493ea2e6eecSWilly Tu std::remove_pointer_t<std::decay_t<decltype(val)>>; 494ea2e6eecSWilly Tu return details::IsOptional<ContainedType>::value; 495ea2e6eecSWilly Tu }, 496ea2e6eecSWilly Tu perUnpack.value); 497ea2e6eecSWilly Tu if (isOptional) 498ea2e6eecSWilly Tu { 499ea2e6eecSWilly Tu continue; 500ea2e6eecSWilly Tu } 501ea2e6eecSWilly Tu messages::propertyMissing(res, perUnpack.key); 502ea2e6eecSWilly Tu result = false; 503ea2e6eecSWilly Tu } 504ea2e6eecSWilly Tu } 505ea2e6eecSWilly Tu return result; 506ea2e6eecSWilly Tu } 507ea2e6eecSWilly Tu 508*89492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {} 509ea2e6eecSWilly Tu 510ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes> 511ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key, 512ea2e6eecSWilly Tu FirstType& first, UnpackTypes&&... in) 513ea2e6eecSWilly Tu { 514ea2e6eecSWilly Tu if (toPack.empty()) 515ea2e6eecSWilly Tu { 516ea2e6eecSWilly Tu return; 517ea2e6eecSWilly Tu } 518ea2e6eecSWilly Tu toPack[0].key = key; 519ea2e6eecSWilly Tu toPack[0].value = &first; 520ea2e6eecSWilly Tu // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 521ea2e6eecSWilly Tu packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...); 522ea2e6eecSWilly Tu } 523ea2e6eecSWilly Tu 524ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes> 525ea2e6eecSWilly Tu bool readJson(nlohmann::json& jsonRequest, crow::Response& res, 526ea2e6eecSWilly Tu std::string_view key, FirstType&& first, UnpackTypes&&... in) 527ea2e6eecSWilly Tu { 528ea2e6eecSWilly Tu const std::size_t n = sizeof...(UnpackTypes) + 2; 529ea2e6eecSWilly Tu std::array<PerUnpack, n / 2> toUnpack2; 530ea2e6eecSWilly Tu packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...); 531ea2e6eecSWilly Tu return readJsonHelper(jsonRequest, res, toUnpack2); 532ea2e6eecSWilly Tu } 533ea2e6eecSWilly Tu 534ea2e6eecSWilly Tu inline std::optional<nlohmann::json> 535ea2e6eecSWilly Tu readJsonPatchHelper(const crow::Request& req, crow::Response& res) 5360627a2c7SEd Tanous { 5370627a2c7SEd Tanous nlohmann::json jsonRequest; 5380627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 5390627a2c7SEd Tanous { 5400627a2c7SEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 541ea2e6eecSWilly Tu return std::nullopt; 5420627a2c7SEd Tanous } 543357bb8f8SEd Tanous nlohmann::json::object_t* object = 544357bb8f8SEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 545357bb8f8SEd Tanous if (object == nullptr || object->empty()) 54615ed6780SWilly Tu { 54715ed6780SWilly Tu BMCWEB_LOG_DEBUG << "Json value is empty"; 54815ed6780SWilly Tu messages::emptyJSON(res); 549ea2e6eecSWilly Tu return std::nullopt; 550ea2e6eecSWilly Tu } 551357bb8f8SEd Tanous std::erase_if(*object, 552357bb8f8SEd Tanous [](const std::pair<std::string, nlohmann::json>& item) { 553357bb8f8SEd Tanous return item.first.starts_with("@odata."); 554357bb8f8SEd Tanous }); 555357bb8f8SEd Tanous if (object->empty()) 556357bb8f8SEd Tanous { 557357bb8f8SEd Tanous // If the update request only contains OData annotations, the service 558357bb8f8SEd Tanous // should return the HTTP 400 Bad Request status code with the 559357bb8f8SEd Tanous // NoOperation message from the Base Message Registry, ... 560357bb8f8SEd Tanous messages::noOperation(res); 561357bb8f8SEd Tanous return std::nullopt; 562357bb8f8SEd Tanous } 563357bb8f8SEd Tanous 564ea2e6eecSWilly Tu return {std::move(jsonRequest)}; 565ea2e6eecSWilly Tu } 566ea2e6eecSWilly Tu 567ea2e6eecSWilly Tu template <typename... UnpackTypes> 568ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res, 569ea2e6eecSWilly Tu std::string_view key, UnpackTypes&&... in) 570ea2e6eecSWilly Tu { 571ea2e6eecSWilly Tu std::optional<nlohmann::json> jsonRequest = readJsonPatchHelper(req, res); 572ea2e6eecSWilly Tu if (jsonRequest == std::nullopt) 573ea2e6eecSWilly Tu { 57415ed6780SWilly Tu return false; 57515ed6780SWilly Tu } 57615ed6780SWilly Tu 577ea2e6eecSWilly Tu return readJson(*jsonRequest, res, key, std::forward<UnpackTypes&&>(in)...); 57815ed6780SWilly Tu } 57915ed6780SWilly Tu 58015ed6780SWilly Tu template <typename... UnpackTypes> 58115ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res, 582ea2e6eecSWilly Tu const char* key, UnpackTypes&&... in) 58315ed6780SWilly Tu { 58415ed6780SWilly Tu nlohmann::json jsonRequest; 58515ed6780SWilly Tu if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 58615ed6780SWilly Tu { 58715ed6780SWilly Tu BMCWEB_LOG_DEBUG << "Json value not readable"; 58815ed6780SWilly Tu return false; 58915ed6780SWilly Tu } 590ea2e6eecSWilly Tu return readJson(jsonRequest, res, key, std::forward<UnpackTypes&&>(in)...); 5910627a2c7SEd Tanous } 5920627a2c7SEd Tanous 59377dd8813SKowalski, Kamil } // namespace json_util 59477dd8813SKowalski, Kamil } // namespace redfish 595