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     nlohmann::json::json_pointer jsonCountKeyName = jsonKeyName;
31     std::string back = jsonCountKeyName.back();
32     jsonCountKeyName.pop_back();
33     jsonCountKeyName /= back + "@odata.count";
34 
35     if (ec == boost::system::errc::io_error)
36     {
37         asyncResp->res.jsonValue[jsonKeyName] = nlohmann::json::array();
38         asyncResp->res.jsonValue[jsonCountKeyName] = 0;
39         return;
40     }
41 
42     if (ec)
43     {
44         BMCWEB_LOG_DEBUG("DBUS response error {}", ec.value());
45         messages::internalError(asyncResp->res);
46         return;
47     }
48 
49     std::vector<std::string> pathNames;
50     for (const auto& object : objects)
51     {
52         sdbusplus::message::object_path path(object);
53         std::string leaf = path.filename();
54         if (leaf.empty())
55         {
56             continue;
57         }
58         pathNames.push_back(leaf);
59     }
60     std::ranges::sort(pathNames, AlphanumLess<std::string>());
61 
62     nlohmann::json& members = asyncResp->res.jsonValue[jsonKeyName];
63     members = nlohmann::json::array();
64     for (const std::string& leaf : pathNames)
65     {
66         boost::urls::url url = collectionPath;
67         crow::utility::appendUrlPieces(url, leaf);
68         nlohmann::json::object_t member;
69         member["@odata.id"] = std::move(url);
70         members.emplace_back(std::move(member));
71     }
72     asyncResp->res.jsonValue[jsonCountKeyName] = members.size();
73 }
74 
75 /**
76  * @brief Populate the collection members from a GetSubTreePaths search of
77  *        inventory
78  *
79  * @param[i,o] asyncResp  Async response object
80  * @param[i]   collectionPath  Redfish collection path which is used for the
81  *             Members Redfish Path
82  * @param[i]   interfaces  List of interfaces to constrain the GetSubTree search
83  * @param[in]  subtree     D-Bus base path to constrain search to.
84  * @param[in]  jsonKeyName Key name in which the collection members will be
85  *             stored.
86  *
87  * @return void
88  */
89 inline void
90     getCollectionToKey(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
91                        const boost::urls::url& collectionPath,
92                        std::span<const std::string_view> interfaces,
93                        const std::string& subtree,
94                        const nlohmann::json::json_pointer& jsonKeyName)
95 {
96     BMCWEB_LOG_DEBUG("Get collection members for: {}", collectionPath.buffer());
97     dbus::utility::getSubTreePaths(subtree, 0, interfaces,
98                                    std::bind_front(handleCollectionMembers,
99                                                    asyncResp, collectionPath,
100                                                    jsonKeyName));
101 }
102 inline void
103     getCollectionMembers(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
104                          const boost::urls::url& collectionPath,
105                          std::span<const std::string_view> interfaces,
106                          const std::string& subtree)
107 {
108     getCollectionToKey(asyncResp, collectionPath, interfaces, subtree,
109                        nlohmann::json::json_pointer("/Members"));
110 }
111 
112 } // namespace collection_util
113 } // namespace redfish
114