xref: /openbmc/bmcweb/features/redfish/include/utils/query_param.hpp (revision a6b9125ff91500afed34dc923e9bafb90da75ec4)
1f4c99e70SEd Tanous #pragma once
2f4c99e70SEd Tanous #include "app.hpp"
3f4c99e70SEd Tanous #include "async_resp.hpp"
4f4c99e70SEd Tanous #include "error_messages.hpp"
5f4c99e70SEd Tanous #include "http_request.hpp"
6f4c99e70SEd Tanous #include "routing.hpp"
7f4c99e70SEd Tanous 
87cf436c9SEd Tanous #include <charconv>
9f4c99e70SEd Tanous #include <string>
10f4c99e70SEd Tanous #include <string_view>
117cf436c9SEd Tanous #include <utility>
12f4c99e70SEd Tanous #include <vector>
13f4c99e70SEd Tanous 
14f4c99e70SEd Tanous namespace redfish
15f4c99e70SEd Tanous {
16f4c99e70SEd Tanous namespace query_param
17f4c99e70SEd Tanous {
18f4c99e70SEd Tanous 
197cf436c9SEd Tanous enum class ExpandType : uint8_t
207cf436c9SEd Tanous {
217cf436c9SEd Tanous     None,
227cf436c9SEd Tanous     Links,
237cf436c9SEd Tanous     NotLinks,
247cf436c9SEd Tanous     Both,
257cf436c9SEd Tanous };
267cf436c9SEd Tanous 
27*a6b9125fSNan Zhou // The struct stores the parsed query parameters of the default Redfish route.
28f4c99e70SEd Tanous struct Query
29f4c99e70SEd Tanous {
30*a6b9125fSNan Zhou     // Only
31f4c99e70SEd Tanous     bool isOnly = false;
32*a6b9125fSNan Zhou     // Expand
33*a6b9125fSNan Zhou     uint8_t expandLevel = 0;
347cf436c9SEd Tanous     ExpandType expandType = ExpandType::None;
35f4c99e70SEd Tanous };
36f4c99e70SEd Tanous 
37*a6b9125fSNan Zhou // The struct defines how resource handlers in redfish-core/lib/ can handle
38*a6b9125fSNan Zhou // query parameters themselves, so that the default Redfish route will delegate
39*a6b9125fSNan Zhou // the processing.
40*a6b9125fSNan Zhou struct QueryCapabilities
41*a6b9125fSNan Zhou {
42*a6b9125fSNan Zhou     bool canDelegateOnly = false;
43*a6b9125fSNan Zhou     uint8_t canDelegateExpandLevel = 0;
44*a6b9125fSNan Zhou };
45*a6b9125fSNan Zhou 
46*a6b9125fSNan Zhou // Delegates query parameters according to the given |queryCapabilities|
47*a6b9125fSNan Zhou // This function doesn't check query parameter conflicts since the parse
48*a6b9125fSNan Zhou // function will take care of it.
49*a6b9125fSNan Zhou // Returns a delegated query object which can be used by individual resource
50*a6b9125fSNan Zhou // handlers so that handlers don't need to query again.
51*a6b9125fSNan Zhou inline Query delegate(const QueryCapabilities& queryCapabilities, Query& query)
52*a6b9125fSNan Zhou {
53*a6b9125fSNan Zhou     Query delegated;
54*a6b9125fSNan Zhou     // delegate only
55*a6b9125fSNan Zhou     if (query.isOnly && queryCapabilities.canDelegateOnly)
56*a6b9125fSNan Zhou     {
57*a6b9125fSNan Zhou         delegated.isOnly = true;
58*a6b9125fSNan Zhou         query.isOnly = false;
59*a6b9125fSNan Zhou     }
60*a6b9125fSNan Zhou     // delegate expand as much as we can
61*a6b9125fSNan Zhou     if (query.expandType != ExpandType::None)
62*a6b9125fSNan Zhou     {
63*a6b9125fSNan Zhou         delegated.expandType = query.expandType;
64*a6b9125fSNan Zhou         if (query.expandLevel <= queryCapabilities.canDelegateExpandLevel)
65*a6b9125fSNan Zhou         {
66*a6b9125fSNan Zhou             query.expandType = ExpandType::None;
67*a6b9125fSNan Zhou             delegated.expandLevel = query.expandLevel;
68*a6b9125fSNan Zhou             query.expandLevel = 0;
69*a6b9125fSNan Zhou         }
70*a6b9125fSNan Zhou         else
71*a6b9125fSNan Zhou         {
72*a6b9125fSNan Zhou             query.expandLevel -= queryCapabilities.canDelegateExpandLevel;
73*a6b9125fSNan Zhou             delegated.expandLevel = queryCapabilities.canDelegateExpandLevel;
74*a6b9125fSNan Zhou         }
75*a6b9125fSNan Zhou     }
76*a6b9125fSNan Zhou     return delegated;
77*a6b9125fSNan Zhou }
78*a6b9125fSNan Zhou 
797cf436c9SEd Tanous inline bool getExpandType(std::string_view value, Query& query)
807cf436c9SEd Tanous {
817cf436c9SEd Tanous     if (value.empty())
827cf436c9SEd Tanous     {
837cf436c9SEd Tanous         return false;
847cf436c9SEd Tanous     }
857cf436c9SEd Tanous     switch (value[0])
867cf436c9SEd Tanous     {
877cf436c9SEd Tanous         case '*':
887cf436c9SEd Tanous             query.expandType = ExpandType::Both;
897cf436c9SEd Tanous             break;
907cf436c9SEd Tanous         case '.':
917cf436c9SEd Tanous             query.expandType = ExpandType::NotLinks;
927cf436c9SEd Tanous             break;
937cf436c9SEd Tanous         case '~':
947cf436c9SEd Tanous             query.expandType = ExpandType::Links;
957cf436c9SEd Tanous             break;
967cf436c9SEd Tanous         default:
977cf436c9SEd Tanous             return false;
987cf436c9SEd Tanous 
997cf436c9SEd Tanous             break;
1007cf436c9SEd Tanous     }
1017cf436c9SEd Tanous     value.remove_prefix(1);
1027cf436c9SEd Tanous     if (value.empty())
1037cf436c9SEd Tanous     {
1047cf436c9SEd Tanous         query.expandLevel = 1;
1057cf436c9SEd Tanous         return true;
1067cf436c9SEd Tanous     }
1077cf436c9SEd Tanous     constexpr std::string_view levels = "($levels=";
1087cf436c9SEd Tanous     if (!value.starts_with(levels))
1097cf436c9SEd Tanous     {
1107cf436c9SEd Tanous         return false;
1117cf436c9SEd Tanous     }
1127cf436c9SEd Tanous     value.remove_prefix(levels.size());
1137cf436c9SEd Tanous 
1147cf436c9SEd Tanous     auto it = std::from_chars(value.data(), value.data() + value.size(),
1157cf436c9SEd Tanous                               query.expandLevel);
1167cf436c9SEd Tanous     if (it.ec != std::errc())
1177cf436c9SEd Tanous     {
1187cf436c9SEd Tanous         return false;
1197cf436c9SEd Tanous     }
1207cf436c9SEd Tanous     value.remove_prefix(static_cast<size_t>(it.ptr - value.data()));
1217cf436c9SEd Tanous     return value == ")";
1227cf436c9SEd Tanous }
1237cf436c9SEd Tanous 
124f4c99e70SEd Tanous inline std::optional<Query>
125f4c99e70SEd Tanous     parseParameters(const boost::urls::params_view& urlParams,
126f4c99e70SEd Tanous                     crow::Response& res)
127f4c99e70SEd Tanous {
128f4c99e70SEd Tanous     Query ret;
129f4c99e70SEd Tanous     for (const boost::urls::params_view::value_type& it : urlParams)
130f4c99e70SEd Tanous     {
131f4c99e70SEd Tanous         std::string_view key(it.key.data(), it.key.size());
132f4c99e70SEd Tanous         std::string_view value(it.value.data(), it.value.size());
133f4c99e70SEd Tanous         if (key == "only")
134f4c99e70SEd Tanous         {
135f4c99e70SEd Tanous             if (!it.value.empty())
136f4c99e70SEd Tanous             {
137f4c99e70SEd Tanous                 messages::queryParameterValueFormatError(res, value, key);
138f4c99e70SEd Tanous                 return std::nullopt;
139f4c99e70SEd Tanous             }
140f4c99e70SEd Tanous             ret.isOnly = true;
141f4c99e70SEd Tanous         }
1427cf436c9SEd Tanous         else if (key == "$expand")
1437cf436c9SEd Tanous         {
1447cf436c9SEd Tanous             if (!getExpandType(value, ret))
1457cf436c9SEd Tanous             {
1467cf436c9SEd Tanous                 messages::queryParameterValueFormatError(res, value, key);
1477cf436c9SEd Tanous                 return std::nullopt;
148f4c99e70SEd Tanous             }
1497cf436c9SEd Tanous         }
1507cf436c9SEd Tanous         else
1517cf436c9SEd Tanous         {
1527cf436c9SEd Tanous             // Intentionally ignore other errors Redfish spec, 7.3.1
1537cf436c9SEd Tanous             if (key.starts_with("$"))
1547cf436c9SEd Tanous             {
1557cf436c9SEd Tanous                 // Services shall return... The HTTP 501 Not Implemented
1567cf436c9SEd Tanous                 // status code for any unsupported query parameters that
1577cf436c9SEd Tanous                 // start with $ .
1587cf436c9SEd Tanous                 messages::queryParameterValueFormatError(res, value, key);
1597cf436c9SEd Tanous                 res.result(boost::beast::http::status::not_implemented);
1607cf436c9SEd Tanous                 return std::nullopt;
1617cf436c9SEd Tanous             }
1627cf436c9SEd Tanous             // "Shall ignore unknown or unsupported query parameters that do
1637cf436c9SEd Tanous             // not begin with $ ."
1647cf436c9SEd Tanous         }
1657cf436c9SEd Tanous     }
1667cf436c9SEd Tanous 
167f4c99e70SEd Tanous     return ret;
168f4c99e70SEd Tanous }
169f4c99e70SEd Tanous 
170f4c99e70SEd Tanous inline bool processOnly(crow::App& app, crow::Response& res,
171f4c99e70SEd Tanous                         std::function<void(crow::Response&)>& completionHandler)
172f4c99e70SEd Tanous {
173f4c99e70SEd Tanous     BMCWEB_LOG_DEBUG << "Processing only query param";
174f4c99e70SEd Tanous     auto itMembers = res.jsonValue.find("Members");
175f4c99e70SEd Tanous     if (itMembers == res.jsonValue.end())
176f4c99e70SEd Tanous     {
177f4c99e70SEd Tanous         messages::queryNotSupportedOnResource(res);
178f4c99e70SEd Tanous         completionHandler(res);
179f4c99e70SEd Tanous         return false;
180f4c99e70SEd Tanous     }
181f4c99e70SEd Tanous     auto itMemBegin = itMembers->begin();
182f4c99e70SEd Tanous     if (itMemBegin == itMembers->end() || itMembers->size() != 1)
183f4c99e70SEd Tanous     {
184f4c99e70SEd Tanous         BMCWEB_LOG_DEBUG << "Members contains " << itMembers->size()
185f4c99e70SEd Tanous                          << " element, returning full collection.";
186f4c99e70SEd Tanous         completionHandler(res);
187f4c99e70SEd Tanous         return false;
188f4c99e70SEd Tanous     }
189f4c99e70SEd Tanous 
190f4c99e70SEd Tanous     auto itUrl = itMemBegin->find("@odata.id");
191f4c99e70SEd Tanous     if (itUrl == itMemBegin->end())
192f4c99e70SEd Tanous     {
193f4c99e70SEd Tanous         BMCWEB_LOG_DEBUG << "No found odata.id";
194f4c99e70SEd Tanous         messages::internalError(res);
195f4c99e70SEd Tanous         completionHandler(res);
196f4c99e70SEd Tanous         return false;
197f4c99e70SEd Tanous     }
198f4c99e70SEd Tanous     const std::string* url = itUrl->get_ptr<const std::string*>();
199f4c99e70SEd Tanous     if (url == nullptr)
200f4c99e70SEd Tanous     {
201f4c99e70SEd Tanous         BMCWEB_LOG_DEBUG << "@odata.id wasn't a string????";
202f4c99e70SEd Tanous         messages::internalError(res);
203f4c99e70SEd Tanous         completionHandler(res);
204f4c99e70SEd Tanous         return false;
205f4c99e70SEd Tanous     }
206f4c99e70SEd Tanous     // TODO(Ed) copy request headers?
207f4c99e70SEd Tanous     // newReq.session = req.session;
208f4c99e70SEd Tanous     std::error_code ec;
209f4c99e70SEd Tanous     crow::Request newReq({boost::beast::http::verb::get, *url, 11}, ec);
210f4c99e70SEd Tanous     if (ec)
211f4c99e70SEd Tanous     {
212f4c99e70SEd Tanous         messages::internalError(res);
213f4c99e70SEd Tanous         completionHandler(res);
214f4c99e70SEd Tanous         return false;
215f4c99e70SEd Tanous     }
216f4c99e70SEd Tanous 
217f4c99e70SEd Tanous     auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
218f4c99e70SEd Tanous     BMCWEB_LOG_DEBUG << "setting completion handler on " << &asyncResp->res;
219f4c99e70SEd Tanous     asyncResp->res.setCompleteRequestHandler(std::move(completionHandler));
220f4c99e70SEd Tanous     asyncResp->res.setIsAliveHelper(res.releaseIsAliveHelper());
221f4c99e70SEd Tanous     app.handle(newReq, asyncResp);
222f4c99e70SEd Tanous     return true;
223f4c99e70SEd Tanous }
224f4c99e70SEd Tanous 
2257cf436c9SEd Tanous struct ExpandNode
2267cf436c9SEd Tanous {
2277cf436c9SEd Tanous     nlohmann::json::json_pointer location;
2287cf436c9SEd Tanous     std::string uri;
2297cf436c9SEd Tanous 
2307cf436c9SEd Tanous     inline bool operator==(const ExpandNode& other) const
2317cf436c9SEd Tanous     {
2327cf436c9SEd Tanous         return location == other.location && uri == other.uri;
2337cf436c9SEd Tanous     }
2347cf436c9SEd Tanous };
2357cf436c9SEd Tanous 
2367cf436c9SEd Tanous // Walks a json object looking for Redfish NavigationReference entries that
2377cf436c9SEd Tanous // might need resolved.  It recursively walks the jsonResponse object, looking
2387cf436c9SEd Tanous // for links at every level, and returns a list (out) of locations within the
2397cf436c9SEd Tanous // tree that need to be expanded.  The current json pointer location p is passed
2407cf436c9SEd Tanous // in to reference the current node that's being expanded, so it can be combined
2417cf436c9SEd Tanous // with the keys from the jsonResponse object
2427cf436c9SEd Tanous inline void findNavigationReferencesRecursive(
2437cf436c9SEd Tanous     ExpandType eType, nlohmann::json& jsonResponse,
2447cf436c9SEd Tanous     const nlohmann::json::json_pointer& p, bool inLinks,
2457cf436c9SEd Tanous     std::vector<ExpandNode>& out)
2467cf436c9SEd Tanous {
2477cf436c9SEd Tanous     // If no expand is needed, return early
2487cf436c9SEd Tanous     if (eType == ExpandType::None)
2497cf436c9SEd Tanous     {
2507cf436c9SEd Tanous         return;
2517cf436c9SEd Tanous     }
2527cf436c9SEd Tanous     nlohmann::json::array_t* array =
2537cf436c9SEd Tanous         jsonResponse.get_ptr<nlohmann::json::array_t*>();
2547cf436c9SEd Tanous     if (array != nullptr)
2557cf436c9SEd Tanous     {
2567cf436c9SEd Tanous         size_t index = 0;
2577cf436c9SEd Tanous         // For arrays, walk every element in the array
2587cf436c9SEd Tanous         for (auto& element : *array)
2597cf436c9SEd Tanous         {
2607cf436c9SEd Tanous             nlohmann::json::json_pointer newPtr = p / index;
2617cf436c9SEd Tanous             BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr.to_string();
2627cf436c9SEd Tanous             findNavigationReferencesRecursive(eType, element, newPtr, inLinks,
2637cf436c9SEd Tanous                                               out);
2647cf436c9SEd Tanous             index++;
2657cf436c9SEd Tanous         }
2667cf436c9SEd Tanous     }
2677cf436c9SEd Tanous     nlohmann::json::object_t* obj =
2687cf436c9SEd Tanous         jsonResponse.get_ptr<nlohmann::json::object_t*>();
2697cf436c9SEd Tanous     if (obj == nullptr)
2707cf436c9SEd Tanous     {
2717cf436c9SEd Tanous         return;
2727cf436c9SEd Tanous     }
2737cf436c9SEd Tanous     // Navigation References only ever have a single element
2747cf436c9SEd Tanous     if (obj->size() == 1)
2757cf436c9SEd Tanous     {
2767cf436c9SEd Tanous         if (obj->begin()->first == "@odata.id")
2777cf436c9SEd Tanous         {
2787cf436c9SEd Tanous             const std::string* uri =
2797cf436c9SEd Tanous                 obj->begin()->second.get_ptr<const std::string*>();
2807cf436c9SEd Tanous             if (uri != nullptr)
2817cf436c9SEd Tanous             {
2827cf436c9SEd Tanous                 BMCWEB_LOG_DEBUG << "Found element at " << p.to_string();
2837cf436c9SEd Tanous                 out.push_back({p, *uri});
2847cf436c9SEd Tanous             }
2857cf436c9SEd Tanous         }
2867cf436c9SEd Tanous     }
2877cf436c9SEd Tanous     // Loop the object and look for links
2887cf436c9SEd Tanous     for (auto& element : *obj)
2897cf436c9SEd Tanous     {
2907cf436c9SEd Tanous         if (!inLinks)
2917cf436c9SEd Tanous         {
2927cf436c9SEd Tanous             // Check if this is a links node
2937cf436c9SEd Tanous             inLinks = element.first == "Links";
2947cf436c9SEd Tanous         }
2957cf436c9SEd Tanous         // Only traverse the parts of the tree the user asked for
2967cf436c9SEd Tanous         // Per section 7.3 of the redfish specification
2977cf436c9SEd Tanous         if (inLinks && eType == ExpandType::NotLinks)
2987cf436c9SEd Tanous         {
2997cf436c9SEd Tanous             continue;
3007cf436c9SEd Tanous         }
3017cf436c9SEd Tanous         if (!inLinks && eType == ExpandType::Links)
3027cf436c9SEd Tanous         {
3037cf436c9SEd Tanous             continue;
3047cf436c9SEd Tanous         }
3057cf436c9SEd Tanous         nlohmann::json::json_pointer newPtr = p / element.first;
3067cf436c9SEd Tanous         BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr;
3077cf436c9SEd Tanous 
3087cf436c9SEd Tanous         findNavigationReferencesRecursive(eType, element.second, newPtr,
3097cf436c9SEd Tanous                                           inLinks, out);
3107cf436c9SEd Tanous     }
3117cf436c9SEd Tanous }
3127cf436c9SEd Tanous 
3137cf436c9SEd Tanous inline std::vector<ExpandNode>
3147cf436c9SEd Tanous     findNavigationReferences(ExpandType eType, nlohmann::json& jsonResponse,
3157cf436c9SEd Tanous                              const nlohmann::json::json_pointer& root)
3167cf436c9SEd Tanous {
3177cf436c9SEd Tanous     std::vector<ExpandNode> ret;
3187cf436c9SEd Tanous     findNavigationReferencesRecursive(eType, jsonResponse, root, false, ret);
3197cf436c9SEd Tanous     return ret;
3207cf436c9SEd Tanous }
3217cf436c9SEd Tanous 
3227cf436c9SEd Tanous class MultiAsyncResp : public std::enable_shared_from_this<MultiAsyncResp>
3237cf436c9SEd Tanous {
3247cf436c9SEd Tanous   public:
3257cf436c9SEd Tanous     // This object takes a single asyncResp object as the "final" one, then
3267cf436c9SEd Tanous     // allows callers to attach sub-responses within the json tree that need
3277cf436c9SEd Tanous     // to be executed and filled into their appropriate locations.  This
3287cf436c9SEd Tanous     // class manages the final "merge" of the json resources.
3297cf436c9SEd Tanous     MultiAsyncResp(crow::App& app,
3307cf436c9SEd Tanous                    std::shared_ptr<bmcweb::AsyncResp> finalResIn) :
3317cf436c9SEd Tanous         app(app),
3327cf436c9SEd Tanous         finalRes(std::move(finalResIn))
3337cf436c9SEd Tanous     {}
3347cf436c9SEd Tanous 
3357cf436c9SEd Tanous     void addAwaitingResponse(
3367cf436c9SEd Tanous         Query query, std::shared_ptr<bmcweb::AsyncResp>& res,
3377cf436c9SEd Tanous         const nlohmann::json::json_pointer& finalExpandLocation)
3387cf436c9SEd Tanous     {
3397cf436c9SEd Tanous         res->res.setCompleteRequestHandler(std::bind_front(
3407cf436c9SEd Tanous             onEndStatic, shared_from_this(), query, finalExpandLocation));
3417cf436c9SEd Tanous     }
3427cf436c9SEd Tanous 
3437cf436c9SEd Tanous     void onEnd(Query query, const nlohmann::json::json_pointer& locationToPlace,
3447cf436c9SEd Tanous                crow::Response& res)
3457cf436c9SEd Tanous     {
3467cf436c9SEd Tanous         nlohmann::json& finalObj = finalRes->res.jsonValue[locationToPlace];
3477cf436c9SEd Tanous         finalObj = std::move(res.jsonValue);
3487cf436c9SEd Tanous 
3497cf436c9SEd Tanous         if (query.expandLevel <= 0)
3507cf436c9SEd Tanous         {
3517cf436c9SEd Tanous             // Last level to expand, no need to go deeper
3527cf436c9SEd Tanous             return;
3537cf436c9SEd Tanous         }
3547cf436c9SEd Tanous         // Now decrease the depth by one to account for the tree node we
3557cf436c9SEd Tanous         // just resolved
3567cf436c9SEd Tanous         query.expandLevel--;
3577cf436c9SEd Tanous 
3587cf436c9SEd Tanous         std::vector<ExpandNode> nodes = findNavigationReferences(
3597cf436c9SEd Tanous             query.expandType, finalObj, locationToPlace);
3607cf436c9SEd Tanous         BMCWEB_LOG_DEBUG << nodes.size() << " nodes to traverse";
3617cf436c9SEd Tanous         for (const ExpandNode& node : nodes)
3627cf436c9SEd Tanous         {
3637cf436c9SEd Tanous             BMCWEB_LOG_DEBUG << "Expanding " << locationToPlace;
3647cf436c9SEd Tanous             std::error_code ec;
3657cf436c9SEd Tanous             crow::Request newReq({boost::beast::http::verb::get, node.uri, 11},
3667cf436c9SEd Tanous                                  ec);
3677cf436c9SEd Tanous             if (ec)
3687cf436c9SEd Tanous             {
3697cf436c9SEd Tanous                 messages::internalError(res);
3707cf436c9SEd Tanous                 return;
3717cf436c9SEd Tanous             }
3727cf436c9SEd Tanous 
3737cf436c9SEd Tanous             auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
3747cf436c9SEd Tanous             BMCWEB_LOG_DEBUG << "setting completion handler on "
3757cf436c9SEd Tanous                              << &asyncResp->res;
3767cf436c9SEd Tanous             addAwaitingResponse(query, asyncResp, node.location);
3777cf436c9SEd Tanous             app.handle(newReq, asyncResp);
3787cf436c9SEd Tanous         }
3797cf436c9SEd Tanous     }
3807cf436c9SEd Tanous 
3817cf436c9SEd Tanous   private:
3827cf436c9SEd Tanous     static void onEndStatic(const std::shared_ptr<MultiAsyncResp>& multi,
3837cf436c9SEd Tanous                             Query query,
3847cf436c9SEd Tanous                             const nlohmann::json::json_pointer& locationToPlace,
3857cf436c9SEd Tanous                             crow::Response& res)
3867cf436c9SEd Tanous     {
3877cf436c9SEd Tanous         multi->onEnd(query, locationToPlace, res);
3887cf436c9SEd Tanous     }
3897cf436c9SEd Tanous 
3907cf436c9SEd Tanous     crow::App& app;
3917cf436c9SEd Tanous     std::shared_ptr<bmcweb::AsyncResp> finalRes;
3927cf436c9SEd Tanous };
3937cf436c9SEd Tanous 
3947cf436c9SEd Tanous inline void
3957cf436c9SEd Tanous     processAllParams(crow::App& app, const Query query,
3967cf436c9SEd Tanous 
3977cf436c9SEd Tanous                      std::function<void(crow::Response&)>& completionHandler,
3987cf436c9SEd Tanous                      crow::Response& intermediateResponse)
399f4c99e70SEd Tanous {
400f4c99e70SEd Tanous     if (!completionHandler)
401f4c99e70SEd Tanous     {
402f4c99e70SEd Tanous         BMCWEB_LOG_DEBUG << "Function was invalid?";
403f4c99e70SEd Tanous         return;
404f4c99e70SEd Tanous     }
405f4c99e70SEd Tanous 
406f4c99e70SEd Tanous     BMCWEB_LOG_DEBUG << "Processing query params";
407f4c99e70SEd Tanous     // If the request failed, there's no reason to even try to run query
408f4c99e70SEd Tanous     // params.
409f4c99e70SEd Tanous     if (intermediateResponse.resultInt() < 200 ||
410f4c99e70SEd Tanous         intermediateResponse.resultInt() >= 400)
411f4c99e70SEd Tanous     {
412f4c99e70SEd Tanous         completionHandler(intermediateResponse);
413f4c99e70SEd Tanous         return;
414f4c99e70SEd Tanous     }
415f4c99e70SEd Tanous     if (query.isOnly)
416f4c99e70SEd Tanous     {
417f4c99e70SEd Tanous         processOnly(app, intermediateResponse, completionHandler);
418f4c99e70SEd Tanous         return;
419f4c99e70SEd Tanous     }
4207cf436c9SEd Tanous     if (query.expandType != ExpandType::None)
4217cf436c9SEd Tanous     {
4227cf436c9SEd Tanous         BMCWEB_LOG_DEBUG << "Executing expand query";
4237cf436c9SEd Tanous         // TODO(ed) this is a copy of the response object.  Admittedly,
4247cf436c9SEd Tanous         // we're inherently doing something inefficient, but we shouldn't
4257cf436c9SEd Tanous         // have to do a full copy
4267cf436c9SEd Tanous         auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
4277cf436c9SEd Tanous         asyncResp->res.setCompleteRequestHandler(std::move(completionHandler));
4287cf436c9SEd Tanous         asyncResp->res.jsonValue = std::move(intermediateResponse.jsonValue);
4297cf436c9SEd Tanous         auto multi = std::make_shared<MultiAsyncResp>(app, asyncResp);
4307cf436c9SEd Tanous 
4317cf436c9SEd Tanous         // Start the chain by "ending" the root response
4327cf436c9SEd Tanous         multi->onEnd(query, nlohmann::json::json_pointer(""), asyncResp->res);
4337cf436c9SEd Tanous         return;
4347cf436c9SEd Tanous     }
435f4c99e70SEd Tanous     completionHandler(intermediateResponse);
436f4c99e70SEd Tanous }
437f4c99e70SEd Tanous 
438f4c99e70SEd Tanous } // namespace query_param
439f4c99e70SEd Tanous } // namespace redfish
440