Filter out query params in searchCollectionsArray

Previously searchCollectionsArray() had an implicit assumption that it
would not receive query parameters in the input URI. This patch adds the
assumption that it will.

Tested:
Unit tests pass
Confirmed that the query to the URI
`/redfish/v1/UpdateService?$expand=.($levels=1)` worked as expected now.

Change-Id: Ia33ece024c58e9d32f5815b2c69e8ab62c0c2127
Signed-off-by: Joonwon Kang <joonwonkang@google.com>
Signed-off-by: Ed Tanous <etanous@nvidia.com>
Signed-off-by: Carson Labrado <clabrado@google.com>
diff --git a/redfish-core/include/redfish_aggregator.hpp b/redfish-core/include/redfish_aggregator.hpp
index 8477b18..78d6819 100644
--- a/redfish-core/include/redfish_aggregator.hpp
+++ b/redfish-core/include/redfish_aggregator.hpp
@@ -57,51 +57,52 @@
 inline bool searchCollectionsArray(std::string_view uri,
                                    const SearchType searchType)
 {
-    constexpr std::string_view serviceRootUri = "/redfish/v1";
-
-    // The passed URI must begin with "/redfish/v1", but we have to strip it
-    // from the URI since topCollections does not include it in its URIs
-    if (!uri.starts_with(serviceRootUri))
-    {
-        return false;
-    }
-
-    // Catch empty final segments such as "/redfish/v1/Chassis//"
-    if (uri.ends_with("//"))
-    {
-        return false;
-    }
-
-    std::size_t parseCount = uri.size() - serviceRootUri.size();
-    // Don't include the trailing "/" if it exists such as in "/redfish/v1/"
-    if (uri.ends_with("/"))
-    {
-        parseCount--;
-    }
-
-    boost::system::result<boost::urls::url_view> parsedUrl =
-        boost::urls::parse_relative_ref(
-            uri.substr(serviceRootUri.size(), parseCount));
+    boost::system::result<boost::urls::url> parsedUrl =
+        boost::urls::parse_relative_ref(uri);
     if (!parsedUrl)
     {
-        BMCWEB_LOG_ERROR("Failed to get target URI from {}",
-                         uri.substr(serviceRootUri.size()));
+        BMCWEB_LOG_ERROR("Failed to get target URI from {}", uri);
         return false;
     }
 
-    if (!parsedUrl->segments().is_absolute() && !parsedUrl->segments().empty())
+    parsedUrl->normalize();
+    boost::urls::segments_ref segments = parsedUrl->segments();
+    if (!segments.is_absolute())
     {
         return false;
     }
 
-    // If no segments() then the passed URI was either "/redfish/v1" or
+    // The passed URI must begin with "/redfish/v1", but we have to strip it
+    // from the URI since topCollections does not include it in its URIs.
+    if (segments.size() < 2)
+    {
+        return false;
+    }
+    if (segments.front() != "redfish")
+    {
+        return false;
+    }
+    segments.erase(segments.begin());
+    if (segments.front() != "v1")
+    {
+        return false;
+    }
+    segments.erase(segments.begin());
+
+    // Exclude the trailing "/" if it exists such as in "/redfish/v1/".
+    if (!segments.empty() && segments.back().empty())
+    {
+        segments.pop_back();
+    }
+
+    // If no segments then the passed URI was either "/redfish/v1" or
     // "/redfish/v1/".
-    if (parsedUrl->segments().empty())
+    if (segments.empty())
     {
         return (searchType == SearchType::ContainsSubordinate) ||
                (searchType == SearchType::CollOrCon);
     }
-    std::string_view url = parsedUrl->buffer();
+    std::string_view url = segments.buffer();
     const auto* it = std::ranges::lower_bound(topCollections, url);
     if (it == topCollections.end())
     {
@@ -118,7 +119,7 @@
         collectionSegments.end();
 
     // Each segment in the passed URI should match the found collection
-    for (const auto& segment : parsedUrl->segments())
+    for (const auto& segment : segments)
     {
         if (itCollection == endCollection)
         {
diff --git a/test/redfish-core/include/redfish_aggregator_test.cpp b/test/redfish-core/include/redfish_aggregator_test.cpp
index b834587..5274e1b 100644
--- a/test/redfish-core/include/redfish_aggregator_test.cpp
+++ b/test/redfish-core/include/redfish_aggregator_test.cpp
@@ -580,6 +580,9 @@
     EXPECT_TRUE(containsSubordinateCollection(
         "/redfish/v1/TelemetryService/LogService/"));
     EXPECT_TRUE(containsSubordinateCollection("/redfish/v1/UpdateService"));
+
+    EXPECT_TRUE(containsSubordinateCollection(
+        "/redfish/v1/UpdateService?$expand=.($levels=1)"));
 }
 
 TEST(searchCollectionsArray, containsSubordinateInvalidURIs)