xref: /openbmc/bmcweb/features/redfish/include/utils/query_param.hpp (revision 7cf436c913a109c0d3ebf7e696970966500bc6b6)
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 
8*7cf436c9SEd Tanous #include <charconv>
9f4c99e70SEd Tanous #include <string>
10f4c99e70SEd Tanous #include <string_view>
11*7cf436c9SEd Tanous #include <utility>
12f4c99e70SEd Tanous #include <vector>
13f4c99e70SEd Tanous 
14f4c99e70SEd Tanous namespace redfish
15f4c99e70SEd Tanous {
16f4c99e70SEd Tanous namespace query_param
17f4c99e70SEd Tanous {
18f4c99e70SEd Tanous 
19*7cf436c9SEd Tanous enum class ExpandType : uint8_t
20*7cf436c9SEd Tanous {
21*7cf436c9SEd Tanous     None,
22*7cf436c9SEd Tanous     Links,
23*7cf436c9SEd Tanous     NotLinks,
24*7cf436c9SEd Tanous     Both,
25*7cf436c9SEd Tanous };
26*7cf436c9SEd Tanous 
27f4c99e70SEd Tanous struct Query
28f4c99e70SEd Tanous {
29f4c99e70SEd Tanous     bool isOnly = false;
30*7cf436c9SEd Tanous     uint8_t expandLevel = 1;
31*7cf436c9SEd Tanous     ExpandType expandType = ExpandType::None;
32f4c99e70SEd Tanous };
33f4c99e70SEd Tanous 
34*7cf436c9SEd Tanous inline bool getExpandType(std::string_view value, Query& query)
35*7cf436c9SEd Tanous {
36*7cf436c9SEd Tanous     if (value.empty())
37*7cf436c9SEd Tanous     {
38*7cf436c9SEd Tanous         return false;
39*7cf436c9SEd Tanous     }
40*7cf436c9SEd Tanous     switch (value[0])
41*7cf436c9SEd Tanous     {
42*7cf436c9SEd Tanous         case '*':
43*7cf436c9SEd Tanous             query.expandType = ExpandType::Both;
44*7cf436c9SEd Tanous             break;
45*7cf436c9SEd Tanous         case '.':
46*7cf436c9SEd Tanous             query.expandType = ExpandType::NotLinks;
47*7cf436c9SEd Tanous             break;
48*7cf436c9SEd Tanous         case '~':
49*7cf436c9SEd Tanous             query.expandType = ExpandType::Links;
50*7cf436c9SEd Tanous             break;
51*7cf436c9SEd Tanous         default:
52*7cf436c9SEd Tanous             return false;
53*7cf436c9SEd Tanous 
54*7cf436c9SEd Tanous             break;
55*7cf436c9SEd Tanous     }
56*7cf436c9SEd Tanous     value.remove_prefix(1);
57*7cf436c9SEd Tanous     if (value.empty())
58*7cf436c9SEd Tanous     {
59*7cf436c9SEd Tanous         query.expandLevel = 1;
60*7cf436c9SEd Tanous         return true;
61*7cf436c9SEd Tanous     }
62*7cf436c9SEd Tanous     constexpr std::string_view levels = "($levels=";
63*7cf436c9SEd Tanous     if (!value.starts_with(levels))
64*7cf436c9SEd Tanous     {
65*7cf436c9SEd Tanous         return false;
66*7cf436c9SEd Tanous     }
67*7cf436c9SEd Tanous     value.remove_prefix(levels.size());
68*7cf436c9SEd Tanous 
69*7cf436c9SEd Tanous     auto it = std::from_chars(value.data(), value.data() + value.size(),
70*7cf436c9SEd Tanous                               query.expandLevel);
71*7cf436c9SEd Tanous     if (it.ec != std::errc())
72*7cf436c9SEd Tanous     {
73*7cf436c9SEd Tanous         return false;
74*7cf436c9SEd Tanous     }
75*7cf436c9SEd Tanous     value.remove_prefix(static_cast<size_t>(it.ptr - value.data()));
76*7cf436c9SEd Tanous     return value == ")";
77*7cf436c9SEd Tanous }
78*7cf436c9SEd Tanous 
79f4c99e70SEd Tanous inline std::optional<Query>
80f4c99e70SEd Tanous     parseParameters(const boost::urls::params_view& urlParams,
81f4c99e70SEd Tanous                     crow::Response& res)
82f4c99e70SEd Tanous {
83f4c99e70SEd Tanous     Query ret;
84f4c99e70SEd Tanous     for (const boost::urls::params_view::value_type& it : urlParams)
85f4c99e70SEd Tanous     {
86f4c99e70SEd Tanous         std::string_view key(it.key.data(), it.key.size());
87f4c99e70SEd Tanous         std::string_view value(it.value.data(), it.value.size());
88f4c99e70SEd Tanous         if (key == "only")
89f4c99e70SEd Tanous         {
90f4c99e70SEd Tanous             if (!it.value.empty())
91f4c99e70SEd Tanous             {
92f4c99e70SEd Tanous                 messages::queryParameterValueFormatError(res, value, key);
93f4c99e70SEd Tanous                 return std::nullopt;
94f4c99e70SEd Tanous             }
95f4c99e70SEd Tanous             ret.isOnly = true;
96f4c99e70SEd Tanous         }
97*7cf436c9SEd Tanous         else if (key == "$expand")
98*7cf436c9SEd Tanous         {
99*7cf436c9SEd Tanous             if (!getExpandType(value, ret))
100*7cf436c9SEd Tanous             {
101*7cf436c9SEd Tanous                 messages::queryParameterValueFormatError(res, value, key);
102*7cf436c9SEd Tanous                 return std::nullopt;
103f4c99e70SEd Tanous             }
104*7cf436c9SEd Tanous         }
105*7cf436c9SEd Tanous         else
106*7cf436c9SEd Tanous         {
107*7cf436c9SEd Tanous             // Intentionally ignore other errors Redfish spec, 7.3.1
108*7cf436c9SEd Tanous             if (key.starts_with("$"))
109*7cf436c9SEd Tanous             {
110*7cf436c9SEd Tanous                 // Services shall return... The HTTP 501 Not Implemented
111*7cf436c9SEd Tanous                 // status code for any unsupported query parameters that
112*7cf436c9SEd Tanous                 // start with $ .
113*7cf436c9SEd Tanous                 messages::queryParameterValueFormatError(res, value, key);
114*7cf436c9SEd Tanous                 res.result(boost::beast::http::status::not_implemented);
115*7cf436c9SEd Tanous                 return std::nullopt;
116*7cf436c9SEd Tanous             }
117*7cf436c9SEd Tanous             // "Shall ignore unknown or unsupported query parameters that do
118*7cf436c9SEd Tanous             // not begin with $ ."
119*7cf436c9SEd Tanous         }
120*7cf436c9SEd Tanous     }
121*7cf436c9SEd Tanous 
122f4c99e70SEd Tanous     return ret;
123f4c99e70SEd Tanous }
124f4c99e70SEd Tanous 
125f4c99e70SEd Tanous inline bool processOnly(crow::App& app, crow::Response& res,
126f4c99e70SEd Tanous                         std::function<void(crow::Response&)>& completionHandler)
127f4c99e70SEd Tanous {
128f4c99e70SEd Tanous     BMCWEB_LOG_DEBUG << "Processing only query param";
129f4c99e70SEd Tanous     auto itMembers = res.jsonValue.find("Members");
130f4c99e70SEd Tanous     if (itMembers == res.jsonValue.end())
131f4c99e70SEd Tanous     {
132f4c99e70SEd Tanous         messages::queryNotSupportedOnResource(res);
133f4c99e70SEd Tanous         completionHandler(res);
134f4c99e70SEd Tanous         return false;
135f4c99e70SEd Tanous     }
136f4c99e70SEd Tanous     auto itMemBegin = itMembers->begin();
137f4c99e70SEd Tanous     if (itMemBegin == itMembers->end() || itMembers->size() != 1)
138f4c99e70SEd Tanous     {
139f4c99e70SEd Tanous         BMCWEB_LOG_DEBUG << "Members contains " << itMembers->size()
140f4c99e70SEd Tanous                          << " element, returning full collection.";
141f4c99e70SEd Tanous         completionHandler(res);
142f4c99e70SEd Tanous         return false;
143f4c99e70SEd Tanous     }
144f4c99e70SEd Tanous 
145f4c99e70SEd Tanous     auto itUrl = itMemBegin->find("@odata.id");
146f4c99e70SEd Tanous     if (itUrl == itMemBegin->end())
147f4c99e70SEd Tanous     {
148f4c99e70SEd Tanous         BMCWEB_LOG_DEBUG << "No found odata.id";
149f4c99e70SEd Tanous         messages::internalError(res);
150f4c99e70SEd Tanous         completionHandler(res);
151f4c99e70SEd Tanous         return false;
152f4c99e70SEd Tanous     }
153f4c99e70SEd Tanous     const std::string* url = itUrl->get_ptr<const std::string*>();
154f4c99e70SEd Tanous     if (url == nullptr)
155f4c99e70SEd Tanous     {
156f4c99e70SEd Tanous         BMCWEB_LOG_DEBUG << "@odata.id wasn't a string????";
157f4c99e70SEd Tanous         messages::internalError(res);
158f4c99e70SEd Tanous         completionHandler(res);
159f4c99e70SEd Tanous         return false;
160f4c99e70SEd Tanous     }
161f4c99e70SEd Tanous     // TODO(Ed) copy request headers?
162f4c99e70SEd Tanous     // newReq.session = req.session;
163f4c99e70SEd Tanous     std::error_code ec;
164f4c99e70SEd Tanous     crow::Request newReq({boost::beast::http::verb::get, *url, 11}, ec);
165f4c99e70SEd Tanous     if (ec)
166f4c99e70SEd Tanous     {
167f4c99e70SEd Tanous         messages::internalError(res);
168f4c99e70SEd Tanous         completionHandler(res);
169f4c99e70SEd Tanous         return false;
170f4c99e70SEd Tanous     }
171f4c99e70SEd Tanous 
172f4c99e70SEd Tanous     auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
173f4c99e70SEd Tanous     BMCWEB_LOG_DEBUG << "setting completion handler on " << &asyncResp->res;
174f4c99e70SEd Tanous     asyncResp->res.setCompleteRequestHandler(std::move(completionHandler));
175f4c99e70SEd Tanous     asyncResp->res.setIsAliveHelper(res.releaseIsAliveHelper());
176f4c99e70SEd Tanous     app.handle(newReq, asyncResp);
177f4c99e70SEd Tanous     return true;
178f4c99e70SEd Tanous }
179f4c99e70SEd Tanous 
180*7cf436c9SEd Tanous struct ExpandNode
181*7cf436c9SEd Tanous {
182*7cf436c9SEd Tanous     nlohmann::json::json_pointer location;
183*7cf436c9SEd Tanous     std::string uri;
184*7cf436c9SEd Tanous 
185*7cf436c9SEd Tanous     inline bool operator==(const ExpandNode& other) const
186*7cf436c9SEd Tanous     {
187*7cf436c9SEd Tanous         return location == other.location && uri == other.uri;
188*7cf436c9SEd Tanous     }
189*7cf436c9SEd Tanous };
190*7cf436c9SEd Tanous 
191*7cf436c9SEd Tanous // Walks a json object looking for Redfish NavigationReference entries that
192*7cf436c9SEd Tanous // might need resolved.  It recursively walks the jsonResponse object, looking
193*7cf436c9SEd Tanous // for links at every level, and returns a list (out) of locations within the
194*7cf436c9SEd Tanous // tree that need to be expanded.  The current json pointer location p is passed
195*7cf436c9SEd Tanous // in to reference the current node that's being expanded, so it can be combined
196*7cf436c9SEd Tanous // with the keys from the jsonResponse object
197*7cf436c9SEd Tanous inline void findNavigationReferencesRecursive(
198*7cf436c9SEd Tanous     ExpandType eType, nlohmann::json& jsonResponse,
199*7cf436c9SEd Tanous     const nlohmann::json::json_pointer& p, bool inLinks,
200*7cf436c9SEd Tanous     std::vector<ExpandNode>& out)
201*7cf436c9SEd Tanous {
202*7cf436c9SEd Tanous     // If no expand is needed, return early
203*7cf436c9SEd Tanous     if (eType == ExpandType::None)
204*7cf436c9SEd Tanous     {
205*7cf436c9SEd Tanous         return;
206*7cf436c9SEd Tanous     }
207*7cf436c9SEd Tanous     nlohmann::json::array_t* array =
208*7cf436c9SEd Tanous         jsonResponse.get_ptr<nlohmann::json::array_t*>();
209*7cf436c9SEd Tanous     if (array != nullptr)
210*7cf436c9SEd Tanous     {
211*7cf436c9SEd Tanous         size_t index = 0;
212*7cf436c9SEd Tanous         // For arrays, walk every element in the array
213*7cf436c9SEd Tanous         for (auto& element : *array)
214*7cf436c9SEd Tanous         {
215*7cf436c9SEd Tanous             nlohmann::json::json_pointer newPtr = p / index;
216*7cf436c9SEd Tanous             BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr.to_string();
217*7cf436c9SEd Tanous             findNavigationReferencesRecursive(eType, element, newPtr, inLinks,
218*7cf436c9SEd Tanous                                               out);
219*7cf436c9SEd Tanous             index++;
220*7cf436c9SEd Tanous         }
221*7cf436c9SEd Tanous     }
222*7cf436c9SEd Tanous     nlohmann::json::object_t* obj =
223*7cf436c9SEd Tanous         jsonResponse.get_ptr<nlohmann::json::object_t*>();
224*7cf436c9SEd Tanous     if (obj == nullptr)
225*7cf436c9SEd Tanous     {
226*7cf436c9SEd Tanous         return;
227*7cf436c9SEd Tanous     }
228*7cf436c9SEd Tanous     // Navigation References only ever have a single element
229*7cf436c9SEd Tanous     if (obj->size() == 1)
230*7cf436c9SEd Tanous     {
231*7cf436c9SEd Tanous         if (obj->begin()->first == "@odata.id")
232*7cf436c9SEd Tanous         {
233*7cf436c9SEd Tanous             const std::string* uri =
234*7cf436c9SEd Tanous                 obj->begin()->second.get_ptr<const std::string*>();
235*7cf436c9SEd Tanous             if (uri != nullptr)
236*7cf436c9SEd Tanous             {
237*7cf436c9SEd Tanous                 BMCWEB_LOG_DEBUG << "Found element at " << p.to_string();
238*7cf436c9SEd Tanous                 out.push_back({p, *uri});
239*7cf436c9SEd Tanous             }
240*7cf436c9SEd Tanous         }
241*7cf436c9SEd Tanous     }
242*7cf436c9SEd Tanous     // Loop the object and look for links
243*7cf436c9SEd Tanous     for (auto& element : *obj)
244*7cf436c9SEd Tanous     {
245*7cf436c9SEd Tanous         if (!inLinks)
246*7cf436c9SEd Tanous         {
247*7cf436c9SEd Tanous             // Check if this is a links node
248*7cf436c9SEd Tanous             inLinks = element.first == "Links";
249*7cf436c9SEd Tanous         }
250*7cf436c9SEd Tanous         // Only traverse the parts of the tree the user asked for
251*7cf436c9SEd Tanous         // Per section 7.3 of the redfish specification
252*7cf436c9SEd Tanous         if (inLinks && eType == ExpandType::NotLinks)
253*7cf436c9SEd Tanous         {
254*7cf436c9SEd Tanous             continue;
255*7cf436c9SEd Tanous         }
256*7cf436c9SEd Tanous         if (!inLinks && eType == ExpandType::Links)
257*7cf436c9SEd Tanous         {
258*7cf436c9SEd Tanous             continue;
259*7cf436c9SEd Tanous         }
260*7cf436c9SEd Tanous         nlohmann::json::json_pointer newPtr = p / element.first;
261*7cf436c9SEd Tanous         BMCWEB_LOG_DEBUG << "Traversing response at " << newPtr;
262*7cf436c9SEd Tanous 
263*7cf436c9SEd Tanous         findNavigationReferencesRecursive(eType, element.second, newPtr,
264*7cf436c9SEd Tanous                                           inLinks, out);
265*7cf436c9SEd Tanous     }
266*7cf436c9SEd Tanous }
267*7cf436c9SEd Tanous 
268*7cf436c9SEd Tanous inline std::vector<ExpandNode>
269*7cf436c9SEd Tanous     findNavigationReferences(ExpandType eType, nlohmann::json& jsonResponse,
270*7cf436c9SEd Tanous                              const nlohmann::json::json_pointer& root)
271*7cf436c9SEd Tanous {
272*7cf436c9SEd Tanous     std::vector<ExpandNode> ret;
273*7cf436c9SEd Tanous     findNavigationReferencesRecursive(eType, jsonResponse, root, false, ret);
274*7cf436c9SEd Tanous     return ret;
275*7cf436c9SEd Tanous }
276*7cf436c9SEd Tanous 
277*7cf436c9SEd Tanous class MultiAsyncResp : public std::enable_shared_from_this<MultiAsyncResp>
278*7cf436c9SEd Tanous {
279*7cf436c9SEd Tanous   public:
280*7cf436c9SEd Tanous     // This object takes a single asyncResp object as the "final" one, then
281*7cf436c9SEd Tanous     // allows callers to attach sub-responses within the json tree that need
282*7cf436c9SEd Tanous     // to be executed and filled into their appropriate locations.  This
283*7cf436c9SEd Tanous     // class manages the final "merge" of the json resources.
284*7cf436c9SEd Tanous     MultiAsyncResp(crow::App& app,
285*7cf436c9SEd Tanous                    std::shared_ptr<bmcweb::AsyncResp> finalResIn) :
286*7cf436c9SEd Tanous         app(app),
287*7cf436c9SEd Tanous         finalRes(std::move(finalResIn))
288*7cf436c9SEd Tanous     {}
289*7cf436c9SEd Tanous 
290*7cf436c9SEd Tanous     void addAwaitingResponse(
291*7cf436c9SEd Tanous         Query query, std::shared_ptr<bmcweb::AsyncResp>& res,
292*7cf436c9SEd Tanous         const nlohmann::json::json_pointer& finalExpandLocation)
293*7cf436c9SEd Tanous     {
294*7cf436c9SEd Tanous         res->res.setCompleteRequestHandler(std::bind_front(
295*7cf436c9SEd Tanous             onEndStatic, shared_from_this(), query, finalExpandLocation));
296*7cf436c9SEd Tanous     }
297*7cf436c9SEd Tanous 
298*7cf436c9SEd Tanous     void onEnd(Query query, const nlohmann::json::json_pointer& locationToPlace,
299*7cf436c9SEd Tanous                crow::Response& res)
300*7cf436c9SEd Tanous     {
301*7cf436c9SEd Tanous         nlohmann::json& finalObj = finalRes->res.jsonValue[locationToPlace];
302*7cf436c9SEd Tanous         finalObj = std::move(res.jsonValue);
303*7cf436c9SEd Tanous 
304*7cf436c9SEd Tanous         if (query.expandLevel <= 0)
305*7cf436c9SEd Tanous         {
306*7cf436c9SEd Tanous             // Last level to expand, no need to go deeper
307*7cf436c9SEd Tanous             return;
308*7cf436c9SEd Tanous         }
309*7cf436c9SEd Tanous         // Now decrease the depth by one to account for the tree node we
310*7cf436c9SEd Tanous         // just resolved
311*7cf436c9SEd Tanous         query.expandLevel--;
312*7cf436c9SEd Tanous 
313*7cf436c9SEd Tanous         std::vector<ExpandNode> nodes = findNavigationReferences(
314*7cf436c9SEd Tanous             query.expandType, finalObj, locationToPlace);
315*7cf436c9SEd Tanous         BMCWEB_LOG_DEBUG << nodes.size() << " nodes to traverse";
316*7cf436c9SEd Tanous         for (const ExpandNode& node : nodes)
317*7cf436c9SEd Tanous         {
318*7cf436c9SEd Tanous             BMCWEB_LOG_DEBUG << "Expanding " << locationToPlace;
319*7cf436c9SEd Tanous             std::error_code ec;
320*7cf436c9SEd Tanous             crow::Request newReq({boost::beast::http::verb::get, node.uri, 11},
321*7cf436c9SEd Tanous                                  ec);
322*7cf436c9SEd Tanous             if (ec)
323*7cf436c9SEd Tanous             {
324*7cf436c9SEd Tanous                 messages::internalError(res);
325*7cf436c9SEd Tanous                 return;
326*7cf436c9SEd Tanous             }
327*7cf436c9SEd Tanous 
328*7cf436c9SEd Tanous             auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
329*7cf436c9SEd Tanous             BMCWEB_LOG_DEBUG << "setting completion handler on "
330*7cf436c9SEd Tanous                              << &asyncResp->res;
331*7cf436c9SEd Tanous             addAwaitingResponse(query, asyncResp, node.location);
332*7cf436c9SEd Tanous             app.handle(newReq, asyncResp);
333*7cf436c9SEd Tanous         }
334*7cf436c9SEd Tanous     }
335*7cf436c9SEd Tanous 
336*7cf436c9SEd Tanous   private:
337*7cf436c9SEd Tanous     static void onEndStatic(const std::shared_ptr<MultiAsyncResp>& multi,
338*7cf436c9SEd Tanous                             Query query,
339*7cf436c9SEd Tanous                             const nlohmann::json::json_pointer& locationToPlace,
340*7cf436c9SEd Tanous                             crow::Response& res)
341*7cf436c9SEd Tanous     {
342*7cf436c9SEd Tanous         multi->onEnd(query, locationToPlace, res);
343*7cf436c9SEd Tanous     }
344*7cf436c9SEd Tanous 
345*7cf436c9SEd Tanous     crow::App& app;
346*7cf436c9SEd Tanous     std::shared_ptr<bmcweb::AsyncResp> finalRes;
347*7cf436c9SEd Tanous };
348*7cf436c9SEd Tanous 
349*7cf436c9SEd Tanous inline void
350*7cf436c9SEd Tanous     processAllParams(crow::App& app, const Query query,
351*7cf436c9SEd Tanous 
352*7cf436c9SEd Tanous                      std::function<void(crow::Response&)>& completionHandler,
353*7cf436c9SEd Tanous                      crow::Response& intermediateResponse)
354f4c99e70SEd Tanous {
355f4c99e70SEd Tanous     if (!completionHandler)
356f4c99e70SEd Tanous     {
357f4c99e70SEd Tanous         BMCWEB_LOG_DEBUG << "Function was invalid?";
358f4c99e70SEd Tanous         return;
359f4c99e70SEd Tanous     }
360f4c99e70SEd Tanous 
361f4c99e70SEd Tanous     BMCWEB_LOG_DEBUG << "Processing query params";
362f4c99e70SEd Tanous     // If the request failed, there's no reason to even try to run query
363f4c99e70SEd Tanous     // params.
364f4c99e70SEd Tanous     if (intermediateResponse.resultInt() < 200 ||
365f4c99e70SEd Tanous         intermediateResponse.resultInt() >= 400)
366f4c99e70SEd Tanous     {
367f4c99e70SEd Tanous         completionHandler(intermediateResponse);
368f4c99e70SEd Tanous         return;
369f4c99e70SEd Tanous     }
370f4c99e70SEd Tanous     if (query.isOnly)
371f4c99e70SEd Tanous     {
372f4c99e70SEd Tanous         processOnly(app, intermediateResponse, completionHandler);
373f4c99e70SEd Tanous         return;
374f4c99e70SEd Tanous     }
375*7cf436c9SEd Tanous     if (query.expandType != ExpandType::None)
376*7cf436c9SEd Tanous     {
377*7cf436c9SEd Tanous         BMCWEB_LOG_DEBUG << "Executing expand query";
378*7cf436c9SEd Tanous         // TODO(ed) this is a copy of the response object.  Admittedly,
379*7cf436c9SEd Tanous         // we're inherently doing something inefficient, but we shouldn't
380*7cf436c9SEd Tanous         // have to do a full copy
381*7cf436c9SEd Tanous         auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
382*7cf436c9SEd Tanous         asyncResp->res.setCompleteRequestHandler(std::move(completionHandler));
383*7cf436c9SEd Tanous         asyncResp->res.jsonValue = std::move(intermediateResponse.jsonValue);
384*7cf436c9SEd Tanous         auto multi = std::make_shared<MultiAsyncResp>(app, asyncResp);
385*7cf436c9SEd Tanous 
386*7cf436c9SEd Tanous         // Start the chain by "ending" the root response
387*7cf436c9SEd Tanous         multi->onEnd(query, nlohmann::json::json_pointer(""), asyncResp->res);
388*7cf436c9SEd Tanous         return;
389*7cf436c9SEd Tanous     }
390f4c99e70SEd Tanous     completionHandler(intermediateResponse);
391f4c99e70SEd Tanous }
392f4c99e70SEd Tanous 
393f4c99e70SEd Tanous } // namespace query_param
394f4c99e70SEd Tanous } // namespace redfish
395