health: take json_ptr instead of reference when filling status

The existing codes populates the health status on the |AsyncResponse| or
a given JSON reference. This doesn't work if we want to populates status
on an array of objects, since the array can be resized which changes the
address of each object.

This commit changed the contructor to take a JSON pointer instead.
|HealthPopulate| will populates status on
|AsyncResponse->res.jsonValue|[json_ptr]. If the point can't be resolved
in the |jsonValue|, |HealthPopulate| populates nothing.

Fixed all places where the old reference based constructor is used.

This commit is extremely useful when implementing efficient level-1
expand handler on ResourceCollections. It also prevents issues on
reference lifecycles.

Tested:
1. It builds
2. Tested DIMM/System/Storage health on real hardware, works as expected
3. Tested on Redfish Service Validator, no new failures on health
properties.

Signed-off-by: Nan Zhou <nanzhoumails@gmail.com>
Change-Id: I305515522af50b48be92a3f4689d8166f3bc0cc0
diff --git a/redfish-core/lib/health.hpp b/redfish-core/lib/health.hpp
index 31adf7b..254f8e9 100644
--- a/redfish-core/lib/health.hpp
+++ b/redfish-core/lib/health.hpp
@@ -22,6 +22,7 @@
 #include <boost/container/flat_set.hpp>
 #include <dbus_singleton.hpp>
 #include <dbus_utility.hpp>
+#include <nlohmann/json.hpp>
 
 #include <variant>
 
@@ -30,14 +31,18 @@
 
 struct HealthPopulate : std::enable_shared_from_this<HealthPopulate>
 {
+    // By default populate status to "/Status" of |asyncResp->res.jsonValue|.
     HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
-        asyncResp(asyncRespIn), jsonStatus(asyncResp->res.jsonValue["Status"])
+        asyncResp(asyncRespIn), statusPtr("/Status")
     {}
 
+    // Takes a JSON pointer rather than a reference. This is pretty useful when
+    // the address of the status JSON might change, for example, elements in an
+    // array.
     HealthPopulate(const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn,
-                   nlohmann::json& status) :
+                   const nlohmann::json::json_pointer& ptr) :
         asyncResp(asyncRespIn),
-        jsonStatus(status)
+        statusPtr(ptr)
     {}
 
     HealthPopulate(const HealthPopulate&) = delete;
@@ -47,6 +52,7 @@
 
     ~HealthPopulate()
     {
+        nlohmann::json& jsonStatus = asyncResp->res.jsonValue[statusPtr];
         nlohmann::json& health = jsonStatus["Health"];
         nlohmann::json& rollup = jsonStatus["HealthRollup"];
 
@@ -232,7 +238,9 @@
     }
 
     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
-    nlohmann::json& jsonStatus;
+
+    // Will populate the health status into |asyncResp_json[statusPtr]|
+    nlohmann::json::json_pointer statusPtr;
 
     // we store pointers to other HealthPopulate items so we can update their
     // members and reduce dbus calls. As we hold a shared_ptr to them, they get