xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision a08b46ccf0fc0081cecc4843484c4f0eb13f5a9a)
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 
1877dd8813SKowalski, Kamil #include <crow/http_request.h>
1977dd8813SKowalski, Kamil #include <crow/http_response.h>
2077dd8813SKowalski, Kamil 
219712f8acSEd Tanous #include <bitset>
229712f8acSEd Tanous #include <error_messages.hpp>
231abe55efSEd Tanous #include <nlohmann/json.hpp>
241abe55efSEd Tanous namespace redfish
251abe55efSEd Tanous {
261abe55efSEd Tanous 
271abe55efSEd Tanous namespace json_util
281abe55efSEd Tanous {
2977dd8813SKowalski, Kamil 
3077dd8813SKowalski, Kamil /**
3177dd8813SKowalski, Kamil  * @brief Processes request to extract JSON from its body. If it fails, adds
3277dd8813SKowalski, Kamil  *       MalformedJSON message to response and ends it.
3377dd8813SKowalski, Kamil  *
3477dd8813SKowalski, Kamil  * @param[io]  res       Response object
3577dd8813SKowalski, Kamil  * @param[in]  req       Request object
3677dd8813SKowalski, Kamil  * @param[out] reqJson   JSON object extracted from request's body
3777dd8813SKowalski, Kamil  *
3877dd8813SKowalski, Kamil  * @return true if JSON is valid, false when JSON is invalid and response has
3977dd8813SKowalski, Kamil  *         been filled with message and ended.
4077dd8813SKowalski, Kamil  */
4155c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req,
4277dd8813SKowalski, Kamil                             nlohmann::json& reqJson);
439712f8acSEd Tanous namespace details
449712f8acSEd Tanous {
459712f8acSEd Tanous template <typename Type> struct unpackValue
469712f8acSEd Tanous {
479712f8acSEd Tanous     using isRequired = std::true_type;
489712f8acSEd Tanous     using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
499712f8acSEd Tanous };
509712f8acSEd Tanous 
519712f8acSEd Tanous template <typename OptionalType>
529712f8acSEd Tanous struct unpackValue<boost::optional<OptionalType>>
539712f8acSEd Tanous {
549712f8acSEd Tanous     using isRequired = std::false_type;
559712f8acSEd Tanous     using JsonType = std::add_const_t<std::add_pointer_t<OptionalType>>;
569712f8acSEd Tanous };
579712f8acSEd Tanous 
589712f8acSEd Tanous template <size_t Count, size_t Index>
599712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue,
609712f8acSEd Tanous                     crow::Response& res, std::bitset<Count>& handled)
619712f8acSEd Tanous {
629712f8acSEd Tanous     BMCWEB_LOG_DEBUG << "Unable to find variable for key" << key;
63*a08b46ccSJason M. Bills     messages::propertyUnknown(res, key);
649712f8acSEd Tanous }
659712f8acSEd Tanous 
669712f8acSEd Tanous template <size_t Count, size_t Index, typename ValueType,
679712f8acSEd Tanous           typename... UnpackTypes>
689712f8acSEd Tanous void readJsonValues(const std::string& key, nlohmann::json& jsonValue,
699712f8acSEd Tanous                     crow::Response& res, std::bitset<Count>& handled,
709712f8acSEd Tanous                     const char* keyToCheck, ValueType& valueToFill,
719712f8acSEd Tanous                     UnpackTypes&... in)
729712f8acSEd Tanous {
739712f8acSEd Tanous     if (key != keyToCheck)
749712f8acSEd Tanous     {
759712f8acSEd Tanous         readJsonValues<Count, Index + 1>(key, jsonValue, res, handled, in...);
769712f8acSEd Tanous         return;
779712f8acSEd Tanous     }
789712f8acSEd Tanous 
799712f8acSEd Tanous     handled.set(Index);
809712f8acSEd Tanous 
819712f8acSEd Tanous     using UnpackType = typename unpackValue<ValueType>::JsonType;
829712f8acSEd Tanous     UnpackType value = jsonValue.get_ptr<UnpackType>();
839712f8acSEd Tanous     if (value == nullptr)
849712f8acSEd Tanous     {
859712f8acSEd Tanous         BMCWEB_LOG_DEBUG << "Value for key " << key
869712f8acSEd Tanous                          << " was incorrect type: " << jsonValue.type_name();
87*a08b46ccSJason M. Bills         messages::propertyValueTypeError(res, jsonValue.dump(), key);
889712f8acSEd Tanous         return;
899712f8acSEd Tanous     }
909712f8acSEd Tanous 
919712f8acSEd Tanous     valueToFill = *value;
929712f8acSEd Tanous }
939712f8acSEd Tanous 
949712f8acSEd Tanous template <size_t Index = 0, size_t Count>
959712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res)
969712f8acSEd Tanous {
979712f8acSEd Tanous }
989712f8acSEd Tanous 
999712f8acSEd Tanous template <size_t Index = 0, size_t Count, typename ValueType,
1009712f8acSEd Tanous           typename... UnpackTypes>
1019712f8acSEd Tanous void handleMissing(std::bitset<Count>& handled, crow::Response& res,
1029712f8acSEd Tanous                    const char* key, ValueType& unused, UnpackTypes&... in)
1039712f8acSEd Tanous {
1049712f8acSEd Tanous     if (!handled.test(Index) && unpackValue<ValueType>::isRequired::value)
1059712f8acSEd Tanous     {
106*a08b46ccSJason M. Bills         messages::propertyMissing(res, key);
1079712f8acSEd Tanous     }
1089712f8acSEd Tanous     details::handleMissing<Index + 1, Count>(handled, res, in...);
1099712f8acSEd Tanous }
1109712f8acSEd Tanous } // namespace details
1119712f8acSEd Tanous 
1129712f8acSEd Tanous template <typename... UnpackTypes>
1139712f8acSEd Tanous bool readJson(const crow::Request& req, crow::Response& res, const char* key,
1149712f8acSEd Tanous               UnpackTypes&... in)
1159712f8acSEd Tanous {
1169712f8acSEd Tanous     nlohmann::json jsonRequest;
1179712f8acSEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
1189712f8acSEd Tanous     {
1199712f8acSEd Tanous         BMCWEB_LOG_DEBUG << "Json value not readable";
1209712f8acSEd Tanous         return false;
1219712f8acSEd Tanous     }
1229712f8acSEd Tanous     if (!jsonRequest.is_object())
1239712f8acSEd Tanous     {
1249712f8acSEd Tanous         BMCWEB_LOG_DEBUG << "Json value is not an object";
125f12894f8SJason M. Bills         messages::unrecognizedRequestBody(res);
1269712f8acSEd Tanous         return false;
1279712f8acSEd Tanous     }
1289712f8acSEd Tanous 
1299712f8acSEd Tanous     if (jsonRequest.empty())
1309712f8acSEd Tanous     {
1319712f8acSEd Tanous         BMCWEB_LOG_DEBUG << "Json value is empty";
132f12894f8SJason M. Bills         messages::emptyJSON(res);
1339712f8acSEd Tanous         return false;
1349712f8acSEd Tanous     }
1359712f8acSEd Tanous 
1369712f8acSEd Tanous     std::bitset<(sizeof...(in) + 1) / 2> handled(0);
1379712f8acSEd Tanous     for (const auto& item : jsonRequest.items())
1389712f8acSEd Tanous     {
1399712f8acSEd Tanous         details::readJsonValues<(sizeof...(in) + 1) / 2, 0, UnpackTypes...>(
1409712f8acSEd Tanous             item.key(), item.value(), res, handled, key, in...);
1419712f8acSEd Tanous     }
1429712f8acSEd Tanous 
1439712f8acSEd Tanous     details::handleMissing(handled, res, key, in...);
1449712f8acSEd Tanous 
1459712f8acSEd Tanous     return res.result() == boost::beast::http::status::ok;
1469712f8acSEd Tanous }
14777dd8813SKowalski, Kamil 
14877dd8813SKowalski, Kamil } // namespace json_util
14977dd8813SKowalski, Kamil } // namespace redfish
150