Create a DBus notification for each LogEntry

For each log entry, a new FilePath DBus object will be created where
the FilePath.path value is the location of the JSON LogEntry file.

Tested:
Tested with unit tests and locally on a machine.

Signed-off-by: Kasun Athukorala <kasunath@google.com>
Change-Id: I5999826f7b4447bfca88b83c487d7c03a1c84a08
diff --git a/include/rde/external_storer_file.hpp b/include/rde/external_storer_file.hpp
index 3d12dc9..f6a6281 100644
--- a/include/rde/external_storer_file.hpp
+++ b/include/rde/external_storer_file.hpp
@@ -2,6 +2,7 @@
 
 #include "external_storer_interface.hpp"
 #include "nlohmann/json.hpp"
+#include "notifier_dbus_handler.hpp"
 
 #include <boost/uuid/uuid_generators.hpp>
 
@@ -75,21 +76,24 @@
     /**
      * @brief Constructor for the ExternalStorerFileInterface.
      *
+     * @param[in] bus - bus to attach to.
      * @param[in] rootPath - root path for creating redfish folders.
      * Eg: "/run/bmcweb"
      * @param[in] fileHandler - an ExternalStorerFileWriter object. This class
      * will take the ownership of this object.
      */
     ExternalStorerFileInterface(
-        std::string_view rootPath,
+        sdbusplus::bus::bus& bus, std::string_view rootPath,
         std::unique_ptr<FileHandlerInterface> fileHandler);
 
     bool publishJson(std::string_view jsonStr) override;
 
   private:
+    sdbusplus::bus::bus& bus;
     std::string rootPath;
     std::unique_ptr<FileHandlerInterface> fileHandler;
     std::string logServiceId;
+    std::unique_ptr<CperFileNotifierHandler> cperNotifier;
     boost::uuids::random_generator randomGen;
 
     /**
diff --git a/src/main.cpp b/src/main.cpp
index 0b520a9..e81150a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -10,6 +10,7 @@
 
 #include <boost/asio.hpp>
 #include <boost/endian/conversion.hpp>
+#include <sdbusplus/asio/object_server.hpp>
 #include <stdplus/fd/create.hpp>
 #include <stdplus/fd/impl.hpp>
 #include <stdplus/fd/managed.hpp>
@@ -87,11 +88,16 @@
         std::make_shared<BufferImpl>(std::move(pciDataHandler));
 
     // rdeCommandHandler initialization
+    std::shared_ptr<sdbusplus::asio::connection> conn =
+        std::make_shared<sdbusplus::asio::connection>(io);
+    conn->request_name("xyz.openbmc_project.bios_bmc_smm_error_logger");
+    sdbusplus::bus::bus& bus = static_cast<sdbusplus::bus::bus&>(*conn);
+
     std::unique_ptr<rde::FileHandlerInterface> fileIface =
         std::make_unique<rde::ExternalStorerFileWriter>();
     std::unique_ptr<rde::ExternalStorerInterface> exFileIface =
         std::make_unique<rde::ExternalStorerFileInterface>(
-            "/run/bmcweb", std::move(fileIface));
+            bus, "/run/bmcweb", std::move(fileIface));
     std::shared_ptr<rde::RdeCommandHandler> rdeCommandHandler =
         std::make_unique<rde::RdeCommandHandler>(std::move(exFileIface));
 
diff --git a/src/rde/external_storer_file.cpp b/src/rde/external_storer_file.cpp
index 0768134..97f2b10 100644
--- a/src/rde/external_storer_file.cpp
+++ b/src/rde/external_storer_file.cpp
@@ -44,10 +44,11 @@
 }
 
 ExternalStorerFileInterface::ExternalStorerFileInterface(
-    std::string_view rootPath,
+    sdbusplus::bus::bus& bus, std::string_view rootPath,
     std::unique_ptr<FileHandlerInterface> fileHandler) :
-    rootPath(rootPath),
-    fileHandler(std::move(fileHandler)), logServiceId("")
+    bus(bus),
+    rootPath(rootPath), fileHandler(std::move(fileHandler)), logServiceId(""),
+    cperNotifier(std::make_unique<CperFileNotifierHandler>(bus))
 {}
 
 bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr)
@@ -115,8 +116,9 @@
     }
 
     std::string id = boost::uuids::to_string(randomGen());
-    std::string path = "/redfish/v1/Systems/system/LogServices/" +
-                       logServiceId + "/Entries/" + id;
+    std::string fullPath =
+        fmt::format("{}/redfish/v1/Systems/system/LogServices/{}/Entries/{}",
+                    rootPath, logServiceId, id);
 
     // Populate the "Id" with the UUID we generated.
     logEntry["Id"] = id;
@@ -124,7 +126,15 @@
     // a client.
     logEntry.erase("@odata.id");
 
-    return createFile(path, logEntry);
+    if (!fileHandler->createFile(fullPath, logEntry))
+    {
+        fmt::print(stderr, "Failed to create a file for log entry path: {}\n",
+                   fullPath);
+        return false;
+    }
+
+    cperNotifier->createEntry(fullPath + "/index.json");
+    return true;
 }
 
 bool ExternalStorerFileInterface::processLogService(
diff --git a/test/external_storer_file_test.cpp b/test/external_storer_file_test.cpp
index fc550ee..f2718aa 100644
--- a/test/external_storer_file_test.cpp
+++ b/test/external_storer_file_test.cpp
@@ -1,5 +1,8 @@
 #include "rde/external_storer_file.hpp"
 
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/test/sdbus_mock.hpp>
+
 #include <string_view>
 
 #include <gmock/gmock-matchers.h>
@@ -15,6 +18,7 @@
 using ::testing::DoAll;
 using ::testing::Return;
 using ::testing::SaveArg;
+using ::testing::StrEq;
 
 class MockFileWriter : public FileHandlerInterface
 {
@@ -30,14 +34,26 @@
 {
   public:
     ExternalStorerFileTest() :
+        bus(sdbusplus::get_mocked_new(&sdbusMock)),
         mockFileWriter(std::make_unique<MockFileWriter>())
     {
         mockFileWriterPtr = dynamic_cast<MockFileWriter*>(mockFileWriter.get());
+
+        EXPECT_CALL(
+            sdbusMock,
+            sd_bus_add_object_manager(
+                nullptr, _,
+                StrEq(
+                    "/xyz/openbmc_project/external_storer/bios_bmc_smm_error_logger/CPER")))
+            .WillOnce(Return(0));
+
         exStorer = std::make_unique<ExternalStorerFileInterface>(
-            rootPath, std::move(mockFileWriter));
+            bus, rootPath, std::move(mockFileWriter));
     }
 
   protected:
+    sdbusplus::SdBusMock sdbusMock;
+    sdbusplus::bus::bus bus;
     std::unique_ptr<FileHandlerInterface> mockFileWriter;
     std::unique_ptr<ExternalStorerFileInterface> exStorer;
     MockFileWriter* mockFileWriterPtr;
@@ -147,12 +163,29 @@
         "@odata.type": "#LogEntry.v1_13_0.LogEntry"
       }
     )";
+
     nlohmann::json logEntryOut;
     EXPECT_CALL(*mockFileWriterPtr, createFile(_, _))
         .WillOnce(DoAll(SaveArg<1>(&logEntryOut), Return(true)));
+
+    constexpr const char* dbusPath =
+        "/xyz/openbmc_project/external_storer/bios_bmc_smm_error_logger/CPER/entry0";
+    constexpr const char* dbusInterface = "xyz.openbmc_project.Common.FilePath";
+
+    EXPECT_CALL(sdbusMock, sd_bus_add_object_vtable(nullptr, _, StrEq(dbusPath),
+                                                    StrEq(dbusInterface), _, _))
+        .WillOnce(Return(0));
+    EXPECT_CALL(sdbusMock,
+                sd_bus_emit_interfaces_added_strv(nullptr, StrEq(dbusPath), _))
+        .WillOnce(Return(0));
+
     EXPECT_THAT(exStorer->publishJson(jsonLogEntry), true);
     EXPECT_NE(logEntryOut["Id"], nullptr);
     EXPECT_EQ(logEntryOut["@odata.id"], nullptr);
+
+    EXPECT_CALL(sdbusMock, sd_bus_emit_interfaces_removed_strv(
+                               nullptr, StrEq(dbusPath), _))
+        .WillOnce(Return(0));
 }
 
 TEST_F(ExternalStorerFileTest, OtherSchemaNoOdataIdTest)