1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "async_resp.hpp" 6 #include "dbus_utility.hpp" 7 #include "error_messages.hpp" 8 #include "http/utility.hpp" 9 #include "human_sort.hpp" 10 #include "logging.hpp" 11 12 #include <boost/url/url.hpp> 13 #include <nlohmann/json.hpp> 14 #include <sdbusplus/message/native_types.hpp> 15 16 #include <algorithm> 17 #include <functional> 18 #include <memory> 19 #include <ranges> 20 #include <span> 21 #include <string> 22 #include <string_view> 23 #include <utility> 24 #include <vector> 25 26 namespace redfish 27 { 28 namespace collection_util 29 { 30 31 inline void handleCollectionMembers( 32 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 33 const boost::urls::url& collectionPath, 34 const nlohmann::json::json_pointer& jsonKeyName, 35 const boost::system::error_code& ec, 36 const dbus::utility::MapperGetSubTreePathsResponse& objects) 37 { 38 if (jsonKeyName.empty()) 39 { 40 messages::internalError(asyncResp->res); 41 BMCWEB_LOG_ERROR("Json Key called empty. Did you mean /Members?"); 42 return; 43 } 44 nlohmann::json::json_pointer jsonCountKeyName = jsonKeyName; 45 std::string back = jsonCountKeyName.back(); 46 jsonCountKeyName.pop_back(); 47 jsonCountKeyName /= back + "@odata.count"; 48 49 if (ec == boost::system::errc::io_error) 50 { 51 asyncResp->res.jsonValue[jsonKeyName] = nlohmann::json::array(); 52 asyncResp->res.jsonValue[jsonCountKeyName] = 0; 53 return; 54 } 55 56 if (ec) 57 { 58 BMCWEB_LOG_DEBUG("DBUS response error {}", ec.value()); 59 messages::internalError(asyncResp->res); 60 return; 61 } 62 63 std::vector<std::string> pathNames; 64 for (const auto& object : objects) 65 { 66 sdbusplus::message::object_path path(object); 67 std::string leaf = path.filename(); 68 if (leaf.empty()) 69 { 70 continue; 71 } 72 pathNames.push_back(leaf); 73 } 74 std::ranges::sort(pathNames, AlphanumLess<std::string>()); 75 76 nlohmann::json& members = asyncResp->res.jsonValue[jsonKeyName]; 77 members = nlohmann::json::array(); 78 for (const std::string& leaf : pathNames) 79 { 80 boost::urls::url url = collectionPath; 81 crow::utility::appendUrlPieces(url, leaf); 82 nlohmann::json::object_t member; 83 member["@odata.id"] = std::move(url); 84 members.emplace_back(std::move(member)); 85 } 86 asyncResp->res.jsonValue[jsonCountKeyName] = members.size(); 87 } 88 89 /** 90 * @brief Populate the collection members from a GetSubTreePaths search of 91 * inventory 92 * 93 * @param[i,o] asyncResp Async response object 94 * @param[i] collectionPath Redfish collection path which is used for the 95 * Members Redfish Path 96 * @param[i] interfaces List of interfaces to constrain the GetSubTree search 97 * @param[in] subtree D-Bus base path to constrain search to. 98 * @param[in] jsonKeyName Key name in which the collection members will be 99 * stored. 100 * 101 * @return void 102 */ 103 inline void getCollectionToKey( 104 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 105 const boost::urls::url& collectionPath, 106 std::span<const std::string_view> interfaces, const std::string& subtree, 107 const nlohmann::json::json_pointer& jsonKeyName) 108 { 109 BMCWEB_LOG_DEBUG("Get collection members for: {}", collectionPath.buffer()); 110 dbus::utility::getSubTreePaths( 111 subtree, 0, interfaces, 112 std::bind_front(handleCollectionMembers, asyncResp, collectionPath, 113 jsonKeyName)); 114 } 115 inline void getCollectionMembers( 116 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 117 const boost::urls::url& collectionPath, 118 std::span<const std::string_view> interfaces, const std::string& subtree) 119 { 120 getCollectionToKey(asyncResp, collectionPath, interfaces, subtree, 121 nlohmann::json::json_pointer("/Members")); 122 } 123 124 } // namespace collection_util 125 } // namespace redfish 126