Fix an issue with non-root objectmapper entries

When the objectManager entry was not on the root, there were certain
cases that would return more entries than a user asked for.  This
patchset resolves the issue, and filters the responses accordingly.

Change-Id: I1c208433c6e8d161b60ea220587fcd0df6f6a6cb
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/include/openbmc_dbus_rest.hpp b/include/openbmc_dbus_rest.hpp
index 1fefa40..6cdc24b 100644
--- a/include/openbmc_dbus_rest.hpp
+++ b/include/openbmc_dbus_rest.hpp
@@ -95,28 +95,31 @@
 void getManagedObjectsForEnumerate(const std::string &object_name,
                                    const std::string &object_manager_path,
                                    const std::string &connection_name,
-                                   crow::Response &res,
-                                   std::shared_ptr<nlohmann::json> transaction)
+                                   std::shared_ptr<bmcweb::AsyncResp> asyncResp)
 {
+    BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
+                     << " object_manager_path " << object_manager_path
+                     << " connection_name " << connection_name;
     crow::connections::systemBus->async_method_call(
-        [&res, transaction](const boost::system::error_code ec,
-                            const dbus::utility::ManagedObjectType &objects) {
+        [asyncResp, object_name,
+         connection_name](const boost::system::error_code ec,
+                          const dbus::utility::ManagedObjectType &objects) {
             if (ec)
             {
-                BMCWEB_LOG_ERROR << ec;
+                BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
+                                 << " failed with code " << ec;
+                return;
             }
-            else
-            {
-                nlohmann::json &dataJson = *transaction;
 
-                for (auto &objectPath : objects)
+            nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
+
+            for (const auto &objectPath : objects)
+            {
+                if (boost::starts_with(objectPath.first.str, object_name))
                 {
-                    BMCWEB_LOG_DEBUG
-                        << "Reading object "
-                        << static_cast<const std::string &>(objectPath.first);
-                    nlohmann::json &objectJson =
-                        dataJson[static_cast<const std::string &>(
-                            objectPath.first)];
+                    BMCWEB_LOG_DEBUG << "Reading object "
+                                     << objectPath.first.str;
+                    nlohmann::json &objectJson = dataJson[objectPath.first.str];
                     if (objectJson.is_null())
                     {
                         objectJson = nlohmann::json::object();
@@ -135,14 +138,15 @@
                         }
                     }
                 }
-            }
-
-            if (transaction.use_count() == 1)
-            {
-                res.jsonValue = {{"message", "200 OK"},
-                                 {"status", "ok"},
-                                 {"data", std::move(*transaction)}};
-                res.end();
+                for (const auto &interface : objectPath.second)
+                {
+                    if (interface.first == "org.freedesktop.DBus.ObjectManager")
+                    {
+                        getManagedObjectsForEnumerate(
+                            objectPath.first.str, objectPath.first.str,
+                            connection_name, asyncResp);
+                    }
+                }
             }
         },
         connection_name, object_manager_path,
@@ -151,11 +155,12 @@
 
 void findObjectManagerPathForEnumerate(
     const std::string &object_name, const std::string &connection_name,
-    crow::Response &res, std::shared_ptr<nlohmann::json> transaction)
+    std::shared_ptr<bmcweb::AsyncResp> asyncResp)
 {
+    BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
+                     << " on connection:" << connection_name;
     crow::connections::systemBus->async_method_call(
-        [&res, transaction, object_name{std::string(object_name)},
-         connection_name{std::string(connection_name)}](
+        [asyncResp, object_name, connection_name](
             const boost::system::error_code ec,
             const boost::container::flat_map<
                 std::string, boost::container::flat_map<
@@ -163,7 +168,8 @@
                 &objects) {
             if (ec)
             {
-                BMCWEB_LOG_ERROR << ec;
+                BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
+                                 << " failed with code " << ec;
                 return;
             }
 
@@ -175,8 +181,8 @@
                     {
                         // Found the object manager path for this resource.
                         getManagedObjectsForEnumerate(
-                            object_name, pathGroup.first, connection_name, res,
-                            transaction);
+                            object_name, pathGroup.first, connection_name,
+                            asyncResp);
                         return;
                     }
                 }
@@ -754,48 +760,66 @@
 
 void handleEnumerate(crow::Response &res, const std::string &objectPath)
 {
+    BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
+    auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
+
+    asyncResp->res.jsonValue = {{"message", "200 OK"},
+                                {"status", "ok"},
+                                {"data", nlohmann::json::object()}};
+
     crow::connections::systemBus->async_method_call(
-        [&res, objectPath{std::string(objectPath)}](
-            const boost::system::error_code ec,
-            const GetSubTreeType &object_names) {
+        [asyncResp, objectPath](const boost::system::error_code ec,
+                                const GetSubTreeType &object_names) {
             if (ec)
             {
-                res.jsonValue = {{"message", "200 OK"},
-                                 {"status", "ok"},
-                                 {"data", nlohmann::json::object()}};
-
-                res.end();
                 return;
             }
-
-            boost::container::flat_set<std::string> connections;
+            // Map indicating connection name, and the path where the object
+            // manager exists
+            boost::container::flat_map<std::string, std::string> connections;
 
             for (const auto &object : object_names)
             {
-                for (const auto &Connection : object.second)
+                for (const auto &connection : object.second)
                 {
-                    connections.insert(Connection.first);
+                    std::string &objectManagerPath =
+                        connections[connection.first];
+                    for (const auto &interface : connection.second)
+                    {
+                        BMCWEB_LOG_DEBUG << connection.first
+                                         << " has interface " << interface;
+                        if (interface == "org.freedesktop.DBus.ObjectManager")
+                        {
+                            objectManagerPath = object.first;
+                        }
+                    }
                 }
             }
+            BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
 
-            if (connections.size() <= 0)
+            for (const auto &connection : connections)
             {
-                res.result(boost::beast::http::status::not_found);
-                res.end();
-                return;
-            }
-            auto transaction =
-                std::make_shared<nlohmann::json>(nlohmann::json::object());
-            for (const std::string &Connection : connections)
-            {
-                findObjectManagerPathForEnumerate(objectPath, Connection, res,
-                                                  transaction);
+                // If we already know where the object manager is, we don't need
+                // to search for it, we can call directly in to
+                // getManagedObjects
+                if (!connection.second.empty())
+                {
+                    getManagedObjectsForEnumerate(objectPath, connection.second,
+                                                  connection.first, asyncResp);
+                }
+                else
+                {
+                    // otherwise we need to find the object manager path before
+                    // we can continue
+                    findObjectManagerPathForEnumerate(
+                        objectPath, connection.first, asyncResp);
+                }
             }
         },
         "xyz.openbmc_project.ObjectMapper",
         "/xyz/openbmc_project/object_mapper",
         "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
-        (int32_t)0, std::array<std::string, 0>());
+        static_cast<int32_t>(0), std::array<const char *, 0>());
 }
 
 void handleGet(crow::Response &res, std::string &objectPath,
@@ -1105,6 +1129,71 @@
         transaction->objectPath, std::array<std::string, 0>());
 }
 
+inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
+                          std::string &objectPath)
+{
+    // Trim any trailing "/" at the end
+    if (boost::ends_with(objectPath, "/"))
+    {
+        objectPath.pop_back();
+    }
+
+    // If accessing a single attribute, fill in and update objectPath,
+    // otherwise leave destProperty blank
+    std::string destProperty = "";
+    const char *attrSeperator = "/attr/";
+    size_t attrPosition = objectPath.find(attrSeperator);
+    if (attrPosition != objectPath.npos)
+    {
+        destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
+                                         objectPath.length());
+        objectPath = objectPath.substr(0, attrPosition);
+    }
+
+    if (req.method() == "POST"_method)
+    {
+        constexpr const char *actionSeperator = "/action/";
+        size_t actionPosition = objectPath.find(actionSeperator);
+        if (actionPosition != objectPath.npos)
+        {
+            std::string postProperty =
+                objectPath.substr((actionPosition + strlen(actionSeperator)),
+                                  objectPath.length());
+            objectPath = objectPath.substr(0, actionPosition);
+            handleAction(req, res, objectPath, postProperty);
+            return;
+        }
+    }
+    else if (req.method() == "GET"_method)
+    {
+        if (boost::ends_with(objectPath, "/enumerate"))
+        {
+            objectPath.erase(objectPath.end() - sizeof("enumerate"),
+                             objectPath.end());
+            handleEnumerate(res, objectPath);
+        }
+        else if (boost::ends_with(objectPath, "/list"))
+        {
+            objectPath.erase(objectPath.end() - sizeof("list"),
+                             objectPath.end());
+            handleList(res, objectPath);
+        }
+        else
+        {
+            handleGet(res, objectPath, destProperty);
+        }
+        return;
+    }
+    else if (req.method() == "PUT"_method)
+    {
+        handlePut(req, res, objectPath, destProperty);
+        return;
+    }
+
+    res.result(boost::beast::http::status::method_not_allowed);
+    res.end();
+}
+
 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
 {
     BMCWEB_ROUTE(app, "/bus/")
@@ -1150,71 +1239,20 @@
             });
 
     BMCWEB_ROUTE(app, "/xyz/<path>")
-        .methods("GET"_method, "PUT"_method,
-                 "POST"_method)([](const crow::Request &req,
-                                   crow::Response &res,
-                                   const std::string &path) {
-            std::string objectPath = "/xyz/" + path;
+        .methods("GET"_method, "PUT"_method, "POST"_method)(
+            [](const crow::Request &req, crow::Response &res,
+               const std::string &path) {
+                std::string objectPath = "/xyz/" + path;
+                handleDBusUrl(req, res, objectPath);
+            });
 
-            // Trim any trailing "/" at the end
-            if (boost::ends_with(objectPath, "/"))
-            {
-                objectPath.pop_back();
-            }
-
-            // If accessing a single attribute, fill in and update objectPath,
-            // otherwise leave destProperty blank
-            std::string destProperty = "";
-            const char *attrSeperator = "/attr/";
-            size_t attrPosition = path.find(attrSeperator);
-            if (attrPosition != path.npos)
-            {
-                objectPath = "/xyz/" + path.substr(0, attrPosition);
-                destProperty = path.substr(attrPosition + strlen(attrSeperator),
-                                           path.length());
-            }
-
-            if (req.method() == "POST"_method)
-            {
-                constexpr const char *actionSeperator = "/action/";
-                size_t actionPosition = path.find(actionSeperator);
-                if (actionPosition != path.npos)
-                {
-                    objectPath = "/xyz/" + path.substr(0, actionPosition);
-                    std::string postProperty =
-                        path.substr((actionPosition + strlen(actionSeperator)),
-                                    path.length());
-                    handleAction(req, res, objectPath, postProperty);
-                    return;
-                }
-            }
-            else if (req.method() == "GET"_method)
-            {
-                if (boost::ends_with(objectPath, "/enumerate"))
-                {
-                    objectPath.erase(objectPath.end() - 10, objectPath.end());
-                    handleEnumerate(res, objectPath);
-                }
-                else if (boost::ends_with(objectPath, "/list"))
-                {
-                    objectPath.erase(objectPath.end() - 5, objectPath.end());
-                    handleList(res, objectPath);
-                }
-                else
-                {
-                    handleGet(res, objectPath, destProperty);
-                }
-                return;
-            }
-            else if (req.method() == "PUT"_method)
-            {
-                handlePut(req, res, objectPath, destProperty);
-                return;
-            }
-
-            res.result(boost::beast::http::status::method_not_allowed);
-            res.end();
-        });
+    BMCWEB_ROUTE(app, "/org/<path>")
+        .methods("GET"_method, "PUT"_method, "POST"_method)(
+            [](const crow::Request &req, crow::Response &res,
+               const std::string &path) {
+                std::string objectPath = "/org/" + path;
+                handleDBusUrl(req, res, objectPath);
+            });
 
     BMCWEB_ROUTE(app, "/download/dump/<str>/")
         .methods("GET"_method)([](const crow::Request &req, crow::Response &res,