1f4c99e70SEd Tanous #pragma once 2d5c80ad9SNan Zhou #include "bmcweb_config.h" 3d5c80ad9SNan Zhou 4f4c99e70SEd Tanous #include "app.hpp" 5f4c99e70SEd Tanous #include "async_resp.hpp" 6f4c99e70SEd Tanous #include "error_messages.hpp" 7f4c99e70SEd Tanous #include "http_request.hpp" 802cad96eSEd Tanous #include "http_response.hpp" 9d5c80ad9SNan Zhou #include "logging.hpp" 10f4c99e70SEd Tanous 11d5c80ad9SNan Zhou #include <sys/types.h> 12d5c80ad9SNan Zhou 13e155ab54SNan Zhou #include <boost/algorithm/string/classification.hpp> 14e155ab54SNan Zhou #include <boost/algorithm/string/split.hpp> 15d5c80ad9SNan Zhou #include <boost/beast/http/message.hpp> // IWYU pragma: keep 16d5c80ad9SNan Zhou #include <boost/beast/http/status.hpp> 17d5c80ad9SNan Zhou #include <boost/beast/http/verb.hpp> 18e155ab54SNan Zhou #include <boost/url/error.hpp> 19d5c80ad9SNan Zhou #include <boost/url/params_view.hpp> 20d5c80ad9SNan Zhou #include <boost/url/string.hpp> 21d5c80ad9SNan Zhou #include <nlohmann/json.hpp> 22d5c80ad9SNan Zhou 23d5c80ad9SNan Zhou #include <algorithm> 24e155ab54SNan Zhou #include <array> 25e155ab54SNan Zhou #include <cctype> 267cf436c9SEd Tanous #include <charconv> 27d5c80ad9SNan Zhou #include <cstdint> 28d5c80ad9SNan Zhou #include <functional> 29e155ab54SNan Zhou #include <iterator> 30d5c80ad9SNan Zhou #include <limits> 31d5c80ad9SNan Zhou #include <map> 32d5c80ad9SNan Zhou #include <memory> 33d5c80ad9SNan Zhou #include <optional> 34e155ab54SNan Zhou #include <span> 35f4c99e70SEd Tanous #include <string> 36f4c99e70SEd Tanous #include <string_view> 37d5c80ad9SNan Zhou #include <system_error> 38e155ab54SNan Zhou #include <unordered_set> 397cf436c9SEd Tanous #include <utility> 40f4c99e70SEd Tanous #include <vector> 41f4c99e70SEd Tanous 42d5c80ad9SNan Zhou // IWYU pragma: no_include <boost/url/impl/params_view.hpp> 43d5c80ad9SNan Zhou // IWYU pragma: no_include <boost/beast/http/impl/message.hpp> 44d5c80ad9SNan Zhou // IWYU pragma: no_include <boost/intrusive/detail/list_iterator.hpp> 45e155ab54SNan Zhou // IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp> 46e155ab54SNan Zhou // IWYU pragma: no_include <boost/iterator/iterator_facade.hpp> 47e155ab54SNan Zhou // IWYU pragma: no_include <boost/type_index/type_index_facade.hpp> 48d5c80ad9SNan Zhou // IWYU pragma: no_include <stdint.h> 49d5c80ad9SNan Zhou 50f4c99e70SEd Tanous namespace redfish 51f4c99e70SEd Tanous { 52f4c99e70SEd Tanous namespace query_param 53f4c99e70SEd Tanous { 54ce8ea743SJiaqing Zhao inline constexpr size_t maxEntriesPerPage = 1000; 55f4c99e70SEd Tanous 567cf436c9SEd Tanous enum class ExpandType : uint8_t 577cf436c9SEd Tanous { 587cf436c9SEd Tanous None, 597cf436c9SEd Tanous Links, 607cf436c9SEd Tanous NotLinks, 617cf436c9SEd Tanous Both, 627cf436c9SEd Tanous }; 637cf436c9SEd Tanous 64a6b9125fSNan Zhou // The struct stores the parsed query parameters of the default Redfish route. 65f4c99e70SEd Tanous struct Query 66f4c99e70SEd Tanous { 67a6b9125fSNan Zhou // Only 68f4c99e70SEd Tanous bool isOnly = false; 69a6b9125fSNan Zhou // Expand 70a6b9125fSNan Zhou uint8_t expandLevel = 0; 717cf436c9SEd Tanous ExpandType expandType = ExpandType::None; 72c937d2bfSEd Tanous 73c937d2bfSEd Tanous // Skip 743648c8beSEd Tanous std::optional<size_t> skip = std::nullopt; 75c937d2bfSEd Tanous 76c937d2bfSEd Tanous // Top 77e155ab54SNan Zhou 783648c8beSEd Tanous std::optional<size_t> top = std::nullopt; 79e155ab54SNan Zhou 80e155ab54SNan Zhou // Select 81e155ab54SNan Zhou std::unordered_set<std::string> selectedProperties = {}; 82f4c99e70SEd Tanous }; 83f4c99e70SEd Tanous 84a6b9125fSNan Zhou // The struct defines how resource handlers in redfish-core/lib/ can handle 85a6b9125fSNan Zhou // query parameters themselves, so that the default Redfish route will delegate 86a6b9125fSNan Zhou // the processing. 87a6b9125fSNan Zhou struct QueryCapabilities 88a6b9125fSNan Zhou { 89a6b9125fSNan Zhou bool canDelegateOnly = false; 90c937d2bfSEd Tanous bool canDelegateTop = false; 91c937d2bfSEd Tanous bool canDelegateSkip = false; 92a6b9125fSNan Zhou uint8_t canDelegateExpandLevel = 0; 93e155ab54SNan Zhou bool canDelegateSelect = false; 94a6b9125fSNan Zhou }; 95a6b9125fSNan Zhou 96a6b9125fSNan Zhou // Delegates query parameters according to the given |queryCapabilities| 97a6b9125fSNan Zhou // This function doesn't check query parameter conflicts since the parse 98a6b9125fSNan Zhou // function will take care of it. 99a6b9125fSNan Zhou // Returns a delegated query object which can be used by individual resource 100a6b9125fSNan Zhou // handlers so that handlers don't need to query again. 101a6b9125fSNan Zhou inline Query delegate(const QueryCapabilities& queryCapabilities, Query& query) 102a6b9125fSNan Zhou { 103a6b9125fSNan Zhou Query delegated; 104a6b9125fSNan Zhou // delegate only 105a6b9125fSNan Zhou if (query.isOnly && queryCapabilities.canDelegateOnly) 106a6b9125fSNan Zhou { 107a6b9125fSNan Zhou delegated.isOnly = true; 108a6b9125fSNan Zhou query.isOnly = false; 109a6b9125fSNan Zhou } 110a6b9125fSNan Zhou // delegate expand as much as we can 111a6b9125fSNan Zhou if (query.expandType != ExpandType::None) 112a6b9125fSNan Zhou { 113a6b9125fSNan Zhou delegated.expandType = query.expandType; 114a6b9125fSNan Zhou if (query.expandLevel <= queryCapabilities.canDelegateExpandLevel) 115a6b9125fSNan Zhou { 116a6b9125fSNan Zhou query.expandType = ExpandType::None; 117a6b9125fSNan Zhou delegated.expandLevel = query.expandLevel; 118a6b9125fSNan Zhou query.expandLevel = 0; 119a6b9125fSNan Zhou } 120a6b9125fSNan Zhou else 121a6b9125fSNan Zhou { 122a6b9125fSNan Zhou query.expandLevel -= queryCapabilities.canDelegateExpandLevel; 123a6b9125fSNan Zhou delegated.expandLevel = queryCapabilities.canDelegateExpandLevel; 124a6b9125fSNan Zhou } 125a6b9125fSNan Zhou } 126c937d2bfSEd Tanous 127c937d2bfSEd Tanous // delegate top 1283648c8beSEd Tanous if (query.top && queryCapabilities.canDelegateTop) 129c937d2bfSEd Tanous { 130c937d2bfSEd Tanous delegated.top = query.top; 1313648c8beSEd Tanous query.top = std::nullopt; 132c937d2bfSEd Tanous } 133c937d2bfSEd Tanous 134c937d2bfSEd Tanous // delegate skip 1353648c8beSEd Tanous if (query.skip && queryCapabilities.canDelegateSkip) 136c937d2bfSEd Tanous { 137c937d2bfSEd Tanous delegated.skip = query.skip; 138c937d2bfSEd Tanous query.skip = 0; 139c937d2bfSEd Tanous } 140e155ab54SNan Zhou 141e155ab54SNan Zhou // delegate select 142e155ab54SNan Zhou if (!query.selectedProperties.empty() && 143e155ab54SNan Zhou queryCapabilities.canDelegateSelect) 144e155ab54SNan Zhou { 145e155ab54SNan Zhou delegated.selectedProperties = std::move(query.selectedProperties); 146e155ab54SNan Zhou query.selectedProperties.clear(); 147e155ab54SNan Zhou } 148a6b9125fSNan Zhou return delegated; 149a6b9125fSNan Zhou } 150a6b9125fSNan Zhou 1517cf436c9SEd Tanous inline bool getExpandType(std::string_view value, Query& query) 1527cf436c9SEd Tanous { 1537cf436c9SEd Tanous if (value.empty()) 1547cf436c9SEd Tanous { 1557cf436c9SEd Tanous return false; 1567cf436c9SEd Tanous } 1577cf436c9SEd Tanous switch (value[0]) 1587cf436c9SEd Tanous { 1597cf436c9SEd Tanous case '*': 1607cf436c9SEd Tanous query.expandType = ExpandType::Both; 1617cf436c9SEd Tanous break; 1627cf436c9SEd Tanous case '.': 1637cf436c9SEd Tanous query.expandType = ExpandType::NotLinks; 1647cf436c9SEd Tanous break; 1657cf436c9SEd Tanous case '~': 1667cf436c9SEd Tanous query.expandType = ExpandType::Links; 1677cf436c9SEd Tanous break; 1687cf436c9SEd Tanous default: 1697cf436c9SEd Tanous return false; 1707cf436c9SEd Tanous 1717cf436c9SEd Tanous break; 1727cf436c9SEd Tanous } 1737cf436c9SEd Tanous value.remove_prefix(1); 1747cf436c9SEd Tanous if (value.empty()) 1757cf436c9SEd Tanous { 1767cf436c9SEd Tanous query.expandLevel = 1; 1777cf436c9SEd Tanous return true; 1787cf436c9SEd Tanous } 1797cf436c9SEd Tanous constexpr std::string_view levels = "($levels="; 1807cf436c9SEd Tanous if (!value.starts_with(levels)) 1817cf436c9SEd Tanous { 1827cf436c9SEd Tanous return false; 1837cf436c9SEd Tanous } 1847cf436c9SEd Tanous value.remove_prefix(levels.size()); 1857cf436c9SEd Tanous 1867cf436c9SEd Tanous auto it = std::from_chars(value.data(), value.data() + value.size(), 1877cf436c9SEd Tanous query.expandLevel); 1887cf436c9SEd Tanous if (it.ec != std::errc()) 1897cf436c9SEd Tanous { 1907cf436c9SEd Tanous return false; 1917cf436c9SEd Tanous } 1927cf436c9SEd Tanous value.remove_prefix(static_cast<size_t>(it.ptr - value.data())); 1937cf436c9SEd Tanous return value == ")"; 1947cf436c9SEd Tanous } 1957cf436c9SEd Tanous 196c937d2bfSEd Tanous enum class QueryError 197c937d2bfSEd Tanous { 198c937d2bfSEd Tanous Ok, 199c937d2bfSEd Tanous OutOfRange, 200c937d2bfSEd Tanous ValueFormat, 201c937d2bfSEd Tanous }; 202c937d2bfSEd Tanous 203c937d2bfSEd Tanous inline QueryError getNumericParam(std::string_view value, size_t& param) 204c937d2bfSEd Tanous { 205c937d2bfSEd Tanous std::from_chars_result r = 206c937d2bfSEd Tanous std::from_chars(value.data(), value.data() + value.size(), param); 207c937d2bfSEd Tanous 208c937d2bfSEd Tanous // If the number wasn't representable in the type, it's out of range 209c937d2bfSEd Tanous if (r.ec == std::errc::result_out_of_range) 210c937d2bfSEd Tanous { 211c937d2bfSEd Tanous return QueryError::OutOfRange; 212c937d2bfSEd Tanous } 213c937d2bfSEd Tanous // All other errors are value format 214c937d2bfSEd Tanous if (r.ec != std::errc()) 215c937d2bfSEd Tanous { 216c937d2bfSEd Tanous return QueryError::ValueFormat; 217c937d2bfSEd Tanous } 218c937d2bfSEd Tanous return QueryError::Ok; 219c937d2bfSEd Tanous } 220c937d2bfSEd Tanous 221c937d2bfSEd Tanous inline QueryError getSkipParam(std::string_view value, Query& query) 222c937d2bfSEd Tanous { 2233648c8beSEd Tanous return getNumericParam(value, query.skip.emplace()); 224c937d2bfSEd Tanous } 225c937d2bfSEd Tanous 226c937d2bfSEd Tanous inline QueryError getTopParam(std::string_view value, Query& query) 227c937d2bfSEd Tanous { 2283648c8beSEd Tanous QueryError ret = getNumericParam(value, query.top.emplace()); 229c937d2bfSEd Tanous if (ret != QueryError::Ok) 230c937d2bfSEd Tanous { 231c937d2bfSEd Tanous return ret; 232c937d2bfSEd Tanous } 233c937d2bfSEd Tanous 234c937d2bfSEd Tanous // Range check for sanity. 235c937d2bfSEd Tanous if (query.top > maxEntriesPerPage) 236c937d2bfSEd Tanous { 237c937d2bfSEd Tanous return QueryError::OutOfRange; 238c937d2bfSEd Tanous } 239c937d2bfSEd Tanous 240c937d2bfSEd Tanous return QueryError::Ok; 241c937d2bfSEd Tanous } 242c937d2bfSEd Tanous 243e155ab54SNan Zhou // Validates the property in the $select parameter. Every character is among 244e155ab54SNan Zhou // [a-zA-Z0-9\/#@_.] (taken from Redfish spec, section 9.6 Properties) 245e155ab54SNan Zhou inline bool isSelectedPropertyAllowed(std::string_view property) 246e155ab54SNan Zhou { 247e155ab54SNan Zhou // These a magic number, but with it it's less likely that this code 248e155ab54SNan Zhou // introduces CVE; e.g., too large properties crash the service. 249e155ab54SNan Zhou constexpr int maxPropertyLength = 60; 250e155ab54SNan Zhou if (property.empty() || property.size() > maxPropertyLength) 251e155ab54SNan Zhou { 252e155ab54SNan Zhou return false; 253e155ab54SNan Zhou } 254e155ab54SNan Zhou for (char ch : property) 255e155ab54SNan Zhou { 256e155ab54SNan Zhou if (std::isalnum(static_cast<unsigned char>(ch)) == 0 && ch != '/' && 257e155ab54SNan Zhou ch != '#' && ch != '@' && ch != '.') 258e155ab54SNan Zhou { 259e155ab54SNan Zhou return false; 260e155ab54SNan Zhou } 261e155ab54SNan Zhou } 262e155ab54SNan Zhou return true; 263e155ab54SNan Zhou } 264e155ab54SNan Zhou 265e155ab54SNan Zhou // Parses and validates the $select parameter. 266e155ab54SNan Zhou // As per OData URL Conventions and Redfish Spec, the $select values shall be 267e155ab54SNan Zhou // comma separated Resource Path 268e155ab54SNan Zhou // Ref: 269e155ab54SNan Zhou // 1. https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 270e155ab54SNan Zhou // 2. 271e155ab54SNan Zhou // https://docs.oasis-open.org/odata/odata/v4.01/os/abnf/odata-abnf-construction-rules.txt 272e155ab54SNan Zhou inline bool getSelectParam(std::string_view value, Query& query) 273e155ab54SNan Zhou { 274e155ab54SNan Zhou std::vector<std::string> properties; 275e155ab54SNan Zhou boost::split(properties, value, boost::is_any_of(",")); 276e155ab54SNan Zhou if (properties.empty()) 277e155ab54SNan Zhou { 278e155ab54SNan Zhou return false; 279e155ab54SNan Zhou } 280e155ab54SNan Zhou // These a magic number, but with it it's less likely that this code 281e155ab54SNan Zhou // introduces CVE; e.g., too large properties crash the service. 282e155ab54SNan Zhou constexpr int maxNumProperties = 10; 283e155ab54SNan Zhou if (properties.size() > maxNumProperties) 284e155ab54SNan Zhou { 285e155ab54SNan Zhou return false; 286e155ab54SNan Zhou } 287e155ab54SNan Zhou for (std::string& property : properties) 288e155ab54SNan Zhou { 289e155ab54SNan Zhou if (!isSelectedPropertyAllowed(property)) 290e155ab54SNan Zhou { 291e155ab54SNan Zhou return false; 292e155ab54SNan Zhou } 293e155ab54SNan Zhou property.insert(property.begin(), '/'); 294e155ab54SNan Zhou } 295e155ab54SNan Zhou query.selectedProperties = {std::make_move_iterator(properties.begin()), 296e155ab54SNan Zhou std::make_move_iterator(properties.end())}; 297e155ab54SNan Zhou // Per the Redfish spec section 7.3.3, the service shall select certain 298e155ab54SNan Zhou // properties as if $select was omitted. 299e155ab54SNan Zhou constexpr std::array<std::string_view, 5> reservedProperties = { 300e155ab54SNan Zhou "/@odata.id", "/@odata.type", "/@odata.context", "/@odata.etag", 301e155ab54SNan Zhou "/error"}; 302e155ab54SNan Zhou for (auto const& str : reservedProperties) 303e155ab54SNan Zhou { 304e155ab54SNan Zhou query.selectedProperties.emplace(std::string(str)); 305e155ab54SNan Zhou } 306e155ab54SNan Zhou return true; 307e155ab54SNan Zhou } 308e155ab54SNan Zhou 309f4c99e70SEd Tanous inline std::optional<Query> 310f4c99e70SEd Tanous parseParameters(const boost::urls::params_view& urlParams, 311f4c99e70SEd Tanous crow::Response& res) 312f4c99e70SEd Tanous { 313f4c99e70SEd Tanous Query ret; 314f4c99e70SEd Tanous for (const boost::urls::params_view::value_type& it : urlParams) 315f4c99e70SEd Tanous { 316f4c99e70SEd Tanous std::string_view key(it.key.data(), it.key.size()); 317f4c99e70SEd Tanous std::string_view value(it.value.data(), it.value.size()); 318f4c99e70SEd Tanous if (key == "only") 319f4c99e70SEd Tanous { 320f4c99e70SEd Tanous if (!it.value.empty()) 321f4c99e70SEd Tanous { 322f4c99e70SEd Tanous messages::queryParameterValueFormatError(res, value, key); 323f4c99e70SEd Tanous return std::nullopt; 324f4c99e70SEd Tanous } 325f4c99e70SEd Tanous ret.isOnly = true; 326f4c99e70SEd Tanous } 3275e52870bSEd Tanous else if (key == "$expand" && bmcwebInsecureEnableQueryParams) 3287cf436c9SEd Tanous { 3297cf436c9SEd Tanous if (!getExpandType(value, ret)) 3307cf436c9SEd Tanous { 3317cf436c9SEd Tanous messages::queryParameterValueFormatError(res, value, key); 3327cf436c9SEd Tanous return std::nullopt; 333f4c99e70SEd Tanous } 3347cf436c9SEd Tanous } 335c937d2bfSEd Tanous else if (key == "$top") 336c937d2bfSEd Tanous { 337c937d2bfSEd Tanous QueryError topRet = getTopParam(value, ret); 338c937d2bfSEd Tanous if (topRet == QueryError::ValueFormat) 339c937d2bfSEd Tanous { 340c937d2bfSEd Tanous messages::queryParameterValueFormatError(res, value, key); 341c937d2bfSEd Tanous return std::nullopt; 342c937d2bfSEd Tanous } 343c937d2bfSEd Tanous if (topRet == QueryError::OutOfRange) 344c937d2bfSEd Tanous { 345c937d2bfSEd Tanous messages::queryParameterOutOfRange( 346c937d2bfSEd Tanous res, value, "$top", 347a926c53eSJiaqing Zhao "0-" + std::to_string(maxEntriesPerPage)); 348c937d2bfSEd Tanous return std::nullopt; 349c937d2bfSEd Tanous } 350c937d2bfSEd Tanous } 351c937d2bfSEd Tanous else if (key == "$skip") 352c937d2bfSEd Tanous { 353c937d2bfSEd Tanous QueryError topRet = getSkipParam(value, ret); 354c937d2bfSEd Tanous if (topRet == QueryError::ValueFormat) 355c937d2bfSEd Tanous { 356c937d2bfSEd Tanous messages::queryParameterValueFormatError(res, value, key); 357c937d2bfSEd Tanous return std::nullopt; 358c937d2bfSEd Tanous } 359c937d2bfSEd Tanous if (topRet == QueryError::OutOfRange) 360c937d2bfSEd Tanous { 361c937d2bfSEd Tanous messages::queryParameterOutOfRange( 362c937d2bfSEd Tanous res, value, key, 363a926c53eSJiaqing Zhao "0-" + std::to_string(std::numeric_limits<size_t>::max())); 364c937d2bfSEd Tanous return std::nullopt; 365c937d2bfSEd Tanous } 366c937d2bfSEd Tanous } 367e155ab54SNan Zhou else if (key == "$select" && bmcwebInsecureEnableQueryParams) 368e155ab54SNan Zhou { 369e155ab54SNan Zhou if (!getSelectParam(value, ret)) 370e155ab54SNan Zhou { 371e155ab54SNan Zhou messages::queryParameterValueFormatError(res, value, key); 372e155ab54SNan Zhou return std::nullopt; 373e155ab54SNan Zhou } 374e155ab54SNan Zhou } 3757cf436c9SEd Tanous else 3767cf436c9SEd Tanous { 3777cf436c9SEd Tanous // Intentionally ignore other errors Redfish spec, 7.3.1 3787cf436c9SEd Tanous if (key.starts_with("$")) 3797cf436c9SEd Tanous { 3807cf436c9SEd Tanous // Services shall return... The HTTP 501 Not Implemented 3817cf436c9SEd Tanous // status code for any unsupported query parameters that 3827cf436c9SEd Tanous // start with $ . 3837cf436c9SEd Tanous messages::queryParameterValueFormatError(res, value, key); 3847cf436c9SEd Tanous res.result(boost::beast::http::status::not_implemented); 3857cf436c9SEd Tanous return std::nullopt; 3867cf436c9SEd Tanous } 3877cf436c9SEd Tanous // "Shall ignore unknown or unsupported query parameters that do 3887cf436c9SEd Tanous // not begin with $ ." 3897cf436c9SEd Tanous } 3907cf436c9SEd Tanous } 3917cf436c9SEd Tanous 392e155ab54SNan Zhou if (ret.expandType != ExpandType::None && !ret.selectedProperties.empty()) 393e155ab54SNan Zhou { 394e155ab54SNan Zhou messages::queryCombinationInvalid(res); 395e155ab54SNan Zhou return std::nullopt; 396e155ab54SNan Zhou } 397e155ab54SNan Zhou 398f4c99e70SEd Tanous return ret; 399f4c99e70SEd Tanous } 400f4c99e70SEd Tanous 401f4c99e70SEd Tanous inline bool processOnly(crow::App& app, crow::Response& res, 402f4c99e70SEd Tanous std::function<void(crow::Response&)>& completionHandler) 403f4c99e70SEd Tanous { 404f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "Processing only query param"; 405f4c99e70SEd Tanous auto itMembers = res.jsonValue.find("Members"); 406f4c99e70SEd Tanous if (itMembers == res.jsonValue.end()) 407f4c99e70SEd Tanous { 408f4c99e70SEd Tanous messages::queryNotSupportedOnResource(res); 409f4c99e70SEd Tanous completionHandler(res); 410f4c99e70SEd Tanous return false; 411f4c99e70SEd Tanous } 412f4c99e70SEd Tanous auto itMemBegin = itMembers->begin(); 413f4c99e70SEd Tanous if (itMemBegin == itMembers->end() || itMembers->size() != 1) 414f4c99e70SEd Tanous { 415f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "Members contains " << itMembers->size() 416f4c99e70SEd Tanous << " element, returning full collection."; 417f4c99e70SEd Tanous completionHandler(res); 418f4c99e70SEd Tanous return false; 419f4c99e70SEd Tanous } 420f4c99e70SEd Tanous 421f4c99e70SEd Tanous auto itUrl = itMemBegin->find("@odata.id"); 422f4c99e70SEd Tanous if (itUrl == itMemBegin->end()) 423f4c99e70SEd Tanous { 424f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "No found odata.id"; 425f4c99e70SEd Tanous messages::internalError(res); 426f4c99e70SEd Tanous completionHandler(res); 427f4c99e70SEd Tanous return false; 428f4c99e70SEd Tanous } 429f4c99e70SEd Tanous const std::string* url = itUrl->get_ptr<const std::string*>(); 430f4c99e70SEd Tanous if (url == nullptr) 431f4c99e70SEd Tanous { 432f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "@odata.id wasn't a string????"; 433f4c99e70SEd Tanous messages::internalError(res); 434f4c99e70SEd Tanous completionHandler(res); 435f4c99e70SEd Tanous return false; 436f4c99e70SEd Tanous } 437f4c99e70SEd Tanous // TODO(Ed) copy request headers? 438f4c99e70SEd Tanous // newReq.session = req.session; 439f4c99e70SEd Tanous std::error_code ec; 440f4c99e70SEd Tanous crow::Request newReq({boost::beast::http::verb::get, *url, 11}, ec); 441f4c99e70SEd Tanous if (ec) 442f4c99e70SEd Tanous { 443f4c99e70SEd Tanous messages::internalError(res); 444f4c99e70SEd Tanous completionHandler(res); 445f4c99e70SEd Tanous return false; 446f4c99e70SEd Tanous } 447f4c99e70SEd Tanous 448f4c99e70SEd Tanous auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 449f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "setting completion handler on " << &asyncResp->res; 450f4c99e70SEd Tanous asyncResp->res.setCompleteRequestHandler(std::move(completionHandler)); 451f4c99e70SEd Tanous asyncResp->res.setIsAliveHelper(res.releaseIsAliveHelper()); 452f4c99e70SEd Tanous app.handle(newReq, asyncResp); 453f4c99e70SEd Tanous return true; 454f4c99e70SEd Tanous } 455f4c99e70SEd Tanous 4567cf436c9SEd Tanous struct ExpandNode 4577cf436c9SEd Tanous { 4587cf436c9SEd Tanous nlohmann::json::json_pointer location; 4597cf436c9SEd Tanous std::string uri; 4607cf436c9SEd Tanous 4617cf436c9SEd Tanous inline bool operator==(const ExpandNode& other) const 4627cf436c9SEd Tanous { 4637cf436c9SEd Tanous return location == other.location && uri == other.uri; 4647cf436c9SEd Tanous } 4657cf436c9SEd Tanous }; 4667cf436c9SEd Tanous 4677cf436c9SEd Tanous // Walks a json object looking for Redfish NavigationReference entries that 4687cf436c9SEd Tanous // might need resolved. It recursively walks the jsonResponse object, looking 4697cf436c9SEd Tanous // for links at every level, and returns a list (out) of locations within the 4707cf436c9SEd Tanous // tree that need to be expanded. The current json pointer location p is passed 4717cf436c9SEd Tanous // in to reference the current node that's being expanded, so it can be combined 4727cf436c9SEd Tanous // with the keys from the jsonResponse object 4737cf436c9SEd Tanous inline void findNavigationReferencesRecursive( 4747cf436c9SEd Tanous ExpandType eType, nlohmann::json& jsonResponse, 4757cf436c9SEd Tanous const nlohmann::json::json_pointer& p, bool inLinks, 4767cf436c9SEd Tanous std::vector<ExpandNode>& out) 4777cf436c9SEd Tanous { 4787cf436c9SEd Tanous // If no expand is needed, return early 4797cf436c9SEd Tanous if (eType == ExpandType::None) 4807cf436c9SEd Tanous { 4817cf436c9SEd Tanous return; 4827cf436c9SEd Tanous } 4837cf436c9SEd Tanous nlohmann::json::array_t* array = 4847cf436c9SEd Tanous jsonResponse.get_ptr<nlohmann::json::array_t*>(); 4857cf436c9SEd Tanous if (array != nullptr) 4867cf436c9SEd Tanous { 4877cf436c9SEd Tanous size_t index = 0; 4887cf436c9SEd Tanous // For arrays, walk every element in the array 4897cf436c9SEd Tanous for (auto& element : *array) 4907cf436c9SEd Tanous { 4917cf436c9SEd Tanous nlohmann::json::json_pointer newPtr = p / index; 4927cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr.to_string(); 4937cf436c9SEd Tanous findNavigationReferencesRecursive(eType, element, newPtr, inLinks, 4947cf436c9SEd Tanous out); 4957cf436c9SEd Tanous index++; 4967cf436c9SEd Tanous } 4977cf436c9SEd Tanous } 4987cf436c9SEd Tanous nlohmann::json::object_t* obj = 4997cf436c9SEd Tanous jsonResponse.get_ptr<nlohmann::json::object_t*>(); 5007cf436c9SEd Tanous if (obj == nullptr) 5017cf436c9SEd Tanous { 5027cf436c9SEd Tanous return; 5037cf436c9SEd Tanous } 5047cf436c9SEd Tanous // Navigation References only ever have a single element 5057cf436c9SEd Tanous if (obj->size() == 1) 5067cf436c9SEd Tanous { 5077cf436c9SEd Tanous if (obj->begin()->first == "@odata.id") 5087cf436c9SEd Tanous { 5097cf436c9SEd Tanous const std::string* uri = 5107cf436c9SEd Tanous obj->begin()->second.get_ptr<const std::string*>(); 5117cf436c9SEd Tanous if (uri != nullptr) 5127cf436c9SEd Tanous { 5137cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "Found element at " << p.to_string(); 5147cf436c9SEd Tanous out.push_back({p, *uri}); 5157cf436c9SEd Tanous } 5167cf436c9SEd Tanous } 5177cf436c9SEd Tanous } 5187cf436c9SEd Tanous // Loop the object and look for links 5197cf436c9SEd Tanous for (auto& element : *obj) 5207cf436c9SEd Tanous { 521e479ad58SNan Zhou bool localInLinks = inLinks; 522e479ad58SNan Zhou if (!localInLinks) 5237cf436c9SEd Tanous { 5247cf436c9SEd Tanous // Check if this is a links node 525e479ad58SNan Zhou localInLinks = element.first == "Links"; 5267cf436c9SEd Tanous } 5277cf436c9SEd Tanous // Only traverse the parts of the tree the user asked for 5287cf436c9SEd Tanous // Per section 7.3 of the redfish specification 529e479ad58SNan Zhou if (localInLinks && eType == ExpandType::NotLinks) 5307cf436c9SEd Tanous { 5317cf436c9SEd Tanous continue; 5327cf436c9SEd Tanous } 533e479ad58SNan Zhou if (!localInLinks && eType == ExpandType::Links) 5347cf436c9SEd Tanous { 5357cf436c9SEd Tanous continue; 5367cf436c9SEd Tanous } 5377cf436c9SEd Tanous nlohmann::json::json_pointer newPtr = p / element.first; 5387cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr; 5397cf436c9SEd Tanous 5407cf436c9SEd Tanous findNavigationReferencesRecursive(eType, element.second, newPtr, 541e479ad58SNan Zhou localInLinks, out); 5427cf436c9SEd Tanous } 5437cf436c9SEd Tanous } 5447cf436c9SEd Tanous 5457cf436c9SEd Tanous inline std::vector<ExpandNode> 54672c3ae33SNan Zhou findNavigationReferences(ExpandType eType, nlohmann::json& jsonResponse) 5477cf436c9SEd Tanous { 5487cf436c9SEd Tanous std::vector<ExpandNode> ret; 54972c3ae33SNan Zhou const nlohmann::json::json_pointer root = nlohmann::json::json_pointer(""); 5507cf436c9SEd Tanous findNavigationReferencesRecursive(eType, jsonResponse, root, false, ret); 5517cf436c9SEd Tanous return ret; 5527cf436c9SEd Tanous } 5537cf436c9SEd Tanous 55472c3ae33SNan Zhou // Formats a query parameter string for the sub-query. 555b66cf2a2SNan Zhou // Returns std::nullopt on failures. 55672c3ae33SNan Zhou // This function shall handle $select when it is added. 55772c3ae33SNan Zhou // There is no need to handle parameters that's not campatible with $expand, 55872c3ae33SNan Zhou // e.g., $only, since this function will only be called in side $expand handlers 559b66cf2a2SNan Zhou inline std::optional<std::string> formatQueryForExpand(const Query& query) 56072c3ae33SNan Zhou { 56172c3ae33SNan Zhou // query.expandLevel<=1: no need to do subqueries 56272c3ae33SNan Zhou if (query.expandLevel <= 1) 56372c3ae33SNan Zhou { 564b66cf2a2SNan Zhou return ""; 56572c3ae33SNan Zhou } 56672c3ae33SNan Zhou std::string str = "?$expand="; 567b66cf2a2SNan Zhou bool queryTypeExpected = false; 56872c3ae33SNan Zhou switch (query.expandType) 56972c3ae33SNan Zhou { 57072c3ae33SNan Zhou case ExpandType::None: 571b66cf2a2SNan Zhou return ""; 57272c3ae33SNan Zhou case ExpandType::Links: 573b66cf2a2SNan Zhou queryTypeExpected = true; 57472c3ae33SNan Zhou str += '~'; 57572c3ae33SNan Zhou break; 57672c3ae33SNan Zhou case ExpandType::NotLinks: 577b66cf2a2SNan Zhou queryTypeExpected = true; 57872c3ae33SNan Zhou str += '.'; 57972c3ae33SNan Zhou break; 58072c3ae33SNan Zhou case ExpandType::Both: 581b66cf2a2SNan Zhou queryTypeExpected = true; 58272c3ae33SNan Zhou str += '*'; 58372c3ae33SNan Zhou break; 584b66cf2a2SNan Zhou } 585b66cf2a2SNan Zhou if (!queryTypeExpected) 586b66cf2a2SNan Zhou { 587b66cf2a2SNan Zhou return std::nullopt; 58872c3ae33SNan Zhou } 58972c3ae33SNan Zhou str += "($levels="; 59072c3ae33SNan Zhou str += std::to_string(query.expandLevel - 1); 59172c3ae33SNan Zhou str += ')'; 59272c3ae33SNan Zhou return str; 59372c3ae33SNan Zhou } 59472c3ae33SNan Zhou 5957cf436c9SEd Tanous class MultiAsyncResp : public std::enable_shared_from_this<MultiAsyncResp> 5967cf436c9SEd Tanous { 5977cf436c9SEd Tanous public: 5987cf436c9SEd Tanous // This object takes a single asyncResp object as the "final" one, then 5997cf436c9SEd Tanous // allows callers to attach sub-responses within the json tree that need 6007cf436c9SEd Tanous // to be executed and filled into their appropriate locations. This 6017cf436c9SEd Tanous // class manages the final "merge" of the json resources. 6028a592810SEd Tanous MultiAsyncResp(crow::App& appIn, 6037cf436c9SEd Tanous std::shared_ptr<bmcweb::AsyncResp> finalResIn) : 6048a592810SEd Tanous app(appIn), 6057cf436c9SEd Tanous finalRes(std::move(finalResIn)) 6067cf436c9SEd Tanous {} 6077cf436c9SEd Tanous 6087cf436c9SEd Tanous void addAwaitingResponse( 60902cad96eSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& res, 6107cf436c9SEd Tanous const nlohmann::json::json_pointer& finalExpandLocation) 6117cf436c9SEd Tanous { 6127cf436c9SEd Tanous res->res.setCompleteRequestHandler(std::bind_front( 61372c3ae33SNan Zhou placeResultStatic, shared_from_this(), finalExpandLocation)); 6147cf436c9SEd Tanous } 6157cf436c9SEd Tanous 61672c3ae33SNan Zhou void placeResult(const nlohmann::json::json_pointer& locationToPlace, 6177cf436c9SEd Tanous crow::Response& res) 6187cf436c9SEd Tanous { 6197cf436c9SEd Tanous nlohmann::json& finalObj = finalRes->res.jsonValue[locationToPlace]; 6207cf436c9SEd Tanous finalObj = std::move(res.jsonValue); 6217cf436c9SEd Tanous } 6227cf436c9SEd Tanous 62372c3ae33SNan Zhou // Handles the very first level of Expand, and starts a chain of sub-queries 62472c3ae33SNan Zhou // for deeper levels. 62572c3ae33SNan Zhou void startQuery(const Query& query) 62672c3ae33SNan Zhou { 62772c3ae33SNan Zhou std::vector<ExpandNode> nodes = 62872c3ae33SNan Zhou findNavigationReferences(query.expandType, finalRes->res.jsonValue); 6297cf436c9SEd Tanous BMCWEB_LOG_DEBUG << nodes.size() << " nodes to traverse"; 630b66cf2a2SNan Zhou const std::optional<std::string> queryStr = formatQueryForExpand(query); 631b66cf2a2SNan Zhou if (!queryStr) 632b66cf2a2SNan Zhou { 633b66cf2a2SNan Zhou messages::internalError(finalRes->res); 634b66cf2a2SNan Zhou return; 635b66cf2a2SNan Zhou } 6367cf436c9SEd Tanous for (const ExpandNode& node : nodes) 6377cf436c9SEd Tanous { 638b66cf2a2SNan Zhou const std::string subQuery = node.uri + *queryStr; 63972c3ae33SNan Zhou BMCWEB_LOG_DEBUG << "URL of subquery: " << subQuery; 6407cf436c9SEd Tanous std::error_code ec; 64172c3ae33SNan Zhou crow::Request newReq({boost::beast::http::verb::get, subQuery, 11}, 6427cf436c9SEd Tanous ec); 6437cf436c9SEd Tanous if (ec) 6447cf436c9SEd Tanous { 64572c3ae33SNan Zhou messages::internalError(finalRes->res); 6467cf436c9SEd Tanous return; 6477cf436c9SEd Tanous } 6487cf436c9SEd Tanous 6497cf436c9SEd Tanous auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 6507cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "setting completion handler on " 6517cf436c9SEd Tanous << &asyncResp->res; 65272c3ae33SNan Zhou 65372c3ae33SNan Zhou addAwaitingResponse(asyncResp, node.location); 6547cf436c9SEd Tanous app.handle(newReq, asyncResp); 6557cf436c9SEd Tanous } 6567cf436c9SEd Tanous } 6577cf436c9SEd Tanous 6587cf436c9SEd Tanous private: 65972c3ae33SNan Zhou static void 66072c3ae33SNan Zhou placeResultStatic(const std::shared_ptr<MultiAsyncResp>& multi, 6617cf436c9SEd Tanous const nlohmann::json::json_pointer& locationToPlace, 6627cf436c9SEd Tanous crow::Response& res) 6637cf436c9SEd Tanous { 66472c3ae33SNan Zhou multi->placeResult(locationToPlace, res); 6657cf436c9SEd Tanous } 6667cf436c9SEd Tanous 6677cf436c9SEd Tanous crow::App& app; 6687cf436c9SEd Tanous std::shared_ptr<bmcweb::AsyncResp> finalRes; 6697cf436c9SEd Tanous }; 6707cf436c9SEd Tanous 6712a68dc80SEd Tanous inline void processTopAndSkip(const Query& query, crow::Response& res) 6722a68dc80SEd Tanous { 6733648c8beSEd Tanous if (!query.skip && !query.top) 6743648c8beSEd Tanous { 6753648c8beSEd Tanous // No work to do. 6763648c8beSEd Tanous return; 6773648c8beSEd Tanous } 6782a68dc80SEd Tanous nlohmann::json::object_t* obj = 6792a68dc80SEd Tanous res.jsonValue.get_ptr<nlohmann::json::object_t*>(); 6802a68dc80SEd Tanous if (obj == nullptr) 6812a68dc80SEd Tanous { 6822a68dc80SEd Tanous // Shouldn't be possible. All responses should be objects. 6832a68dc80SEd Tanous messages::internalError(res); 6842a68dc80SEd Tanous return; 6852a68dc80SEd Tanous } 6862a68dc80SEd Tanous 6872a68dc80SEd Tanous BMCWEB_LOG_DEBUG << "Handling top/skip"; 6882a68dc80SEd Tanous nlohmann::json::object_t::iterator members = obj->find("Members"); 6892a68dc80SEd Tanous if (members == obj->end()) 6902a68dc80SEd Tanous { 6912a68dc80SEd Tanous // From the Redfish specification 7.3.1 6922a68dc80SEd Tanous // ... the HTTP 400 Bad Request status code with the 6932a68dc80SEd Tanous // QueryNotSupportedOnResource message from the Base Message Registry 6942a68dc80SEd Tanous // for any supported query parameters that apply only to resource 6952a68dc80SEd Tanous // collections but are used on singular resources. 6962a68dc80SEd Tanous messages::queryNotSupportedOnResource(res); 6972a68dc80SEd Tanous return; 6982a68dc80SEd Tanous } 6992a68dc80SEd Tanous 7002a68dc80SEd Tanous nlohmann::json::array_t* arr = 7012a68dc80SEd Tanous members->second.get_ptr<nlohmann::json::array_t*>(); 7022a68dc80SEd Tanous if (arr == nullptr) 7032a68dc80SEd Tanous { 7042a68dc80SEd Tanous messages::internalError(res); 7052a68dc80SEd Tanous return; 7062a68dc80SEd Tanous } 7072a68dc80SEd Tanous 7083648c8beSEd Tanous if (query.skip) 7093648c8beSEd Tanous { 7103648c8beSEd Tanous // Per section 7.3.1 of the Redfish specification, $skip is run before 7113648c8beSEd Tanous // $top Can only skip as many values as we have 7123648c8beSEd Tanous size_t skip = std::min(arr->size(), *query.skip); 7132a68dc80SEd Tanous arr->erase(arr->begin(), arr->begin() + static_cast<ssize_t>(skip)); 7143648c8beSEd Tanous } 7153648c8beSEd Tanous if (query.top) 7163648c8beSEd Tanous { 7173648c8beSEd Tanous size_t top = std::min(arr->size(), *query.top); 7182a68dc80SEd Tanous arr->erase(arr->begin() + static_cast<ssize_t>(top), arr->end()); 7192a68dc80SEd Tanous } 7203648c8beSEd Tanous } 7212a68dc80SEd Tanous 722e155ab54SNan Zhou // Given a JSON subtree |currRoot|, and its JSON pointer |currRootPtr| to the 723e155ab54SNan Zhou // |root| JSON in the async response, this function erases leaves whose keys are 724e155ab54SNan Zhou // not in the |shouldSelect| set. 725e155ab54SNan Zhou // |shouldSelect| contains all the properties that needs to be selected. 7261d8d9a3fSNan Zhou inline void 7271d8d9a3fSNan Zhou recursiveSelect(nlohmann::json& currRoot, 7281d8d9a3fSNan Zhou const nlohmann::json::json_pointer& currRootPtr, 729e155ab54SNan Zhou const std::unordered_set<std::string>& intermediatePaths, 7301d8d9a3fSNan Zhou const std::unordered_set<std::string>& properties) 731e155ab54SNan Zhou { 732e155ab54SNan Zhou nlohmann::json::object_t* object = 733e155ab54SNan Zhou currRoot.get_ptr<nlohmann::json::object_t*>(); 734e155ab54SNan Zhou if (object != nullptr) 735e155ab54SNan Zhou { 736e155ab54SNan Zhou BMCWEB_LOG_DEBUG << "Current JSON is an object: " << currRootPtr; 737e155ab54SNan Zhou auto it = currRoot.begin(); 738e155ab54SNan Zhou while (it != currRoot.end()) 739e155ab54SNan Zhou { 740e155ab54SNan Zhou auto nextIt = std::next(it); 741e155ab54SNan Zhou nlohmann::json::json_pointer childPtr = currRootPtr / it.key(); 742e155ab54SNan Zhou BMCWEB_LOG_DEBUG << "childPtr=" << childPtr; 743e155ab54SNan Zhou if (properties.contains(childPtr)) 744e155ab54SNan Zhou { 745e155ab54SNan Zhou it = nextIt; 746e155ab54SNan Zhou continue; 747e155ab54SNan Zhou } 748e155ab54SNan Zhou if (intermediatePaths.contains(childPtr)) 749e155ab54SNan Zhou { 750e155ab54SNan Zhou BMCWEB_LOG_DEBUG << "Recursively select: " << childPtr; 7511d8d9a3fSNan Zhou recursiveSelect(*it, childPtr, intermediatePaths, properties); 752e155ab54SNan Zhou it = nextIt; 753e155ab54SNan Zhou continue; 754e155ab54SNan Zhou } 755e155ab54SNan Zhou BMCWEB_LOG_DEBUG << childPtr << " is getting removed!"; 756e155ab54SNan Zhou it = currRoot.erase(it); 757e155ab54SNan Zhou } 758e155ab54SNan Zhou } 759e155ab54SNan Zhou } 760e155ab54SNan Zhou 761e155ab54SNan Zhou inline std::unordered_set<std::string> 762e155ab54SNan Zhou getIntermediatePaths(const std::unordered_set<std::string>& properties) 763e155ab54SNan Zhou { 764e155ab54SNan Zhou std::unordered_set<std::string> res; 765e155ab54SNan Zhou std::vector<std::string> segments; 766e155ab54SNan Zhou 767e155ab54SNan Zhou for (auto const& property : properties) 768e155ab54SNan Zhou { 769e155ab54SNan Zhou // Omit the root "/" and split all other segments 770e155ab54SNan Zhou boost::split(segments, property.substr(1), boost::is_any_of("/")); 771e155ab54SNan Zhou std::string path; 772e155ab54SNan Zhou if (!segments.empty()) 773e155ab54SNan Zhou { 774e155ab54SNan Zhou segments.pop_back(); 775e155ab54SNan Zhou } 776e155ab54SNan Zhou for (auto const& segment : segments) 777e155ab54SNan Zhou { 778e155ab54SNan Zhou path += '/'; 779e155ab54SNan Zhou path += segment; 780e155ab54SNan Zhou res.insert(path); 781e155ab54SNan Zhou } 782e155ab54SNan Zhou } 783e155ab54SNan Zhou return res; 784e155ab54SNan Zhou } 785e155ab54SNan Zhou 786e155ab54SNan Zhou inline void performSelect(nlohmann::json& root, 787e155ab54SNan Zhou const std::unordered_set<std::string>& properties) 788e155ab54SNan Zhou { 789e155ab54SNan Zhou std::unordered_set<std::string> intermediatePaths = 790e155ab54SNan Zhou getIntermediatePaths(properties); 791e155ab54SNan Zhou recursiveSelect(root, nlohmann::json::json_pointer(""), intermediatePaths, 7921d8d9a3fSNan Zhou properties); 793e155ab54SNan Zhou } 794e155ab54SNan Zhou 795e155ab54SNan Zhou // The current implementation of $select still has the following TODOs due to 796e155ab54SNan Zhou // ambiguity and/or complexity. 797e155ab54SNan Zhou // 1. select properties in array of objects; 798e155ab54SNan Zhou // https://github.com/DMTF/Redfish/issues/5188 was created for clarification. 799e155ab54SNan Zhou // 2. combined with $expand; https://github.com/DMTF/Redfish/issues/5058 was 800e155ab54SNan Zhou // created for clarification. 801e155ab54SNan Zhou // 2. respect the full odata spec; e.g., deduplication, namespace, star (*), 802e155ab54SNan Zhou // etc. 803e155ab54SNan Zhou inline void processSelect(crow::Response& intermediateResponse, 804e155ab54SNan Zhou const std::unordered_set<std::string>& shouldSelect) 805e155ab54SNan Zhou { 806e155ab54SNan Zhou BMCWEB_LOG_DEBUG << "Process $select quary parameter"; 807e155ab54SNan Zhou performSelect(intermediateResponse.jsonValue, shouldSelect); 808e155ab54SNan Zhou } 809e155ab54SNan Zhou 8107cf436c9SEd Tanous inline void 811593f6449SNan Zhou processAllParams(crow::App& app, const Query& query, 8127cf436c9SEd Tanous std::function<void(crow::Response&)>& completionHandler, 8137cf436c9SEd Tanous crow::Response& intermediateResponse) 814f4c99e70SEd Tanous { 815f4c99e70SEd Tanous if (!completionHandler) 816f4c99e70SEd Tanous { 817f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "Function was invalid?"; 818f4c99e70SEd Tanous return; 819f4c99e70SEd Tanous } 820f4c99e70SEd Tanous 821f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "Processing query params"; 822f4c99e70SEd Tanous // If the request failed, there's no reason to even try to run query 823f4c99e70SEd Tanous // params. 824f4c99e70SEd Tanous if (intermediateResponse.resultInt() < 200 || 825f4c99e70SEd Tanous intermediateResponse.resultInt() >= 400) 826f4c99e70SEd Tanous { 827f4c99e70SEd Tanous completionHandler(intermediateResponse); 828f4c99e70SEd Tanous return; 829f4c99e70SEd Tanous } 830f4c99e70SEd Tanous if (query.isOnly) 831f4c99e70SEd Tanous { 832f4c99e70SEd Tanous processOnly(app, intermediateResponse, completionHandler); 833f4c99e70SEd Tanous return; 834f4c99e70SEd Tanous } 8352a68dc80SEd Tanous 8363648c8beSEd Tanous if (query.top || query.skip) 8372a68dc80SEd Tanous { 8382a68dc80SEd Tanous processTopAndSkip(query, intermediateResponse); 8392a68dc80SEd Tanous } 8402a68dc80SEd Tanous 8417cf436c9SEd Tanous if (query.expandType != ExpandType::None) 8427cf436c9SEd Tanous { 8437cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "Executing expand query"; 844*13548d85SEd Tanous auto asyncResp = std::make_shared<bmcweb::AsyncResp>( 845*13548d85SEd Tanous std::move(intermediateResponse)); 8467cf436c9SEd Tanous 847*13548d85SEd Tanous asyncResp->res.setCompleteRequestHandler(std::move(completionHandler)); 848*13548d85SEd Tanous auto multi = std::make_shared<MultiAsyncResp>(app, asyncResp); 84972c3ae33SNan Zhou multi->startQuery(query); 8507cf436c9SEd Tanous return; 8517cf436c9SEd Tanous } 852e155ab54SNan Zhou 853e155ab54SNan Zhou // According to Redfish Spec Section 7.3.1, $select is the last parameter to 854e155ab54SNan Zhou // to process 855e155ab54SNan Zhou if (!query.selectedProperties.empty()) 856e155ab54SNan Zhou { 857e155ab54SNan Zhou processSelect(intermediateResponse, query.selectedProperties); 858e155ab54SNan Zhou } 859e155ab54SNan Zhou 860f4c99e70SEd Tanous completionHandler(intermediateResponse); 861f4c99e70SEd Tanous } 862f4c99e70SEd Tanous 863f4c99e70SEd Tanous } // namespace query_param 864f4c99e70SEd Tanous } // namespace redfish 865