Implement get correlated properties API

This commit implements API used to get correlated properties for given
property. This API is required to process correlated properties
callback.

Test:
```
- Add debug traces to callback processing method
- On rainier 2s2u simics, do busctl set-property on /system Decorator
  Asset interface's SerialNumber property
- Observe get correlated properties API return targets:
  /xyz/openbmc_project/inventory/system com.ibm.ipzvpd.VSYS SE
  /xyz/openbmc_project/inventory/system/chassis com.ibm.ipzvpd.VSYS SE
  /xyz/openbmc_project/inventory/system/chassis/motherboard
com.ibm.ipzvpd.VSYS SE
 /xyz/openbmc_project/inventory/system/chassis/motherboard/tod_battery
com.ibm.ipzvpd.VSYS SE
- On rainier 2s2u simics, do busctl set-property on /system Decorator
  Asset's PartNumber property
- Observe get correlated properties API return target:
  /xyz/openbmc_project/inventory/system/chassis/motherboard
  com.ibm.ipzvpd.VINI PN
```

Change-Id: I3ad537f7b4f4663681a192cc137b267c114ac03a
Signed-off-by: Souvik Roy <souvikroyofficial10@gmail.com>
diff --git a/vpd-manager/src/listener.cpp b/vpd-manager/src/listener.cpp
index 759f85d..534112b 100644
--- a/vpd-manager/src/listener.cpp
+++ b/vpd-manager/src/listener.cpp
@@ -408,16 +408,60 @@
 }
 
 types::DbusPropertyList Listener::getCorrelatedProps(
-    [[maybe_unused]] const std::string& i_serviceName,
-    [[maybe_unused]] const std::string& i_objectPath,
-    [[maybe_unused]] const std::string& i_interface,
-    [[maybe_unused]] const std::string& i_property) const
+    const std::string& i_serviceName, const std::string& i_objectPath,
+    const std::string& i_interface, const std::string& i_property) const
 {
     types::DbusPropertyList l_result;
     try
     {
-        /*TODO: Use parsed correlated JSON to find target {object path(s),
-        interface(s), property/properties}*/
+        if (m_correlatedPropJson.contains(i_serviceName) &&
+            m_correlatedPropJson[i_serviceName].contains(i_interface) &&
+            m_correlatedPropJson[i_serviceName][i_interface].contains(
+                i_property))
+        {
+            const nlohmann::json& l_destinationJsonObj =
+                m_correlatedPropJson[i_serviceName][i_interface][i_property];
+
+            // check if any matching paths pair entry is present
+            if (l_destinationJsonObj.contains("pathsPair") &&
+                l_destinationJsonObj["pathsPair"].contains(i_objectPath) &&
+                l_destinationJsonObj["pathsPair"][i_objectPath].contains(
+                    "destinationInventoryPath") &&
+                l_destinationJsonObj["pathsPair"][i_objectPath].contains(
+                    "interfaces"))
+            {
+                // iterate through all the destination interface and property
+                // name
+                for (const auto& l_destinationInterfaceJsonObj :
+                     l_destinationJsonObj["pathsPair"][i_objectPath]
+                                         ["interfaces"]
+                                             .items())
+                {
+                    // iterate through all destination inventory paths
+                    for (const auto& l_destinationInventoryPath :
+                         l_destinationJsonObj["pathsPair"][i_objectPath]
+                                             ["destinationInventoryPath"])
+                    {
+                        l_result.emplace_back(
+                            l_destinationInventoryPath,
+                            l_destinationInterfaceJsonObj.key(),
+                            l_destinationInterfaceJsonObj.value());
+                    } // destination inventory paths
+                } // destination interfaces
+            }
+            // get the default interface, property to update
+            else if (l_destinationJsonObj.contains("defaultInterfaces"))
+            {
+                // iterate through all default interfaces to update
+                for (const auto& l_destinationIfcPropEntry :
+                     l_destinationJsonObj["defaultInterfaces"].items())
+                {
+                    l_result.emplace_back(i_objectPath,
+                                          l_destinationIfcPropEntry.key(),
+                                          l_destinationIfcPropEntry.value());
+                }
+            }
+        }
     }
     catch (const std::exception& l_ex)
     {