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