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