Get Inventory list using unexpanded location code

This commit implements an api to get inventory list with respect to
a given location code in un-expanded format.
Along with location code node also needs to be passed.

In case no inventory is found at the given location or location code
is not correct, corresponding error is returned to the caller.

Tested on simics.
This api is under com.ibm.vpd.Manager interface. So vpd-manager app
needs to be running for this api to work.
Sample command for simics:
busctl call com.ibm.VPD.Manager /com/ibm/VPD/Manager com.ibm.VPD.Manager
GetFRUsByUnexpandedLocationCode sq <location_code> <node_number>

Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: I87159d2a3c293e82c9e82d21a9d281bd176a30e7
diff --git a/types.hpp b/types.hpp
index bab4569..0c02fd9 100644
--- a/types.hpp
+++ b/types.hpp
@@ -37,8 +37,8 @@
 using FrusMap =
     std::unordered_map<Path, std::pair<VPDfilepath, FruIsMotherboard>>;
 using LocationCode = std::string;
-using LocationCodeMap = std::unordered_map<Path, LocationCode>;
-
+using LocationCodeMap = std::unordered_multimap<LocationCode, Path>;
+using ListOfPaths = std::vector<sdbusplus::message::object_path>;
 using namespace std::string_literals;
 constexpr auto pimPath = "/xyz/openbmc_project/inventory";
 constexpr auto pimIntf = "xyz.openbmc_project.Inventory.Manager";
diff --git a/vpd-manager/manager.cpp b/vpd-manager/manager.cpp
index 1ce1cd1..347aade 100644
--- a/vpd-manager/manager.cpp
+++ b/vpd-manager/manager.cpp
@@ -5,8 +5,10 @@
 #include "editor_impl.hpp"
 #include "parser.hpp"
 #include "reader_impl.hpp"
+#include "utils.hpp"
 
 using namespace openpower::vpd::constants;
+using namespace openpower::vpd::inventory;
 using namespace openpower::vpd::manager::editor;
 using namespace openpower::vpd::manager::reader;
 
@@ -126,14 +128,15 @@
     }
 }
 
-std::vector<sdbusplus::message::object_path>
+ListOfPaths
     Manager::getFRUsByUnexpandedLocationCode(const std::string locationCode,
                                              const uint16_t nodeNumber)
 {
-    // implement the interface
+    ReaderImpl read;
+    return read.getFrusAtLocation(locationCode, nodeNumber, fruLocationCode);
 }
 
-std::vector<sdbusplus::message::object_path>
+ListOfPaths
     Manager::getFRUsByExpandedLocationCode(const std::string locationCode)
 {
     // implement the interface
diff --git a/vpd-manager/manager.hpp b/vpd-manager/manager.hpp
index 455cd11..6846c6e 100644
--- a/vpd-manager/manager.hpp
+++ b/vpd-manager/manager.hpp
@@ -6,7 +6,6 @@
 #include <com/ibm/VPD/Manager/server.hpp>
 #include <map>
 #include <nlohmann/json.hpp>
-#include <sdbusplus/server.hpp>
 
 namespace sdbusplus
 {
@@ -86,7 +85,7 @@
      *  @return inventoryList[std::vector<sdbusplus::message::object_path>] -
      *  List of all the FRUs D-Bus object paths for the given location code.
      */
-    std::vector<sdbusplus::message::object_path>
+    inventory::ListOfPaths
         getFRUsByUnexpandedLocationCode(const std::string locationCode,
                                         const uint16_t nodeNumber);
 
@@ -100,7 +99,7 @@
      *  @return inventoryList[std::vector<sdbusplus::message::object_path>] -
      *  List of all the FRUs D-Bus object path for the given location code.
      */
-    std::vector<sdbusplus::message::object_path>
+    inventory::ListOfPaths
         getFRUsByExpandedLocationCode(const std::string locationCode);
 
     /** @brief Implementation for GetExpandedLocationCode
diff --git a/vpd-manager/reader_impl.cpp b/vpd-manager/reader_impl.cpp
index 21660d9..a40aa44 100644
--- a/vpd-manager/reader_impl.cpp
+++ b/vpd-manager/reader_impl.cpp
@@ -1,10 +1,14 @@
+#include "config.h"
+
 #include "reader_impl.hpp"
 
 #include "utils.hpp"
 
+#include <algorithm>
 #include <com/ibm/VPD/error.hpp>
 #include <map>
 #include <phosphor-logging/elog-errors.hpp>
+#include <vector>
 #include <xyz/openbmc_project/Common/error.hpp>
 
 namespace openpower
@@ -25,14 +29,24 @@
 using Argument = xyz::openbmc_project::Common::InvalidArgument;
 using LocationNotFound = sdbusplus::com::ibm::VPD::Error::LocationNotFound;
 
-std::string ReaderImpl::getExpandedLocationCode(
-    const std::string& locationCode, const uint16_t& nodeNumber,
-    const LocationCodeMap& frusLocationCode) const
+bool ReaderImpl::isValidLocationCode(const std::string& locationCode) const
 {
     if ((locationCode.length() < UNEXP_LOCATION_CODE_MIN_LENGTH) ||
         ((locationCode.find("fcs", 1, 3) == std::string::npos) &&
          (locationCode.find("mts", 1, 3) == std::string::npos)))
     {
+        return false;
+    }
+
+    return true;
+}
+
+std::string ReaderImpl::getExpandedLocationCode(
+    const std::string& locationCode, const uint16_t& nodeNumber,
+    const LocationCodeMap& frusLocationCode) const
+{
+    if (!isValidLocationCode(locationCode))
+    {
         // argument is not valid
         elog<InvalidArgument>(Argument::ARGUMENT_NAME("LOCATIONCODE"),
                               Argument::ARGUMENT_VALUE(locationCode.c_str()));
@@ -54,6 +68,40 @@
     return expandedLocationCode;
 }
 
+ListOfPaths
+    ReaderImpl::getFrusAtLocation(const std::string& locationCode,
+                                  const uint16_t& nodeNumber,
+                                  const LocationCodeMap& frusLocationCode) const
+{
+    if (!isValidLocationCode(locationCode))
+    {
+        // argument is not valid
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("LOCATIONCODE"),
+                              Argument::ARGUMENT_VALUE(locationCode.c_str()));
+    }
+
+    auto range = frusLocationCode.equal_range(locationCode);
+
+    if (range.first == frusLocationCode.end())
+    {
+        // TODO: Implementation of error logic till then throwing invalid
+        // argument
+        // the location code was not found in the system
+        // elog<LocationNotFound>();
+        elog<InvalidArgument>(Argument::ARGUMENT_NAME("LOCATIONCODE"),
+                              Argument::ARGUMENT_VALUE(locationCode.c_str()));
+    }
+
+    ListOfPaths inventoryPaths;
+
+    for_each(range.first, range.second,
+             [&inventoryPaths](
+                 const inventory::LocationCodeMap::value_type& mappedItem) {
+                 inventoryPaths.push_back(INVENTORY_PATH + mappedItem.second);
+             });
+    return inventoryPaths;
+}
+
 } // namespace reader
 } // namespace manager
 } // namespace vpd
diff --git a/vpd-manager/reader_impl.hpp b/vpd-manager/reader_impl.hpp
index 17cfc75..465f414 100644
--- a/vpd-manager/reader_impl.hpp
+++ b/vpd-manager/reader_impl.hpp
@@ -36,9 +36,26 @@
         const std::string& locationCode, const uint16_t& nodeNumber,
         const inventory::LocationCodeMap& frusLocationCode) const;
 
+    /** @brief An api to get list of all the FRUs at the given location code
+     *  @param[in] - location code in unexpanded format
+     *  @param[in] - node number
+     *  @param[in] - mapping of location code and Inventory path
+     *  @return list of Inventory paths at the given location
+     */
+    inventory::ListOfPaths getFrusAtLocation(
+        const std::string& locationCode, const uint16_t& nodeNumber,
+        const inventory::LocationCodeMap& frusLocationCode) const;
+
+  private:
+    /** @brief An api to check validity of location code
+     *  @param[in] - location code
+     *  @return true/false based on validity check
+     */
+    bool isValidLocationCode(const std::string& locationCode) const;
+
 }; // class ReaderImpl
 
 } // namespace reader
 } // namespace manager
 } // namespace vpd
-} // namespace openpower
\ No newline at end of file
+} // namespace openpower