xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision 185444b1dd5f28702e287c4ce7f7b6558356519b)
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