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" 21*185444b1SNan Zhou #include "human_sort.hpp" 22d5c80ad9SNan Zhou #include "logging.hpp" 23faf100f9SEd Tanous 24faf100f9SEd Tanous #include <nlohmann/json.hpp> 250627a2c7SEd Tanous 26*185444b1SNan Zhou #include <algorithm> 27d5c80ad9SNan Zhou #include <array> 28d5c80ad9SNan Zhou #include <cmath> 29d5c80ad9SNan Zhou #include <cstddef> 30d5c80ad9SNan Zhou #include <cstdint> 31d5c80ad9SNan Zhou #include <limits> 32d5c80ad9SNan Zhou #include <map> 33d5c80ad9SNan Zhou #include <optional> 34ea2e6eecSWilly Tu #include <span> 35d5c80ad9SNan Zhou #include <string> 36d5c80ad9SNan Zhou #include <string_view> 37d5c80ad9SNan Zhou #include <type_traits> 38d5c80ad9SNan Zhou #include <utility> 39d5c80ad9SNan Zhou #include <variant> 40d5c80ad9SNan Zhou #include <vector> 41d5c80ad9SNan Zhou 42d5c80ad9SNan Zhou // IWYU pragma: no_include <stdint.h> 431e75e1ddSNan Zhou // IWYU pragma: no_forward_declare crow::Request 441214b7e7SGunnar Mills 451abe55efSEd Tanous namespace redfish 461abe55efSEd Tanous { 471abe55efSEd Tanous 481abe55efSEd Tanous namespace json_util 491abe55efSEd Tanous { 5077dd8813SKowalski, Kamil 5177dd8813SKowalski, Kamil /** 5277dd8813SKowalski, Kamil * @brief Processes request to extract JSON from its body. If it fails, adds 5377dd8813SKowalski, Kamil * MalformedJSON message to response and ends it. 5477dd8813SKowalski, Kamil * 5577dd8813SKowalski, Kamil * @param[io] res Response object 5677dd8813SKowalski, Kamil * @param[in] req Request object 5777dd8813SKowalski, Kamil * @param[out] reqJson JSON object extracted from request's body 5877dd8813SKowalski, Kamil * 5977dd8813SKowalski, Kamil * @return true if JSON is valid, false when JSON is invalid and response has 6077dd8813SKowalski, Kamil * been filled with message and ended. 6177dd8813SKowalski, Kamil */ 6255c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req, 6377dd8813SKowalski, Kamil nlohmann::json& reqJson); 649712f8acSEd Tanous namespace details 659712f8acSEd Tanous { 66771cfa0fSJason M. Bills 671214b7e7SGunnar Mills template <typename Type> 682c70f800SEd Tanous struct IsOptional : std::false_type 691214b7e7SGunnar Mills {}; 709712f8acSEd Tanous 71771cfa0fSJason M. Bills template <typename Type> 722c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type 731214b7e7SGunnar Mills {}; 749712f8acSEd Tanous 75771cfa0fSJason M. Bills template <typename Type> 762c70f800SEd Tanous struct IsVector : std::false_type 771214b7e7SGunnar Mills {}; 78b1556427SEd Tanous 791214b7e7SGunnar Mills template <typename Type> 802c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type 811214b7e7SGunnar Mills {}; 82b1556427SEd Tanous 831214b7e7SGunnar Mills template <typename Type> 842c70f800SEd Tanous struct IsStdArray : std::false_type 851214b7e7SGunnar Mills {}; 86318226c2SJames Feist 87318226c2SJames Feist template <typename Type, std::size_t size> 882c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type 891214b7e7SGunnar Mills {}; 90318226c2SJames Feist 91471a5eb8SAppaRao Puli enum class UnpackErrorCode 92471a5eb8SAppaRao Puli { 93471a5eb8SAppaRao Puli success, 94471a5eb8SAppaRao Puli invalidType, 95471a5eb8SAppaRao Puli outOfRange 96471a5eb8SAppaRao Puli }; 97471a5eb8SAppaRao Puli 98a6acbb31SJames Feist template <typename ToType, typename FromType> 99ea2e6eecSWilly Tu bool checkRange(const FromType& from, std::string_view key) 100a6acbb31SJames Feist { 101ee344e0fSEd Tanous if (from > std::numeric_limits<ToType>::max()) 102a6acbb31SJames Feist { 103a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 104a6acbb31SJames Feist << " was greater than max: " << __PRETTY_FUNCTION__; 105a6acbb31SJames Feist return false; 106a6acbb31SJames Feist } 107ee344e0fSEd Tanous if (from < std::numeric_limits<ToType>::lowest()) 108a6acbb31SJames Feist { 109a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key 110a6acbb31SJames Feist << " was less than min: " << __PRETTY_FUNCTION__; 111a6acbb31SJames Feist return false; 112a6acbb31SJames Feist } 113a6acbb31SJames Feist if constexpr (std::is_floating_point_v<ToType>) 114a6acbb31SJames Feist { 115ee344e0fSEd Tanous if (std::isnan(from)) 116a6acbb31SJames Feist { 117a6acbb31SJames Feist BMCWEB_LOG_DEBUG << "Value for key " << key << " was NAN"; 118a6acbb31SJames Feist return false; 119a6acbb31SJames Feist } 120a6acbb31SJames Feist } 121a6acbb31SJames Feist 122a6acbb31SJames Feist return true; 123a6acbb31SJames Feist } 124a6acbb31SJames Feist 125771cfa0fSJason M. Bills template <typename Type> 126471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue, 127ea2e6eecSWilly Tu std::string_view key, Type& value) 128771cfa0fSJason M. Bills { 129471a5eb8SAppaRao Puli UnpackErrorCode ret = UnpackErrorCode::success; 13041352c24SSantosh Puranik 131a6acbb31SJames Feist if constexpr (std::is_floating_point_v<Type>) 132771cfa0fSJason M. Bills { 133a6acbb31SJames Feist double helper = 0; 134a6acbb31SJames Feist double* jsonPtr = jsonValue.get_ptr<double*>(); 135771cfa0fSJason M. Bills 136771cfa0fSJason M. Bills if (jsonPtr == nullptr) 137771cfa0fSJason M. Bills { 138a6acbb31SJames Feist int64_t* intPtr = jsonValue.get_ptr<int64_t*>(); 139a6acbb31SJames Feist if (intPtr != nullptr) 140771cfa0fSJason M. Bills { 141a6acbb31SJames Feist helper = static_cast<double>(*intPtr); 142a6acbb31SJames Feist jsonPtr = &helper; 143771cfa0fSJason M. Bills } 144a6acbb31SJames Feist } 1455eb2bef2SAppaRao Puli if (jsonPtr == nullptr) 1465eb2bef2SAppaRao Puli { 147471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 1485eb2bef2SAppaRao Puli } 149cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 150771cfa0fSJason M. Bills { 151471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 152771cfa0fSJason M. Bills } 153771cfa0fSJason M. Bills value = static_cast<Type>(*jsonPtr); 154771cfa0fSJason M. Bills } 155a6acbb31SJames Feist 156a6acbb31SJames Feist else if constexpr (std::is_signed_v<Type>) 157a6acbb31SJames Feist { 158a6acbb31SJames Feist int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>(); 159271584abSEd Tanous if (jsonPtr == nullptr) 160271584abSEd Tanous { 161471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 162271584abSEd Tanous } 163cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 164a6acbb31SJames Feist { 165471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 166a6acbb31SJames Feist } 167a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 168a6acbb31SJames Feist } 169a6acbb31SJames Feist 1708102ddbaSAppaRao Puli else if constexpr ((std::is_unsigned_v<Type>)&&( 1718102ddbaSAppaRao Puli !std::is_same_v<bool, Type>)) 172a6acbb31SJames Feist { 173a6acbb31SJames Feist uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>(); 174271584abSEd Tanous if (jsonPtr == nullptr) 175271584abSEd Tanous { 176471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 177271584abSEd Tanous } 178cb13a392SEd Tanous if (!checkRange<Type>(*jsonPtr, key)) 179a6acbb31SJames Feist { 180471a5eb8SAppaRao Puli return UnpackErrorCode::outOfRange; 181a6acbb31SJames Feist } 182a6acbb31SJames Feist value = static_cast<Type>(*jsonPtr); 183a6acbb31SJames Feist } 184a6acbb31SJames Feist 1850627a2c7SEd Tanous else if constexpr (std::is_same_v<nlohmann::json, Type>) 1860627a2c7SEd Tanous { 1870627a2c7SEd Tanous value = std::move(jsonValue); 1880627a2c7SEd Tanous } 189471a5eb8SAppaRao Puli else 190471a5eb8SAppaRao Puli { 191471a5eb8SAppaRao Puli using JsonType = std::add_const_t<std::add_pointer_t<Type>>; 192471a5eb8SAppaRao Puli JsonType jsonPtr = jsonValue.get_ptr<JsonType>(); 193471a5eb8SAppaRao Puli if (jsonPtr == nullptr) 194471a5eb8SAppaRao Puli { 195471a5eb8SAppaRao Puli BMCWEB_LOG_DEBUG 196471a5eb8SAppaRao Puli << "Value for key " << key 197471a5eb8SAppaRao Puli << " was incorrect type: " << jsonValue.type_name(); 198471a5eb8SAppaRao Puli return UnpackErrorCode::invalidType; 199471a5eb8SAppaRao Puli } 200471a5eb8SAppaRao Puli value = std::move(*jsonPtr); 201471a5eb8SAppaRao Puli } 202471a5eb8SAppaRao Puli return ret; 203471a5eb8SAppaRao Puli } 204471a5eb8SAppaRao Puli 205471a5eb8SAppaRao Puli template <typename Type> 206ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, 207471a5eb8SAppaRao Puli crow::Response& res, Type& value) 208471a5eb8SAppaRao Puli { 209471a5eb8SAppaRao Puli bool ret = true; 210471a5eb8SAppaRao Puli 2112c70f800SEd Tanous if constexpr (IsOptional<Type>::value) 212471a5eb8SAppaRao Puli { 213471a5eb8SAppaRao Puli value.emplace(); 214471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(jsonValue, key, res, 215471a5eb8SAppaRao Puli *value) && 216471a5eb8SAppaRao Puli ret; 217471a5eb8SAppaRao Puli } 2182c70f800SEd Tanous else if constexpr (IsStdArray<Type>::value) 219318226c2SJames Feist { 220318226c2SJames Feist if (!jsonValue.is_array()) 221318226c2SJames Feist { 22271f52d96SEd Tanous messages::propertyValueTypeError( 22371f52d96SEd Tanous res, 22471f52d96SEd Tanous res.jsonValue.dump(2, ' ', true, 22571f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 22671f52d96SEd Tanous key); 22741352c24SSantosh Puranik return false; 228318226c2SJames Feist } 229318226c2SJames Feist if (jsonValue.size() != value.size()) 230318226c2SJames Feist { 23171f52d96SEd Tanous messages::propertyValueTypeError( 23271f52d96SEd Tanous res, 23371f52d96SEd Tanous res.jsonValue.dump(2, ' ', true, 23471f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 23571f52d96SEd Tanous key); 23641352c24SSantosh Puranik return false; 237318226c2SJames Feist } 238318226c2SJames Feist size_t index = 0; 239318226c2SJames Feist for (const auto& val : jsonValue.items()) 240318226c2SJames Feist { 24141352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 24241352c24SSantosh Puranik value[index++]) && 24341352c24SSantosh Puranik ret; 244318226c2SJames Feist } 245318226c2SJames Feist } 2462c70f800SEd Tanous else if constexpr (IsVector<Type>::value) 247b1556427SEd Tanous { 248b1556427SEd Tanous if (!jsonValue.is_array()) 249b1556427SEd Tanous { 25071f52d96SEd Tanous messages::propertyValueTypeError( 25171f52d96SEd Tanous res, 25271f52d96SEd Tanous res.jsonValue.dump(2, ' ', true, 25371f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 25471f52d96SEd Tanous key); 25541352c24SSantosh Puranik return false; 256b1556427SEd Tanous } 257b1556427SEd Tanous 258b1556427SEd Tanous for (const auto& val : jsonValue.items()) 259b1556427SEd Tanous { 260b1556427SEd Tanous value.emplace_back(); 26141352c24SSantosh Puranik ret = unpackValue<typename Type::value_type>(val.value(), key, res, 26241352c24SSantosh Puranik value.back()) && 26341352c24SSantosh Puranik ret; 264b1556427SEd Tanous } 265b1556427SEd Tanous } 266771cfa0fSJason M. Bills else 267771cfa0fSJason M. Bills { 268471a5eb8SAppaRao Puli UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 269471a5eb8SAppaRao Puli if (ec != UnpackErrorCode::success) 270771cfa0fSJason M. Bills { 271471a5eb8SAppaRao Puli if (ec == UnpackErrorCode::invalidType) 272471a5eb8SAppaRao Puli { 27371f52d96SEd Tanous messages::propertyValueTypeError( 27471f52d96SEd Tanous res, 27571f52d96SEd Tanous jsonValue.dump(2, ' ', true, 27671f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 27771f52d96SEd Tanous key); 278471a5eb8SAppaRao Puli } 279471a5eb8SAppaRao Puli else if (ec == UnpackErrorCode::outOfRange) 280471a5eb8SAppaRao Puli { 28171f52d96SEd Tanous messages::propertyValueNotInList( 28271f52d96SEd Tanous res, 28371f52d96SEd Tanous jsonValue.dump(2, ' ', true, 28471f52d96SEd Tanous nlohmann::json::error_handler_t::replace), 28571f52d96SEd Tanous key); 286471a5eb8SAppaRao Puli } 28741352c24SSantosh Puranik return false; 288771cfa0fSJason M. Bills } 289771cfa0fSJason M. Bills } 290471a5eb8SAppaRao Puli 291471a5eb8SAppaRao Puli return ret; 292471a5eb8SAppaRao Puli } 293471a5eb8SAppaRao Puli 294471a5eb8SAppaRao Puli template <typename Type> 295ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value) 296471a5eb8SAppaRao Puli { 297471a5eb8SAppaRao Puli bool ret = true; 2982c70f800SEd Tanous if constexpr (IsOptional<Type>::value) 299471a5eb8SAppaRao Puli { 300471a5eb8SAppaRao Puli value.emplace(); 301471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) && 302471a5eb8SAppaRao Puli ret; 303471a5eb8SAppaRao Puli } 3042c70f800SEd Tanous else if constexpr (IsStdArray<Type>::value) 305471a5eb8SAppaRao Puli { 306471a5eb8SAppaRao Puli if (!jsonValue.is_array()) 307471a5eb8SAppaRao Puli { 308471a5eb8SAppaRao Puli return false; 309471a5eb8SAppaRao Puli } 310471a5eb8SAppaRao Puli if (jsonValue.size() != value.size()) 311471a5eb8SAppaRao Puli { 312471a5eb8SAppaRao Puli return false; 313471a5eb8SAppaRao Puli } 314471a5eb8SAppaRao Puli size_t index = 0; 315471a5eb8SAppaRao Puli for (const auto& val : jsonValue.items()) 316471a5eb8SAppaRao Puli { 317471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(val.value(), key, 318471a5eb8SAppaRao Puli value[index++]) && 319471a5eb8SAppaRao Puli ret; 320471a5eb8SAppaRao Puli } 321471a5eb8SAppaRao Puli } 3222c70f800SEd Tanous else if constexpr (IsVector<Type>::value) 323471a5eb8SAppaRao Puli { 324471a5eb8SAppaRao Puli if (!jsonValue.is_array()) 325471a5eb8SAppaRao Puli { 326471a5eb8SAppaRao Puli return false; 327471a5eb8SAppaRao Puli } 328471a5eb8SAppaRao Puli 329471a5eb8SAppaRao Puli for (const auto& val : jsonValue.items()) 330471a5eb8SAppaRao Puli { 331471a5eb8SAppaRao Puli value.emplace_back(); 332471a5eb8SAppaRao Puli ret = unpackValue<typename Type::value_type>(val.value(), key, 333471a5eb8SAppaRao Puli value.back()) && 334471a5eb8SAppaRao Puli ret; 335471a5eb8SAppaRao Puli } 336471a5eb8SAppaRao Puli } 337471a5eb8SAppaRao Puli else 338471a5eb8SAppaRao Puli { 339471a5eb8SAppaRao Puli UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value); 340471a5eb8SAppaRao Puli if (ec != UnpackErrorCode::success) 341471a5eb8SAppaRao Puli { 342471a5eb8SAppaRao Puli return false; 343471a5eb8SAppaRao Puli } 344471a5eb8SAppaRao Puli } 345471a5eb8SAppaRao Puli 34641352c24SSantosh Puranik return ret; 347771cfa0fSJason M. Bills } 3489712f8acSEd Tanous } // namespace details 3499712f8acSEd Tanous 350ea2e6eecSWilly Tu // clang-format off 351ea2e6eecSWilly Tu using UnpackVariant = std::variant< 352ea2e6eecSWilly Tu uint8_t*, 353ea2e6eecSWilly Tu uint16_t*, 354ea2e6eecSWilly Tu int16_t*, 355ea2e6eecSWilly Tu uint32_t*, 356ea2e6eecSWilly Tu int32_t*, 357ea2e6eecSWilly Tu uint64_t*, 358ea2e6eecSWilly Tu int64_t*, 359ea2e6eecSWilly Tu bool*, 360ea2e6eecSWilly Tu double*, 361ea2e6eecSWilly Tu std::string*, 362ea2e6eecSWilly Tu nlohmann::json*, 363ea2e6eecSWilly Tu std::vector<uint8_t>*, 364ea2e6eecSWilly Tu std::vector<uint16_t>*, 365ea2e6eecSWilly Tu std::vector<int16_t>*, 366ea2e6eecSWilly Tu std::vector<uint32_t>*, 367ea2e6eecSWilly Tu std::vector<int32_t>*, 368ea2e6eecSWilly Tu std::vector<uint64_t>*, 369ea2e6eecSWilly Tu std::vector<int64_t>*, 370ea2e6eecSWilly Tu //std::vector<bool>*, 371ea2e6eecSWilly Tu std::vector<double>*, 372ea2e6eecSWilly Tu std::vector<std::string>*, 373ea2e6eecSWilly Tu std::vector<nlohmann::json>*, 374ea2e6eecSWilly Tu std::optional<uint8_t>*, 375ea2e6eecSWilly Tu std::optional<uint16_t>*, 376ea2e6eecSWilly Tu std::optional<int16_t>*, 377ea2e6eecSWilly Tu std::optional<uint32_t>*, 378ea2e6eecSWilly Tu std::optional<int32_t>*, 379ea2e6eecSWilly Tu std::optional<uint64_t>*, 380ea2e6eecSWilly Tu std::optional<int64_t>*, 381ea2e6eecSWilly Tu std::optional<bool>*, 382ea2e6eecSWilly Tu std::optional<double>*, 383ea2e6eecSWilly Tu std::optional<std::string>*, 384ea2e6eecSWilly Tu std::optional<nlohmann::json>*, 385ea2e6eecSWilly Tu std::optional<std::vector<uint8_t>>*, 386ea2e6eecSWilly Tu std::optional<std::vector<uint16_t>>*, 387ea2e6eecSWilly Tu std::optional<std::vector<int16_t>>*, 388ea2e6eecSWilly Tu std::optional<std::vector<uint32_t>>*, 389ea2e6eecSWilly Tu std::optional<std::vector<int32_t>>*, 390ea2e6eecSWilly Tu std::optional<std::vector<uint64_t>>*, 391ea2e6eecSWilly Tu std::optional<std::vector<int64_t>>*, 392ea2e6eecSWilly Tu //std::optional<std::vector<bool>>*, 393ea2e6eecSWilly Tu std::optional<std::vector<double>>*, 394ea2e6eecSWilly Tu std::optional<std::vector<std::string>>*, 395ea2e6eecSWilly Tu std::optional<std::vector<nlohmann::json>>* 396ea2e6eecSWilly Tu >; 397ea2e6eecSWilly Tu // clang-format on 398ea2e6eecSWilly Tu 399ea2e6eecSWilly Tu struct PerUnpack 400ea2e6eecSWilly Tu { 401ea2e6eecSWilly Tu std::string_view key; 402ea2e6eecSWilly Tu UnpackVariant value; 403ea2e6eecSWilly Tu bool complete = false; 404ea2e6eecSWilly Tu }; 405ea2e6eecSWilly Tu 406ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res, 407ea2e6eecSWilly Tu std::span<PerUnpack> toUnpack) 4089712f8acSEd Tanous { 40941352c24SSantosh Puranik bool result = true; 410d91415c4SEd Tanous nlohmann::json::object_t* obj = 411d91415c4SEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 412d91415c4SEd Tanous if (obj == nullptr) 4139712f8acSEd Tanous { 4149712f8acSEd Tanous BMCWEB_LOG_DEBUG << "Json value is not an object"; 415f12894f8SJason M. Bills messages::unrecognizedRequestBody(res); 4169712f8acSEd Tanous return false; 4179712f8acSEd Tanous } 418d91415c4SEd Tanous for (auto& item : *obj) 4199712f8acSEd Tanous { 420ea2e6eecSWilly Tu size_t unpackIndex = 0; 421ea2e6eecSWilly Tu for (; unpackIndex < toUnpack.size(); unpackIndex++) 422ea2e6eecSWilly Tu { 423ea2e6eecSWilly Tu PerUnpack& unpackSpec = toUnpack[unpackIndex]; 424ea2e6eecSWilly Tu std::string_view key = unpackSpec.key; 425ea2e6eecSWilly Tu size_t keysplitIndex = key.find('/'); 426ea2e6eecSWilly Tu std::string_view leftover; 427ea2e6eecSWilly Tu if (keysplitIndex != std::string_view::npos) 428ea2e6eecSWilly Tu { 429ea2e6eecSWilly Tu leftover = key.substr(keysplitIndex + 1); 430ea2e6eecSWilly Tu key = key.substr(0, keysplitIndex); 431ea2e6eecSWilly Tu } 432ea2e6eecSWilly Tu 433d91415c4SEd Tanous if (key != item.first || unpackSpec.complete) 434ea2e6eecSWilly Tu { 435ea2e6eecSWilly Tu continue; 436ea2e6eecSWilly Tu } 437ea2e6eecSWilly Tu 438ea2e6eecSWilly Tu // Sublevel key 439ea2e6eecSWilly Tu if (!leftover.empty()) 440ea2e6eecSWilly Tu { 441ea2e6eecSWilly Tu // Include the slash in the key so we can compare later 442ea2e6eecSWilly Tu key = unpackSpec.key.substr(0, keysplitIndex + 1); 443ea2e6eecSWilly Tu nlohmann::json j; 444d91415c4SEd Tanous result = details::unpackValue<nlohmann::json>(item.second, key, 445ea2e6eecSWilly Tu res, j) && 44641352c24SSantosh Puranik result; 44755f79e6fSEd Tanous if (!result) 448ea2e6eecSWilly Tu { 449ea2e6eecSWilly Tu return result; 4509712f8acSEd Tanous } 4519712f8acSEd Tanous 452ea2e6eecSWilly Tu std::vector<PerUnpack> nextLevel; 453ea2e6eecSWilly Tu for (PerUnpack& p : toUnpack) 454ea2e6eecSWilly Tu { 455ea2e6eecSWilly Tu if (!p.key.starts_with(key)) 456ea2e6eecSWilly Tu { 457ea2e6eecSWilly Tu continue; 458ea2e6eecSWilly Tu } 459ea2e6eecSWilly Tu std::string_view thisLeftover = p.key.substr(key.size()); 460ea2e6eecSWilly Tu nextLevel.push_back({thisLeftover, p.value, false}); 461ea2e6eecSWilly Tu p.complete = true; 4629712f8acSEd Tanous } 46377dd8813SKowalski, Kamil 464ea2e6eecSWilly Tu result = readJsonHelper(j, res, nextLevel) && result; 465ea2e6eecSWilly Tu break; 466ea2e6eecSWilly Tu } 467ea2e6eecSWilly Tu 468002d39b4SEd Tanous result = std::visit( 469ea2e6eecSWilly Tu [&item, &unpackSpec, &res](auto&& val) { 470ea2e6eecSWilly Tu using ContainedT = 471ea2e6eecSWilly Tu std::remove_pointer_t<std::decay_t<decltype(val)>>; 472ea2e6eecSWilly Tu return details::unpackValue<ContainedT>( 473d91415c4SEd Tanous item.second, unpackSpec.key, res, *val); 474ea2e6eecSWilly Tu }, 475ea2e6eecSWilly Tu unpackSpec.value) && 476ea2e6eecSWilly Tu result; 477ea2e6eecSWilly Tu 478ea2e6eecSWilly Tu unpackSpec.complete = true; 479ea2e6eecSWilly Tu break; 480ea2e6eecSWilly Tu } 481ea2e6eecSWilly Tu 482ea2e6eecSWilly Tu if (unpackIndex == toUnpack.size()) 483ea2e6eecSWilly Tu { 484d91415c4SEd Tanous messages::propertyUnknown(res, item.first); 485ea2e6eecSWilly Tu result = false; 486ea2e6eecSWilly Tu } 487ea2e6eecSWilly Tu } 488ea2e6eecSWilly Tu 489ea2e6eecSWilly Tu for (PerUnpack& perUnpack : toUnpack) 490ea2e6eecSWilly Tu { 49155f79e6fSEd Tanous if (!perUnpack.complete) 492ea2e6eecSWilly Tu { 493ea2e6eecSWilly Tu bool isOptional = std::visit( 494ea2e6eecSWilly Tu [](auto&& val) { 495ea2e6eecSWilly Tu using ContainedType = 496ea2e6eecSWilly Tu std::remove_pointer_t<std::decay_t<decltype(val)>>; 497ea2e6eecSWilly Tu return details::IsOptional<ContainedType>::value; 498ea2e6eecSWilly Tu }, 499ea2e6eecSWilly Tu perUnpack.value); 500ea2e6eecSWilly Tu if (isOptional) 501ea2e6eecSWilly Tu { 502ea2e6eecSWilly Tu continue; 503ea2e6eecSWilly Tu } 504ea2e6eecSWilly Tu messages::propertyMissing(res, perUnpack.key); 505ea2e6eecSWilly Tu result = false; 506ea2e6eecSWilly Tu } 507ea2e6eecSWilly Tu } 508ea2e6eecSWilly Tu return result; 509ea2e6eecSWilly Tu } 510ea2e6eecSWilly Tu 51189492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {} 512ea2e6eecSWilly Tu 513ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes> 514ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key, 515ea2e6eecSWilly Tu FirstType& first, UnpackTypes&&... in) 516ea2e6eecSWilly Tu { 517ea2e6eecSWilly Tu if (toPack.empty()) 518ea2e6eecSWilly Tu { 519ea2e6eecSWilly Tu return; 520ea2e6eecSWilly Tu } 521ea2e6eecSWilly Tu toPack[0].key = key; 522ea2e6eecSWilly Tu toPack[0].value = &first; 523ea2e6eecSWilly Tu // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 524ea2e6eecSWilly Tu packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...); 525ea2e6eecSWilly Tu } 526ea2e6eecSWilly Tu 527ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes> 528ea2e6eecSWilly Tu bool readJson(nlohmann::json& jsonRequest, crow::Response& res, 529ea2e6eecSWilly Tu std::string_view key, FirstType&& first, UnpackTypes&&... in) 530ea2e6eecSWilly Tu { 531ea2e6eecSWilly Tu const std::size_t n = sizeof...(UnpackTypes) + 2; 532ea2e6eecSWilly Tu std::array<PerUnpack, n / 2> toUnpack2; 533ea2e6eecSWilly Tu packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...); 534ea2e6eecSWilly Tu return readJsonHelper(jsonRequest, res, toUnpack2); 535ea2e6eecSWilly Tu } 536ea2e6eecSWilly Tu 537ea2e6eecSWilly Tu inline std::optional<nlohmann::json> 538ea2e6eecSWilly Tu readJsonPatchHelper(const crow::Request& req, crow::Response& res) 5390627a2c7SEd Tanous { 5400627a2c7SEd Tanous nlohmann::json jsonRequest; 5410627a2c7SEd Tanous if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 5420627a2c7SEd Tanous { 5430627a2c7SEd Tanous BMCWEB_LOG_DEBUG << "Json value not readable"; 544ea2e6eecSWilly Tu return std::nullopt; 5450627a2c7SEd Tanous } 546357bb8f8SEd Tanous nlohmann::json::object_t* object = 547357bb8f8SEd Tanous jsonRequest.get_ptr<nlohmann::json::object_t*>(); 548357bb8f8SEd Tanous if (object == nullptr || object->empty()) 54915ed6780SWilly Tu { 55015ed6780SWilly Tu BMCWEB_LOG_DEBUG << "Json value is empty"; 55115ed6780SWilly Tu messages::emptyJSON(res); 552ea2e6eecSWilly Tu return std::nullopt; 553ea2e6eecSWilly Tu } 554357bb8f8SEd Tanous std::erase_if(*object, 555357bb8f8SEd Tanous [](const std::pair<std::string, nlohmann::json>& item) { 556357bb8f8SEd Tanous return item.first.starts_with("@odata."); 557357bb8f8SEd Tanous }); 558357bb8f8SEd Tanous if (object->empty()) 559357bb8f8SEd Tanous { 560357bb8f8SEd Tanous // If the update request only contains OData annotations, the service 561357bb8f8SEd Tanous // should return the HTTP 400 Bad Request status code with the 562357bb8f8SEd Tanous // NoOperation message from the Base Message Registry, ... 563357bb8f8SEd Tanous messages::noOperation(res); 564357bb8f8SEd Tanous return std::nullopt; 565357bb8f8SEd Tanous } 566357bb8f8SEd Tanous 567ea2e6eecSWilly Tu return {std::move(jsonRequest)}; 568ea2e6eecSWilly Tu } 569ea2e6eecSWilly Tu 570ea2e6eecSWilly Tu template <typename... UnpackTypes> 571ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res, 572ea2e6eecSWilly Tu std::string_view key, UnpackTypes&&... in) 573ea2e6eecSWilly Tu { 574ea2e6eecSWilly Tu std::optional<nlohmann::json> jsonRequest = readJsonPatchHelper(req, res); 575ea2e6eecSWilly Tu if (jsonRequest == std::nullopt) 576ea2e6eecSWilly Tu { 57715ed6780SWilly Tu return false; 57815ed6780SWilly Tu } 57915ed6780SWilly Tu 580ea2e6eecSWilly Tu return readJson(*jsonRequest, res, key, std::forward<UnpackTypes&&>(in)...); 58115ed6780SWilly Tu } 58215ed6780SWilly Tu 58315ed6780SWilly Tu template <typename... UnpackTypes> 58415ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res, 585ea2e6eecSWilly Tu const char* key, UnpackTypes&&... in) 58615ed6780SWilly Tu { 58715ed6780SWilly Tu nlohmann::json jsonRequest; 58815ed6780SWilly Tu if (!json_util::processJsonFromRequest(res, req, jsonRequest)) 58915ed6780SWilly Tu { 59015ed6780SWilly Tu BMCWEB_LOG_DEBUG << "Json value not readable"; 59115ed6780SWilly Tu return false; 59215ed6780SWilly Tu } 593ea2e6eecSWilly Tu return readJson(jsonRequest, res, key, std::forward<UnpackTypes&&>(in)...); 5940627a2c7SEd Tanous } 595*185444b1SNan Zhou 596*185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the 597*185444b1SNan Zhou // @odata.id key 598*185444b1SNan Zhou inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b) 599*185444b1SNan Zhou { 600*185444b1SNan Zhou using object_t = nlohmann::json::object_t; 601*185444b1SNan Zhou const object_t* aObj = a.get_ptr<const object_t*>(); 602*185444b1SNan Zhou const object_t* bObj = b.get_ptr<const object_t*>(); 603*185444b1SNan Zhou 604*185444b1SNan Zhou if (aObj == nullptr) 605*185444b1SNan Zhou { 606*185444b1SNan Zhou if (bObj == nullptr) 607*185444b1SNan Zhou { 608*185444b1SNan Zhou return 0; 609*185444b1SNan Zhou } 610*185444b1SNan Zhou return -1; 611*185444b1SNan Zhou } 612*185444b1SNan Zhou if (bObj == nullptr) 613*185444b1SNan Zhou { 614*185444b1SNan Zhou return 1; 615*185444b1SNan Zhou } 616*185444b1SNan Zhou object_t::const_iterator aIt = aObj->find("@odata.id"); 617*185444b1SNan Zhou object_t::const_iterator bIt = bObj->find("@odata.id"); 618*185444b1SNan Zhou // If either object doesn't have the key, they get "sorted" to the end. 619*185444b1SNan Zhou if (aIt == aObj->end()) 620*185444b1SNan Zhou { 621*185444b1SNan Zhou if (bIt == bObj->end()) 622*185444b1SNan Zhou { 623*185444b1SNan Zhou return 0; 624*185444b1SNan Zhou } 625*185444b1SNan Zhou return -1; 626*185444b1SNan Zhou } 627*185444b1SNan Zhou if (bIt == bObj->end()) 628*185444b1SNan Zhou { 629*185444b1SNan Zhou return 1; 630*185444b1SNan Zhou } 631*185444b1SNan Zhou const nlohmann::json::string_t* nameA = 632*185444b1SNan Zhou aIt->second.get_ptr<const std::string*>(); 633*185444b1SNan Zhou const nlohmann::json::string_t* nameB = 634*185444b1SNan Zhou bIt->second.get_ptr<const std::string*>(); 635*185444b1SNan Zhou // If either object doesn't have a string as the key, they get "sorted" to 636*185444b1SNan Zhou // the end. 637*185444b1SNan Zhou if (nameA == nullptr) 638*185444b1SNan Zhou { 639*185444b1SNan Zhou if (nameB == nullptr) 640*185444b1SNan Zhou { 641*185444b1SNan Zhou return 0; 642*185444b1SNan Zhou } 643*185444b1SNan Zhou return -1; 644*185444b1SNan Zhou } 645*185444b1SNan Zhou if (nameB == nullptr) 646*185444b1SNan Zhou { 647*185444b1SNan Zhou return 1; 648*185444b1SNan Zhou } 649*185444b1SNan Zhou boost::urls::url_view aUrl(*nameA); 650*185444b1SNan Zhou boost::urls::url_view bUrl(*nameB); 651*185444b1SNan Zhou auto segmentsAIt = aUrl.segments().begin(); 652*185444b1SNan Zhou auto segmentsBIt = bUrl.segments().begin(); 653*185444b1SNan Zhou 654*185444b1SNan Zhou while (true) 655*185444b1SNan Zhou { 656*185444b1SNan Zhou if (segmentsAIt == aUrl.segments().end()) 657*185444b1SNan Zhou { 658*185444b1SNan Zhou if (segmentsBIt == bUrl.segments().end()) 659*185444b1SNan Zhou { 660*185444b1SNan Zhou return 0; 661*185444b1SNan Zhou } 662*185444b1SNan Zhou return -1; 663*185444b1SNan Zhou } 664*185444b1SNan Zhou if (segmentsBIt == bUrl.segments().end()) 665*185444b1SNan Zhou { 666*185444b1SNan Zhou return 1; 667*185444b1SNan Zhou } 668*185444b1SNan Zhou int res = alphanumComp(*segmentsAIt, *segmentsBIt); 669*185444b1SNan Zhou if (res != 0) 670*185444b1SNan Zhou { 671*185444b1SNan Zhou return res; 672*185444b1SNan Zhou } 673*185444b1SNan Zhou 674*185444b1SNan Zhou segmentsAIt++; 675*185444b1SNan Zhou segmentsBIt++; 676*185444b1SNan Zhou } 677*185444b1SNan Zhou }; 678*185444b1SNan Zhou 679*185444b1SNan Zhou struct ODataObjectLess 680*185444b1SNan Zhou { 681*185444b1SNan Zhou bool operator()(const nlohmann::json& left, 682*185444b1SNan Zhou const nlohmann::json& right) const 683*185444b1SNan Zhou { 684*185444b1SNan Zhou return odataObjectCmp(left, right) < 0; 685*185444b1SNan Zhou } 686*185444b1SNan Zhou }; 687*185444b1SNan Zhou 688*185444b1SNan Zhou // Sort the JSON array by |element[key]|. 689*185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller 690*185444b1SNan Zhou // those whose |element[key]| is string. 691*185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array) 692*185444b1SNan Zhou { 693*185444b1SNan Zhou std::sort(array.begin(), array.end(), ODataObjectLess()); 694*185444b1SNan Zhou } 695*185444b1SNan Zhou 69677dd8813SKowalski, Kamil } // namespace json_util 69777dd8813SKowalski, Kamil } // namespace redfish 698