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> 18d5c80ad9SNan Zhou #include <boost/url/params_view.hpp> 19d5c80ad9SNan Zhou #include <boost/url/string.hpp> 20d5c80ad9SNan Zhou #include <nlohmann/json.hpp> 21d5c80ad9SNan Zhou 22d5c80ad9SNan Zhou #include <algorithm> 23e155ab54SNan Zhou #include <array> 24e155ab54SNan Zhou #include <cctype> 257cf436c9SEd Tanous #include <charconv> 26*827c4902SNan Zhou #include <compare> 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> 34f4c99e70SEd Tanous #include <string> 35f4c99e70SEd Tanous #include <string_view> 36d5c80ad9SNan Zhou #include <system_error> 377cf436c9SEd Tanous #include <utility> 38f4c99e70SEd Tanous #include <vector> 39f4c99e70SEd Tanous 40d5c80ad9SNan Zhou // IWYU pragma: no_include <boost/url/impl/params_view.hpp> 41d5c80ad9SNan Zhou // IWYU pragma: no_include <boost/beast/http/impl/message.hpp> 42d5c80ad9SNan Zhou // IWYU pragma: no_include <boost/intrusive/detail/list_iterator.hpp> 43e155ab54SNan Zhou // IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp> 44e155ab54SNan Zhou // IWYU pragma: no_include <boost/iterator/iterator_facade.hpp> 45e155ab54SNan Zhou // IWYU pragma: no_include <boost/type_index/type_index_facade.hpp> 46d5c80ad9SNan Zhou // IWYU pragma: no_include <stdint.h> 47d5c80ad9SNan Zhou 48f4c99e70SEd Tanous namespace redfish 49f4c99e70SEd Tanous { 50f4c99e70SEd Tanous namespace query_param 51f4c99e70SEd Tanous { 52ce8ea743SJiaqing Zhao inline constexpr size_t maxEntriesPerPage = 1000; 53f4c99e70SEd Tanous 547cf436c9SEd Tanous enum class ExpandType : uint8_t 557cf436c9SEd Tanous { 567cf436c9SEd Tanous None, 577cf436c9SEd Tanous Links, 587cf436c9SEd Tanous NotLinks, 597cf436c9SEd Tanous Both, 607cf436c9SEd Tanous }; 617cf436c9SEd Tanous 62*827c4902SNan Zhou // A simple implementation of Trie to help |recursiveSelect|. 63*827c4902SNan Zhou class SelectTrieNode 64*827c4902SNan Zhou { 65*827c4902SNan Zhou public: 66*827c4902SNan Zhou SelectTrieNode() = default; 67*827c4902SNan Zhou 68*827c4902SNan Zhou const SelectTrieNode* find(const std::string& jsonKey) const 69*827c4902SNan Zhou { 70*827c4902SNan Zhou auto it = children.find(jsonKey); 71*827c4902SNan Zhou if (it == children.end()) 72*827c4902SNan Zhou { 73*827c4902SNan Zhou return nullptr; 74*827c4902SNan Zhou } 75*827c4902SNan Zhou return &it->second; 76*827c4902SNan Zhou } 77*827c4902SNan Zhou 78*827c4902SNan Zhou // Creates a new node if the key doesn't exist, returns the reference to the 79*827c4902SNan Zhou // newly created node; otherwise, return the reference to the existing node 80*827c4902SNan Zhou SelectTrieNode* emplace(std::string_view jsonKey) 81*827c4902SNan Zhou { 82*827c4902SNan Zhou auto [it, _] = children.emplace(jsonKey, SelectTrieNode{}); 83*827c4902SNan Zhou return &it->second; 84*827c4902SNan Zhou } 85*827c4902SNan Zhou 86*827c4902SNan Zhou bool empty() const 87*827c4902SNan Zhou { 88*827c4902SNan Zhou return children.empty(); 89*827c4902SNan Zhou } 90*827c4902SNan Zhou 91*827c4902SNan Zhou void clear() 92*827c4902SNan Zhou { 93*827c4902SNan Zhou children.clear(); 94*827c4902SNan Zhou } 95*827c4902SNan Zhou 96*827c4902SNan Zhou void setToSelected() 97*827c4902SNan Zhou { 98*827c4902SNan Zhou selected = true; 99*827c4902SNan Zhou } 100*827c4902SNan Zhou 101*827c4902SNan Zhou bool isSelected() const 102*827c4902SNan Zhou { 103*827c4902SNan Zhou return selected; 104*827c4902SNan Zhou } 105*827c4902SNan Zhou 106*827c4902SNan Zhou private: 107*827c4902SNan Zhou std::map<std::string, SelectTrieNode, std::less<>> children; 108*827c4902SNan Zhou bool selected = false; 109*827c4902SNan Zhou }; 110*827c4902SNan Zhou 111*827c4902SNan Zhou // Validates the property in the $select parameter. Every character is among 112*827c4902SNan Zhou // [a-zA-Z0-9#@_.] (taken from Redfish spec, section 9.6 Properties) 113*827c4902SNan Zhou inline bool isSelectedPropertyAllowed(std::string_view property) 114*827c4902SNan Zhou { 115*827c4902SNan Zhou // These a magic number, but with it it's less likely that this code 116*827c4902SNan Zhou // introduces CVE; e.g., too large properties crash the service. 117*827c4902SNan Zhou constexpr int maxPropertyLength = 60; 118*827c4902SNan Zhou if (property.empty() || property.size() > maxPropertyLength) 119*827c4902SNan Zhou { 120*827c4902SNan Zhou return false; 121*827c4902SNan Zhou } 122*827c4902SNan Zhou for (char ch : property) 123*827c4902SNan Zhou { 124*827c4902SNan Zhou if (std::isalnum(static_cast<unsigned char>(ch)) == 0 && ch != '#' && 125*827c4902SNan Zhou ch != '@' && ch != '.') 126*827c4902SNan Zhou { 127*827c4902SNan Zhou return false; 128*827c4902SNan Zhou } 129*827c4902SNan Zhou } 130*827c4902SNan Zhou return true; 131*827c4902SNan Zhou } 132*827c4902SNan Zhou 133*827c4902SNan Zhou struct SelectTrie 134*827c4902SNan Zhou { 135*827c4902SNan Zhou SelectTrie() = default; 136*827c4902SNan Zhou 137*827c4902SNan Zhou // Inserts a $select value; returns false if the nestedProperty is illegal. 138*827c4902SNan Zhou bool insertNode(std::string_view nestedProperty) 139*827c4902SNan Zhou { 140*827c4902SNan Zhou if (nestedProperty.empty()) 141*827c4902SNan Zhou { 142*827c4902SNan Zhou return false; 143*827c4902SNan Zhou } 144*827c4902SNan Zhou SelectTrieNode* currNode = &root; 145*827c4902SNan Zhou size_t index = nestedProperty.find_first_of('/'); 146*827c4902SNan Zhou while (!nestedProperty.empty()) 147*827c4902SNan Zhou { 148*827c4902SNan Zhou std::string_view property = nestedProperty.substr(0, index); 149*827c4902SNan Zhou if (!isSelectedPropertyAllowed(property)) 150*827c4902SNan Zhou { 151*827c4902SNan Zhou return false; 152*827c4902SNan Zhou } 153*827c4902SNan Zhou currNode = currNode->emplace(property); 154*827c4902SNan Zhou if (index == std::string::npos) 155*827c4902SNan Zhou { 156*827c4902SNan Zhou break; 157*827c4902SNan Zhou } 158*827c4902SNan Zhou nestedProperty.remove_prefix(index + 1); 159*827c4902SNan Zhou index = nestedProperty.find_first_of('/'); 160*827c4902SNan Zhou } 161*827c4902SNan Zhou 162*827c4902SNan Zhou currNode->setToSelected(); 163*827c4902SNan Zhou return true; 164*827c4902SNan Zhou } 165*827c4902SNan Zhou 166*827c4902SNan Zhou SelectTrieNode root; 167*827c4902SNan Zhou }; 168*827c4902SNan Zhou 169a6b9125fSNan Zhou // The struct stores the parsed query parameters of the default Redfish route. 170f4c99e70SEd Tanous struct Query 171f4c99e70SEd Tanous { 172a6b9125fSNan Zhou // Only 173f4c99e70SEd Tanous bool isOnly = false; 174a6b9125fSNan Zhou // Expand 175a6b9125fSNan Zhou uint8_t expandLevel = 0; 1767cf436c9SEd Tanous ExpandType expandType = ExpandType::None; 177c937d2bfSEd Tanous 178c937d2bfSEd Tanous // Skip 1793648c8beSEd Tanous std::optional<size_t> skip = std::nullopt; 180c937d2bfSEd Tanous 181c937d2bfSEd Tanous // Top 1823648c8beSEd Tanous std::optional<size_t> top = std::nullopt; 183e155ab54SNan Zhou 184e155ab54SNan Zhou // Select 185*827c4902SNan Zhou SelectTrie selectTrie = {}; 186f4c99e70SEd Tanous }; 187f4c99e70SEd Tanous 188a6b9125fSNan Zhou // The struct defines how resource handlers in redfish-core/lib/ can handle 189a6b9125fSNan Zhou // query parameters themselves, so that the default Redfish route will delegate 190a6b9125fSNan Zhou // the processing. 191a6b9125fSNan Zhou struct QueryCapabilities 192a6b9125fSNan Zhou { 193a6b9125fSNan Zhou bool canDelegateOnly = false; 194c937d2bfSEd Tanous bool canDelegateTop = false; 195c937d2bfSEd Tanous bool canDelegateSkip = false; 196a6b9125fSNan Zhou uint8_t canDelegateExpandLevel = 0; 197e155ab54SNan Zhou bool canDelegateSelect = false; 198a6b9125fSNan Zhou }; 199a6b9125fSNan Zhou 200a6b9125fSNan Zhou // Delegates query parameters according to the given |queryCapabilities| 201a6b9125fSNan Zhou // This function doesn't check query parameter conflicts since the parse 202a6b9125fSNan Zhou // function will take care of it. 203a6b9125fSNan Zhou // Returns a delegated query object which can be used by individual resource 204a6b9125fSNan Zhou // handlers so that handlers don't need to query again. 205a6b9125fSNan Zhou inline Query delegate(const QueryCapabilities& queryCapabilities, Query& query) 206a6b9125fSNan Zhou { 207a6b9125fSNan Zhou Query delegated; 208a6b9125fSNan Zhou // delegate only 209a6b9125fSNan Zhou if (query.isOnly && queryCapabilities.canDelegateOnly) 210a6b9125fSNan Zhou { 211a6b9125fSNan Zhou delegated.isOnly = true; 212a6b9125fSNan Zhou query.isOnly = false; 213a6b9125fSNan Zhou } 214a6b9125fSNan Zhou // delegate expand as much as we can 215a6b9125fSNan Zhou if (query.expandType != ExpandType::None) 216a6b9125fSNan Zhou { 217a6b9125fSNan Zhou delegated.expandType = query.expandType; 218a6b9125fSNan Zhou if (query.expandLevel <= queryCapabilities.canDelegateExpandLevel) 219a6b9125fSNan Zhou { 220a6b9125fSNan Zhou query.expandType = ExpandType::None; 221a6b9125fSNan Zhou delegated.expandLevel = query.expandLevel; 222a6b9125fSNan Zhou query.expandLevel = 0; 223a6b9125fSNan Zhou } 224a6b9125fSNan Zhou else 225a6b9125fSNan Zhou { 226a6b9125fSNan Zhou query.expandLevel -= queryCapabilities.canDelegateExpandLevel; 227a6b9125fSNan Zhou delegated.expandLevel = queryCapabilities.canDelegateExpandLevel; 228a6b9125fSNan Zhou } 229a6b9125fSNan Zhou } 230c937d2bfSEd Tanous 231c937d2bfSEd Tanous // delegate top 2323648c8beSEd Tanous if (query.top && queryCapabilities.canDelegateTop) 233c937d2bfSEd Tanous { 234c937d2bfSEd Tanous delegated.top = query.top; 2353648c8beSEd Tanous query.top = std::nullopt; 236c937d2bfSEd Tanous } 237c937d2bfSEd Tanous 238c937d2bfSEd Tanous // delegate skip 2393648c8beSEd Tanous if (query.skip && queryCapabilities.canDelegateSkip) 240c937d2bfSEd Tanous { 241c937d2bfSEd Tanous delegated.skip = query.skip; 242c937d2bfSEd Tanous query.skip = 0; 243c937d2bfSEd Tanous } 244e155ab54SNan Zhou 245e155ab54SNan Zhou // delegate select 246*827c4902SNan Zhou if (!query.selectTrie.root.empty() && queryCapabilities.canDelegateSelect) 247e155ab54SNan Zhou { 248*827c4902SNan Zhou delegated.selectTrie = std::move(query.selectTrie); 249*827c4902SNan Zhou query.selectTrie.root.clear(); 250e155ab54SNan Zhou } 251a6b9125fSNan Zhou return delegated; 252a6b9125fSNan Zhou } 253a6b9125fSNan Zhou 2547cf436c9SEd Tanous inline bool getExpandType(std::string_view value, Query& query) 2557cf436c9SEd Tanous { 2567cf436c9SEd Tanous if (value.empty()) 2577cf436c9SEd Tanous { 2587cf436c9SEd Tanous return false; 2597cf436c9SEd Tanous } 2607cf436c9SEd Tanous switch (value[0]) 2617cf436c9SEd Tanous { 2627cf436c9SEd Tanous case '*': 2637cf436c9SEd Tanous query.expandType = ExpandType::Both; 2647cf436c9SEd Tanous break; 2657cf436c9SEd Tanous case '.': 2667cf436c9SEd Tanous query.expandType = ExpandType::NotLinks; 2677cf436c9SEd Tanous break; 2687cf436c9SEd Tanous case '~': 2697cf436c9SEd Tanous query.expandType = ExpandType::Links; 2707cf436c9SEd Tanous break; 2717cf436c9SEd Tanous default: 2727cf436c9SEd Tanous return false; 2737cf436c9SEd Tanous 2747cf436c9SEd Tanous break; 2757cf436c9SEd Tanous } 2767cf436c9SEd Tanous value.remove_prefix(1); 2777cf436c9SEd Tanous if (value.empty()) 2787cf436c9SEd Tanous { 2797cf436c9SEd Tanous query.expandLevel = 1; 2807cf436c9SEd Tanous return true; 2817cf436c9SEd Tanous } 2827cf436c9SEd Tanous constexpr std::string_view levels = "($levels="; 2837cf436c9SEd Tanous if (!value.starts_with(levels)) 2847cf436c9SEd Tanous { 2857cf436c9SEd Tanous return false; 2867cf436c9SEd Tanous } 2877cf436c9SEd Tanous value.remove_prefix(levels.size()); 2887cf436c9SEd Tanous 2897cf436c9SEd Tanous auto it = std::from_chars(value.data(), value.data() + value.size(), 2907cf436c9SEd Tanous query.expandLevel); 2917cf436c9SEd Tanous if (it.ec != std::errc()) 2927cf436c9SEd Tanous { 2937cf436c9SEd Tanous return false; 2947cf436c9SEd Tanous } 2957cf436c9SEd Tanous value.remove_prefix(static_cast<size_t>(it.ptr - value.data())); 2967cf436c9SEd Tanous return value == ")"; 2977cf436c9SEd Tanous } 2987cf436c9SEd Tanous 299c937d2bfSEd Tanous enum class QueryError 300c937d2bfSEd Tanous { 301c937d2bfSEd Tanous Ok, 302c937d2bfSEd Tanous OutOfRange, 303c937d2bfSEd Tanous ValueFormat, 304c937d2bfSEd Tanous }; 305c937d2bfSEd Tanous 306c937d2bfSEd Tanous inline QueryError getNumericParam(std::string_view value, size_t& param) 307c937d2bfSEd Tanous { 308c937d2bfSEd Tanous std::from_chars_result r = 309c937d2bfSEd Tanous std::from_chars(value.data(), value.data() + value.size(), param); 310c937d2bfSEd Tanous 311c937d2bfSEd Tanous // If the number wasn't representable in the type, it's out of range 312c937d2bfSEd Tanous if (r.ec == std::errc::result_out_of_range) 313c937d2bfSEd Tanous { 314c937d2bfSEd Tanous return QueryError::OutOfRange; 315c937d2bfSEd Tanous } 316c937d2bfSEd Tanous // All other errors are value format 317c937d2bfSEd Tanous if (r.ec != std::errc()) 318c937d2bfSEd Tanous { 319c937d2bfSEd Tanous return QueryError::ValueFormat; 320c937d2bfSEd Tanous } 321c937d2bfSEd Tanous return QueryError::Ok; 322c937d2bfSEd Tanous } 323c937d2bfSEd Tanous 324c937d2bfSEd Tanous inline QueryError getSkipParam(std::string_view value, Query& query) 325c937d2bfSEd Tanous { 3263648c8beSEd Tanous return getNumericParam(value, query.skip.emplace()); 327c937d2bfSEd Tanous } 328c937d2bfSEd Tanous 329c937d2bfSEd Tanous inline QueryError getTopParam(std::string_view value, Query& query) 330c937d2bfSEd Tanous { 3313648c8beSEd Tanous QueryError ret = getNumericParam(value, query.top.emplace()); 332c937d2bfSEd Tanous if (ret != QueryError::Ok) 333c937d2bfSEd Tanous { 334c937d2bfSEd Tanous return ret; 335c937d2bfSEd Tanous } 336c937d2bfSEd Tanous 337c937d2bfSEd Tanous // Range check for sanity. 338c937d2bfSEd Tanous if (query.top > maxEntriesPerPage) 339c937d2bfSEd Tanous { 340c937d2bfSEd Tanous return QueryError::OutOfRange; 341c937d2bfSEd Tanous } 342c937d2bfSEd Tanous 343c937d2bfSEd Tanous return QueryError::Ok; 344c937d2bfSEd Tanous } 345c937d2bfSEd Tanous 346e155ab54SNan Zhou // Parses and validates the $select parameter. 347e155ab54SNan Zhou // As per OData URL Conventions and Redfish Spec, the $select values shall be 348e155ab54SNan Zhou // comma separated Resource Path 349e155ab54SNan Zhou // Ref: 350e155ab54SNan Zhou // 1. https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 351e155ab54SNan Zhou // 2. 352e155ab54SNan Zhou // https://docs.oasis-open.org/odata/odata/v4.01/os/abnf/odata-abnf-construction-rules.txt 353e155ab54SNan Zhou inline bool getSelectParam(std::string_view value, Query& query) 354e155ab54SNan Zhou { 355e155ab54SNan Zhou std::vector<std::string> properties; 356e155ab54SNan Zhou boost::split(properties, value, boost::is_any_of(",")); 357e155ab54SNan Zhou if (properties.empty()) 358e155ab54SNan Zhou { 359e155ab54SNan Zhou return false; 360e155ab54SNan Zhou } 361e155ab54SNan Zhou // These a magic number, but with it it's less likely that this code 362e155ab54SNan Zhou // introduces CVE; e.g., too large properties crash the service. 363e155ab54SNan Zhou constexpr int maxNumProperties = 10; 364e155ab54SNan Zhou if (properties.size() > maxNumProperties) 365e155ab54SNan Zhou { 366e155ab54SNan Zhou return false; 367e155ab54SNan Zhou } 368*827c4902SNan Zhou for (const auto& property : properties) 369e155ab54SNan Zhou { 370*827c4902SNan Zhou if (!query.selectTrie.insertNode(property)) 371e155ab54SNan Zhou { 372e155ab54SNan Zhou return false; 373e155ab54SNan Zhou } 374e155ab54SNan Zhou } 375e155ab54SNan Zhou // Per the Redfish spec section 7.3.3, the service shall select certain 376e155ab54SNan Zhou // properties as if $select was omitted. 377e155ab54SNan Zhou constexpr std::array<std::string_view, 5> reservedProperties = { 378*827c4902SNan Zhou "@odata.id", "@odata.type", "@odata.context", "@odata.etag", "error"}; 379e155ab54SNan Zhou for (auto const& str : reservedProperties) 380e155ab54SNan Zhou { 381*827c4902SNan Zhou query.selectTrie.insertNode(str.data()); 382e155ab54SNan Zhou } 383e155ab54SNan Zhou return true; 384e155ab54SNan Zhou } 385e155ab54SNan Zhou 386f4c99e70SEd Tanous inline std::optional<Query> 387f4c99e70SEd Tanous parseParameters(const boost::urls::params_view& urlParams, 388f4c99e70SEd Tanous crow::Response& res) 389f4c99e70SEd Tanous { 390f4c99e70SEd Tanous Query ret; 391f4c99e70SEd Tanous for (const boost::urls::params_view::value_type& it : urlParams) 392f4c99e70SEd Tanous { 393f4c99e70SEd Tanous std::string_view key(it.key.data(), it.key.size()); 394f4c99e70SEd Tanous std::string_view value(it.value.data(), it.value.size()); 395f4c99e70SEd Tanous if (key == "only") 396f4c99e70SEd Tanous { 397f4c99e70SEd Tanous if (!it.value.empty()) 398f4c99e70SEd Tanous { 399f4c99e70SEd Tanous messages::queryParameterValueFormatError(res, value, key); 400f4c99e70SEd Tanous return std::nullopt; 401f4c99e70SEd Tanous } 402f4c99e70SEd Tanous ret.isOnly = true; 403f4c99e70SEd Tanous } 4045e52870bSEd Tanous else if (key == "$expand" && bmcwebInsecureEnableQueryParams) 4057cf436c9SEd Tanous { 4067cf436c9SEd Tanous if (!getExpandType(value, ret)) 4077cf436c9SEd Tanous { 4087cf436c9SEd Tanous messages::queryParameterValueFormatError(res, value, key); 4097cf436c9SEd Tanous return std::nullopt; 410f4c99e70SEd Tanous } 4117cf436c9SEd Tanous } 412c937d2bfSEd Tanous else if (key == "$top") 413c937d2bfSEd Tanous { 414c937d2bfSEd Tanous QueryError topRet = getTopParam(value, ret); 415c937d2bfSEd Tanous if (topRet == QueryError::ValueFormat) 416c937d2bfSEd Tanous { 417c937d2bfSEd Tanous messages::queryParameterValueFormatError(res, value, key); 418c937d2bfSEd Tanous return std::nullopt; 419c937d2bfSEd Tanous } 420c937d2bfSEd Tanous if (topRet == QueryError::OutOfRange) 421c937d2bfSEd Tanous { 422c937d2bfSEd Tanous messages::queryParameterOutOfRange( 423c937d2bfSEd Tanous res, value, "$top", 424a926c53eSJiaqing Zhao "0-" + std::to_string(maxEntriesPerPage)); 425c937d2bfSEd Tanous return std::nullopt; 426c937d2bfSEd Tanous } 427c937d2bfSEd Tanous } 428c937d2bfSEd Tanous else if (key == "$skip") 429c937d2bfSEd Tanous { 430c937d2bfSEd Tanous QueryError topRet = getSkipParam(value, ret); 431c937d2bfSEd Tanous if (topRet == QueryError::ValueFormat) 432c937d2bfSEd Tanous { 433c937d2bfSEd Tanous messages::queryParameterValueFormatError(res, value, key); 434c937d2bfSEd Tanous return std::nullopt; 435c937d2bfSEd Tanous } 436c937d2bfSEd Tanous if (topRet == QueryError::OutOfRange) 437c937d2bfSEd Tanous { 438c937d2bfSEd Tanous messages::queryParameterOutOfRange( 439c937d2bfSEd Tanous res, value, key, 440a926c53eSJiaqing Zhao "0-" + std::to_string(std::numeric_limits<size_t>::max())); 441c937d2bfSEd Tanous return std::nullopt; 442c937d2bfSEd Tanous } 443c937d2bfSEd Tanous } 444e155ab54SNan Zhou else if (key == "$select" && bmcwebInsecureEnableQueryParams) 445e155ab54SNan Zhou { 446e155ab54SNan Zhou if (!getSelectParam(value, ret)) 447e155ab54SNan Zhou { 448e155ab54SNan Zhou messages::queryParameterValueFormatError(res, value, key); 449e155ab54SNan Zhou return std::nullopt; 450e155ab54SNan Zhou } 451e155ab54SNan Zhou } 4527cf436c9SEd Tanous else 4537cf436c9SEd Tanous { 4547cf436c9SEd Tanous // Intentionally ignore other errors Redfish spec, 7.3.1 4557cf436c9SEd Tanous if (key.starts_with("$")) 4567cf436c9SEd Tanous { 4577cf436c9SEd Tanous // Services shall return... The HTTP 501 Not Implemented 4587cf436c9SEd Tanous // status code for any unsupported query parameters that 4597cf436c9SEd Tanous // start with $ . 4607cf436c9SEd Tanous messages::queryParameterValueFormatError(res, value, key); 4617cf436c9SEd Tanous res.result(boost::beast::http::status::not_implemented); 4627cf436c9SEd Tanous return std::nullopt; 4637cf436c9SEd Tanous } 4647cf436c9SEd Tanous // "Shall ignore unknown or unsupported query parameters that do 4657cf436c9SEd Tanous // not begin with $ ." 4667cf436c9SEd Tanous } 4677cf436c9SEd Tanous } 4687cf436c9SEd Tanous 469*827c4902SNan Zhou if (ret.expandType != ExpandType::None && !ret.selectTrie.root.empty()) 470e155ab54SNan Zhou { 471e155ab54SNan Zhou messages::queryCombinationInvalid(res); 472e155ab54SNan Zhou return std::nullopt; 473e155ab54SNan Zhou } 474e155ab54SNan Zhou 475f4c99e70SEd Tanous return ret; 476f4c99e70SEd Tanous } 477f4c99e70SEd Tanous 478f4c99e70SEd Tanous inline bool processOnly(crow::App& app, crow::Response& res, 479f4c99e70SEd Tanous std::function<void(crow::Response&)>& completionHandler) 480f4c99e70SEd Tanous { 481f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "Processing only query param"; 482f4c99e70SEd Tanous auto itMembers = res.jsonValue.find("Members"); 483f4c99e70SEd Tanous if (itMembers == res.jsonValue.end()) 484f4c99e70SEd Tanous { 485f4c99e70SEd Tanous messages::queryNotSupportedOnResource(res); 486f4c99e70SEd Tanous completionHandler(res); 487f4c99e70SEd Tanous return false; 488f4c99e70SEd Tanous } 489f4c99e70SEd Tanous auto itMemBegin = itMembers->begin(); 490f4c99e70SEd Tanous if (itMemBegin == itMembers->end() || itMembers->size() != 1) 491f4c99e70SEd Tanous { 492f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "Members contains " << itMembers->size() 493f4c99e70SEd Tanous << " element, returning full collection."; 494f4c99e70SEd Tanous completionHandler(res); 495f4c99e70SEd Tanous return false; 496f4c99e70SEd Tanous } 497f4c99e70SEd Tanous 498f4c99e70SEd Tanous auto itUrl = itMemBegin->find("@odata.id"); 499f4c99e70SEd Tanous if (itUrl == itMemBegin->end()) 500f4c99e70SEd Tanous { 501f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "No found odata.id"; 502f4c99e70SEd Tanous messages::internalError(res); 503f4c99e70SEd Tanous completionHandler(res); 504f4c99e70SEd Tanous return false; 505f4c99e70SEd Tanous } 506f4c99e70SEd Tanous const std::string* url = itUrl->get_ptr<const std::string*>(); 507f4c99e70SEd Tanous if (url == nullptr) 508f4c99e70SEd Tanous { 509f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "@odata.id wasn't a string????"; 510f4c99e70SEd Tanous messages::internalError(res); 511f4c99e70SEd Tanous completionHandler(res); 512f4c99e70SEd Tanous return false; 513f4c99e70SEd Tanous } 514f4c99e70SEd Tanous // TODO(Ed) copy request headers? 515f4c99e70SEd Tanous // newReq.session = req.session; 516f4c99e70SEd Tanous std::error_code ec; 517f4c99e70SEd Tanous crow::Request newReq({boost::beast::http::verb::get, *url, 11}, ec); 518f4c99e70SEd Tanous if (ec) 519f4c99e70SEd Tanous { 520f4c99e70SEd Tanous messages::internalError(res); 521f4c99e70SEd Tanous completionHandler(res); 522f4c99e70SEd Tanous return false; 523f4c99e70SEd Tanous } 524f4c99e70SEd Tanous 525f4c99e70SEd Tanous auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 526f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "setting completion handler on " << &asyncResp->res; 527f4c99e70SEd Tanous asyncResp->res.setCompleteRequestHandler(std::move(completionHandler)); 528f4c99e70SEd Tanous asyncResp->res.setIsAliveHelper(res.releaseIsAliveHelper()); 529f4c99e70SEd Tanous app.handle(newReq, asyncResp); 530f4c99e70SEd Tanous return true; 531f4c99e70SEd Tanous } 532f4c99e70SEd Tanous 5337cf436c9SEd Tanous struct ExpandNode 5347cf436c9SEd Tanous { 5357cf436c9SEd Tanous nlohmann::json::json_pointer location; 5367cf436c9SEd Tanous std::string uri; 5377cf436c9SEd Tanous 5387cf436c9SEd Tanous inline bool operator==(const ExpandNode& other) const 5397cf436c9SEd Tanous { 5407cf436c9SEd Tanous return location == other.location && uri == other.uri; 5417cf436c9SEd Tanous } 5427cf436c9SEd Tanous }; 5437cf436c9SEd Tanous 5447cf436c9SEd Tanous // Walks a json object looking for Redfish NavigationReference entries that 5457cf436c9SEd Tanous // might need resolved. It recursively walks the jsonResponse object, looking 5467cf436c9SEd Tanous // for links at every level, and returns a list (out) of locations within the 5477cf436c9SEd Tanous // tree that need to be expanded. The current json pointer location p is passed 5487cf436c9SEd Tanous // in to reference the current node that's being expanded, so it can be combined 5497cf436c9SEd Tanous // with the keys from the jsonResponse object 5507cf436c9SEd Tanous inline void findNavigationReferencesRecursive( 5517cf436c9SEd Tanous ExpandType eType, nlohmann::json& jsonResponse, 5527cf436c9SEd Tanous const nlohmann::json::json_pointer& p, bool inLinks, 5537cf436c9SEd Tanous std::vector<ExpandNode>& out) 5547cf436c9SEd Tanous { 5557cf436c9SEd Tanous // If no expand is needed, return early 5567cf436c9SEd Tanous if (eType == ExpandType::None) 5577cf436c9SEd Tanous { 5587cf436c9SEd Tanous return; 5597cf436c9SEd Tanous } 5607cf436c9SEd Tanous nlohmann::json::array_t* array = 5617cf436c9SEd Tanous jsonResponse.get_ptr<nlohmann::json::array_t*>(); 5627cf436c9SEd Tanous if (array != nullptr) 5637cf436c9SEd Tanous { 5647cf436c9SEd Tanous size_t index = 0; 5657cf436c9SEd Tanous // For arrays, walk every element in the array 5667cf436c9SEd Tanous for (auto& element : *array) 5677cf436c9SEd Tanous { 5687cf436c9SEd Tanous nlohmann::json::json_pointer newPtr = p / index; 5697cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr.to_string(); 5707cf436c9SEd Tanous findNavigationReferencesRecursive(eType, element, newPtr, inLinks, 5717cf436c9SEd Tanous out); 5727cf436c9SEd Tanous index++; 5737cf436c9SEd Tanous } 5747cf436c9SEd Tanous } 5757cf436c9SEd Tanous nlohmann::json::object_t* obj = 5767cf436c9SEd Tanous jsonResponse.get_ptr<nlohmann::json::object_t*>(); 5777cf436c9SEd Tanous if (obj == nullptr) 5787cf436c9SEd Tanous { 5797cf436c9SEd Tanous return; 5807cf436c9SEd Tanous } 5817cf436c9SEd Tanous // Navigation References only ever have a single element 5827cf436c9SEd Tanous if (obj->size() == 1) 5837cf436c9SEd Tanous { 5847cf436c9SEd Tanous if (obj->begin()->first == "@odata.id") 5857cf436c9SEd Tanous { 5867cf436c9SEd Tanous const std::string* uri = 5877cf436c9SEd Tanous obj->begin()->second.get_ptr<const std::string*>(); 5887cf436c9SEd Tanous if (uri != nullptr) 5897cf436c9SEd Tanous { 5907cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "Found element at " << p.to_string(); 5917cf436c9SEd Tanous out.push_back({p, *uri}); 5927cf436c9SEd Tanous } 5937cf436c9SEd Tanous } 5947cf436c9SEd Tanous } 5957cf436c9SEd Tanous // Loop the object and look for links 5967cf436c9SEd Tanous for (auto& element : *obj) 5977cf436c9SEd Tanous { 598e479ad58SNan Zhou bool localInLinks = inLinks; 599e479ad58SNan Zhou if (!localInLinks) 6007cf436c9SEd Tanous { 6017cf436c9SEd Tanous // Check if this is a links node 602e479ad58SNan Zhou localInLinks = element.first == "Links"; 6037cf436c9SEd Tanous } 6047cf436c9SEd Tanous // Only traverse the parts of the tree the user asked for 6057cf436c9SEd Tanous // Per section 7.3 of the redfish specification 606e479ad58SNan Zhou if (localInLinks && eType == ExpandType::NotLinks) 6077cf436c9SEd Tanous { 6087cf436c9SEd Tanous continue; 6097cf436c9SEd Tanous } 610e479ad58SNan Zhou if (!localInLinks && eType == ExpandType::Links) 6117cf436c9SEd Tanous { 6127cf436c9SEd Tanous continue; 6137cf436c9SEd Tanous } 6147cf436c9SEd Tanous nlohmann::json::json_pointer newPtr = p / element.first; 6157cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr; 6167cf436c9SEd Tanous 6177cf436c9SEd Tanous findNavigationReferencesRecursive(eType, element.second, newPtr, 618e479ad58SNan Zhou localInLinks, out); 6197cf436c9SEd Tanous } 6207cf436c9SEd Tanous } 6217cf436c9SEd Tanous 6227cf436c9SEd Tanous inline std::vector<ExpandNode> 62372c3ae33SNan Zhou findNavigationReferences(ExpandType eType, nlohmann::json& jsonResponse) 6247cf436c9SEd Tanous { 6257cf436c9SEd Tanous std::vector<ExpandNode> ret; 62672c3ae33SNan Zhou const nlohmann::json::json_pointer root = nlohmann::json::json_pointer(""); 6277cf436c9SEd Tanous findNavigationReferencesRecursive(eType, jsonResponse, root, false, ret); 6287cf436c9SEd Tanous return ret; 6297cf436c9SEd Tanous } 6307cf436c9SEd Tanous 63172c3ae33SNan Zhou // Formats a query parameter string for the sub-query. 632b66cf2a2SNan Zhou // Returns std::nullopt on failures. 63372c3ae33SNan Zhou // This function shall handle $select when it is added. 63472c3ae33SNan Zhou // There is no need to handle parameters that's not campatible with $expand, 63572c3ae33SNan Zhou // e.g., $only, since this function will only be called in side $expand handlers 636b66cf2a2SNan Zhou inline std::optional<std::string> formatQueryForExpand(const Query& query) 63772c3ae33SNan Zhou { 63872c3ae33SNan Zhou // query.expandLevel<=1: no need to do subqueries 63972c3ae33SNan Zhou if (query.expandLevel <= 1) 64072c3ae33SNan Zhou { 641b66cf2a2SNan Zhou return ""; 64272c3ae33SNan Zhou } 64372c3ae33SNan Zhou std::string str = "?$expand="; 644b66cf2a2SNan Zhou bool queryTypeExpected = false; 64572c3ae33SNan Zhou switch (query.expandType) 64672c3ae33SNan Zhou { 64772c3ae33SNan Zhou case ExpandType::None: 648b66cf2a2SNan Zhou return ""; 64972c3ae33SNan Zhou case ExpandType::Links: 650b66cf2a2SNan Zhou queryTypeExpected = true; 65172c3ae33SNan Zhou str += '~'; 65272c3ae33SNan Zhou break; 65372c3ae33SNan Zhou case ExpandType::NotLinks: 654b66cf2a2SNan Zhou queryTypeExpected = true; 65572c3ae33SNan Zhou str += '.'; 65672c3ae33SNan Zhou break; 65772c3ae33SNan Zhou case ExpandType::Both: 658b66cf2a2SNan Zhou queryTypeExpected = true; 65972c3ae33SNan Zhou str += '*'; 66072c3ae33SNan Zhou break; 661b66cf2a2SNan Zhou } 662b66cf2a2SNan Zhou if (!queryTypeExpected) 663b66cf2a2SNan Zhou { 664b66cf2a2SNan Zhou return std::nullopt; 66572c3ae33SNan Zhou } 66672c3ae33SNan Zhou str += "($levels="; 66772c3ae33SNan Zhou str += std::to_string(query.expandLevel - 1); 66872c3ae33SNan Zhou str += ')'; 66972c3ae33SNan Zhou return str; 67072c3ae33SNan Zhou } 67172c3ae33SNan Zhou 6727cf436c9SEd Tanous class MultiAsyncResp : public std::enable_shared_from_this<MultiAsyncResp> 6737cf436c9SEd Tanous { 6747cf436c9SEd Tanous public: 6757cf436c9SEd Tanous // This object takes a single asyncResp object as the "final" one, then 6767cf436c9SEd Tanous // allows callers to attach sub-responses within the json tree that need 6777cf436c9SEd Tanous // to be executed and filled into their appropriate locations. This 6787cf436c9SEd Tanous // class manages the final "merge" of the json resources. 6798a592810SEd Tanous MultiAsyncResp(crow::App& appIn, 6807cf436c9SEd Tanous std::shared_ptr<bmcweb::AsyncResp> finalResIn) : 6818a592810SEd Tanous app(appIn), 6827cf436c9SEd Tanous finalRes(std::move(finalResIn)) 6837cf436c9SEd Tanous {} 6847cf436c9SEd Tanous 6857cf436c9SEd Tanous void addAwaitingResponse( 68602cad96eSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& res, 6877cf436c9SEd Tanous const nlohmann::json::json_pointer& finalExpandLocation) 6887cf436c9SEd Tanous { 6897cf436c9SEd Tanous res->res.setCompleteRequestHandler(std::bind_front( 69072c3ae33SNan Zhou placeResultStatic, shared_from_this(), finalExpandLocation)); 6917cf436c9SEd Tanous } 6927cf436c9SEd Tanous 69372c3ae33SNan Zhou void placeResult(const nlohmann::json::json_pointer& locationToPlace, 6947cf436c9SEd Tanous crow::Response& res) 6957cf436c9SEd Tanous { 6967cf436c9SEd Tanous nlohmann::json& finalObj = finalRes->res.jsonValue[locationToPlace]; 6977cf436c9SEd Tanous finalObj = std::move(res.jsonValue); 6987cf436c9SEd Tanous } 6997cf436c9SEd Tanous 70072c3ae33SNan Zhou // Handles the very first level of Expand, and starts a chain of sub-queries 70172c3ae33SNan Zhou // for deeper levels. 70272c3ae33SNan Zhou void startQuery(const Query& query) 70372c3ae33SNan Zhou { 70472c3ae33SNan Zhou std::vector<ExpandNode> nodes = 70572c3ae33SNan Zhou findNavigationReferences(query.expandType, finalRes->res.jsonValue); 7067cf436c9SEd Tanous BMCWEB_LOG_DEBUG << nodes.size() << " nodes to traverse"; 707b66cf2a2SNan Zhou const std::optional<std::string> queryStr = formatQueryForExpand(query); 708b66cf2a2SNan Zhou if (!queryStr) 709b66cf2a2SNan Zhou { 710b66cf2a2SNan Zhou messages::internalError(finalRes->res); 711b66cf2a2SNan Zhou return; 712b66cf2a2SNan Zhou } 7137cf436c9SEd Tanous for (const ExpandNode& node : nodes) 7147cf436c9SEd Tanous { 715b66cf2a2SNan Zhou const std::string subQuery = node.uri + *queryStr; 71672c3ae33SNan Zhou BMCWEB_LOG_DEBUG << "URL of subquery: " << subQuery; 7177cf436c9SEd Tanous std::error_code ec; 71872c3ae33SNan Zhou crow::Request newReq({boost::beast::http::verb::get, subQuery, 11}, 7197cf436c9SEd Tanous ec); 7207cf436c9SEd Tanous if (ec) 7217cf436c9SEd Tanous { 72272c3ae33SNan Zhou messages::internalError(finalRes->res); 7237cf436c9SEd Tanous return; 7247cf436c9SEd Tanous } 7257cf436c9SEd Tanous 7267cf436c9SEd Tanous auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); 7277cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "setting completion handler on " 7287cf436c9SEd Tanous << &asyncResp->res; 72972c3ae33SNan Zhou 73072c3ae33SNan Zhou addAwaitingResponse(asyncResp, node.location); 7317cf436c9SEd Tanous app.handle(newReq, asyncResp); 7327cf436c9SEd Tanous } 7337cf436c9SEd Tanous } 7347cf436c9SEd Tanous 7357cf436c9SEd Tanous private: 73672c3ae33SNan Zhou static void 73772c3ae33SNan Zhou placeResultStatic(const std::shared_ptr<MultiAsyncResp>& multi, 7387cf436c9SEd Tanous const nlohmann::json::json_pointer& locationToPlace, 7397cf436c9SEd Tanous crow::Response& res) 7407cf436c9SEd Tanous { 74172c3ae33SNan Zhou multi->placeResult(locationToPlace, res); 7427cf436c9SEd Tanous } 7437cf436c9SEd Tanous 7447cf436c9SEd Tanous crow::App& app; 7457cf436c9SEd Tanous std::shared_ptr<bmcweb::AsyncResp> finalRes; 7467cf436c9SEd Tanous }; 7477cf436c9SEd Tanous 7482a68dc80SEd Tanous inline void processTopAndSkip(const Query& query, crow::Response& res) 7492a68dc80SEd Tanous { 7503648c8beSEd Tanous if (!query.skip && !query.top) 7513648c8beSEd Tanous { 7523648c8beSEd Tanous // No work to do. 7533648c8beSEd Tanous return; 7543648c8beSEd Tanous } 7552a68dc80SEd Tanous nlohmann::json::object_t* obj = 7562a68dc80SEd Tanous res.jsonValue.get_ptr<nlohmann::json::object_t*>(); 7572a68dc80SEd Tanous if (obj == nullptr) 7582a68dc80SEd Tanous { 7592a68dc80SEd Tanous // Shouldn't be possible. All responses should be objects. 7602a68dc80SEd Tanous messages::internalError(res); 7612a68dc80SEd Tanous return; 7622a68dc80SEd Tanous } 7632a68dc80SEd Tanous 7642a68dc80SEd Tanous BMCWEB_LOG_DEBUG << "Handling top/skip"; 7652a68dc80SEd Tanous nlohmann::json::object_t::iterator members = obj->find("Members"); 7662a68dc80SEd Tanous if (members == obj->end()) 7672a68dc80SEd Tanous { 7682a68dc80SEd Tanous // From the Redfish specification 7.3.1 7692a68dc80SEd Tanous // ... the HTTP 400 Bad Request status code with the 7702a68dc80SEd Tanous // QueryNotSupportedOnResource message from the Base Message Registry 7712a68dc80SEd Tanous // for any supported query parameters that apply only to resource 7722a68dc80SEd Tanous // collections but are used on singular resources. 7732a68dc80SEd Tanous messages::queryNotSupportedOnResource(res); 7742a68dc80SEd Tanous return; 7752a68dc80SEd Tanous } 7762a68dc80SEd Tanous 7772a68dc80SEd Tanous nlohmann::json::array_t* arr = 7782a68dc80SEd Tanous members->second.get_ptr<nlohmann::json::array_t*>(); 7792a68dc80SEd Tanous if (arr == nullptr) 7802a68dc80SEd Tanous { 7812a68dc80SEd Tanous messages::internalError(res); 7822a68dc80SEd Tanous return; 7832a68dc80SEd Tanous } 7842a68dc80SEd Tanous 7853648c8beSEd Tanous if (query.skip) 7863648c8beSEd Tanous { 7873648c8beSEd Tanous // Per section 7.3.1 of the Redfish specification, $skip is run before 7883648c8beSEd Tanous // $top Can only skip as many values as we have 7893648c8beSEd Tanous size_t skip = std::min(arr->size(), *query.skip); 7902a68dc80SEd Tanous arr->erase(arr->begin(), arr->begin() + static_cast<ssize_t>(skip)); 7913648c8beSEd Tanous } 7923648c8beSEd Tanous if (query.top) 7933648c8beSEd Tanous { 7943648c8beSEd Tanous size_t top = std::min(arr->size(), *query.top); 7952a68dc80SEd Tanous arr->erase(arr->begin() + static_cast<ssize_t>(top), arr->end()); 7962a68dc80SEd Tanous } 7973648c8beSEd Tanous } 7982a68dc80SEd Tanous 799*827c4902SNan Zhou // Given a JSON subtree |currRoot|, this function erases leaves whose keys are 800*827c4902SNan Zhou // not in the |currNode| Trie node. 801*827c4902SNan Zhou inline void recursiveSelect(nlohmann::json& currRoot, 802*827c4902SNan Zhou const SelectTrieNode& currNode) 803e155ab54SNan Zhou { 804e155ab54SNan Zhou nlohmann::json::object_t* object = 805e155ab54SNan Zhou currRoot.get_ptr<nlohmann::json::object_t*>(); 806e155ab54SNan Zhou if (object != nullptr) 807e155ab54SNan Zhou { 808*827c4902SNan Zhou BMCWEB_LOG_DEBUG << "Current JSON is an object"; 809e155ab54SNan Zhou auto it = currRoot.begin(); 810e155ab54SNan Zhou while (it != currRoot.end()) 811e155ab54SNan Zhou { 812e155ab54SNan Zhou auto nextIt = std::next(it); 813*827c4902SNan Zhou BMCWEB_LOG_DEBUG << "key=" << it.key(); 814*827c4902SNan Zhou const SelectTrieNode* nextNode = currNode.find(it.key()); 815*827c4902SNan Zhou if (nextNode != nullptr && nextNode->isSelected()) 816e155ab54SNan Zhou { 817e155ab54SNan Zhou it = nextIt; 818e155ab54SNan Zhou continue; 819e155ab54SNan Zhou } 820*827c4902SNan Zhou if (nextNode != nullptr) 821e155ab54SNan Zhou { 822*827c4902SNan Zhou BMCWEB_LOG_DEBUG << "Recursively select: " << it.key(); 823*827c4902SNan Zhou recursiveSelect(*it, *nextNode); 824e155ab54SNan Zhou it = nextIt; 825e155ab54SNan Zhou continue; 826e155ab54SNan Zhou } 827*827c4902SNan Zhou BMCWEB_LOG_DEBUG << it.key() << " is getting removed!"; 828e155ab54SNan Zhou it = currRoot.erase(it); 829e155ab54SNan Zhou } 830e155ab54SNan Zhou } 831e155ab54SNan Zhou } 832e155ab54SNan Zhou 833e155ab54SNan Zhou // The current implementation of $select still has the following TODOs due to 834e155ab54SNan Zhou // ambiguity and/or complexity. 835e155ab54SNan Zhou // 1. select properties in array of objects; 836e155ab54SNan Zhou // https://github.com/DMTF/Redfish/issues/5188 was created for clarification. 837e155ab54SNan Zhou // 2. combined with $expand; https://github.com/DMTF/Redfish/issues/5058 was 838e155ab54SNan Zhou // created for clarification. 839*827c4902SNan Zhou // 3. respect the full odata spec; e.g., deduplication, namespace, star (*), 840e155ab54SNan Zhou // etc. 841e155ab54SNan Zhou inline void processSelect(crow::Response& intermediateResponse, 842*827c4902SNan Zhou const SelectTrieNode& trieRoot) 843e155ab54SNan Zhou { 844e155ab54SNan Zhou BMCWEB_LOG_DEBUG << "Process $select quary parameter"; 845*827c4902SNan Zhou recursiveSelect(intermediateResponse.jsonValue, trieRoot); 846e155ab54SNan Zhou } 847e155ab54SNan Zhou 8487cf436c9SEd Tanous inline void 849593f6449SNan Zhou processAllParams(crow::App& app, const Query& query, 8507cf436c9SEd Tanous std::function<void(crow::Response&)>& completionHandler, 8517cf436c9SEd Tanous crow::Response& intermediateResponse) 852f4c99e70SEd Tanous { 853f4c99e70SEd Tanous if (!completionHandler) 854f4c99e70SEd Tanous { 855f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "Function was invalid?"; 856f4c99e70SEd Tanous return; 857f4c99e70SEd Tanous } 858f4c99e70SEd Tanous 859f4c99e70SEd Tanous BMCWEB_LOG_DEBUG << "Processing query params"; 860f4c99e70SEd Tanous // If the request failed, there's no reason to even try to run query 861f4c99e70SEd Tanous // params. 862f4c99e70SEd Tanous if (intermediateResponse.resultInt() < 200 || 863f4c99e70SEd Tanous intermediateResponse.resultInt() >= 400) 864f4c99e70SEd Tanous { 865f4c99e70SEd Tanous completionHandler(intermediateResponse); 866f4c99e70SEd Tanous return; 867f4c99e70SEd Tanous } 868f4c99e70SEd Tanous if (query.isOnly) 869f4c99e70SEd Tanous { 870f4c99e70SEd Tanous processOnly(app, intermediateResponse, completionHandler); 871f4c99e70SEd Tanous return; 872f4c99e70SEd Tanous } 8732a68dc80SEd Tanous 8743648c8beSEd Tanous if (query.top || query.skip) 8752a68dc80SEd Tanous { 8762a68dc80SEd Tanous processTopAndSkip(query, intermediateResponse); 8772a68dc80SEd Tanous } 8782a68dc80SEd Tanous 8797cf436c9SEd Tanous if (query.expandType != ExpandType::None) 8807cf436c9SEd Tanous { 8817cf436c9SEd Tanous BMCWEB_LOG_DEBUG << "Executing expand query"; 88213548d85SEd Tanous auto asyncResp = std::make_shared<bmcweb::AsyncResp>( 88313548d85SEd Tanous std::move(intermediateResponse)); 8847cf436c9SEd Tanous 88513548d85SEd Tanous asyncResp->res.setCompleteRequestHandler(std::move(completionHandler)); 88613548d85SEd Tanous auto multi = std::make_shared<MultiAsyncResp>(app, asyncResp); 88772c3ae33SNan Zhou multi->startQuery(query); 8887cf436c9SEd Tanous return; 8897cf436c9SEd Tanous } 890e155ab54SNan Zhou 891e155ab54SNan Zhou // According to Redfish Spec Section 7.3.1, $select is the last parameter to 892e155ab54SNan Zhou // to process 893*827c4902SNan Zhou if (!query.selectTrie.root.empty()) 894e155ab54SNan Zhou { 895*827c4902SNan Zhou processSelect(intermediateResponse, query.selectTrie.root); 896e155ab54SNan Zhou } 897e155ab54SNan Zhou 898f4c99e70SEd Tanous completionHandler(intermediateResponse); 899f4c99e70SEd Tanous } 900f4c99e70SEd Tanous 901f4c99e70SEd Tanous } // namespace query_param 902f4c99e70SEd Tanous } // namespace redfish 903