Add support to request a single Journal Event Log Entry

This change adds back support to request a single Journal Event
Log Entry.

Tested:
Requested a single valid entry and got it.
Requested an invalid entry and got the correct error message.
Passed the Redfish Service Validator.

Change-Id: Ibd2defee04510c1b52fe010cf3e2c6aced94537f
Signed-off-by: Jason M. Bills <jason.m.bills@linux.intel.com>
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index 94496a1..a9356ae 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -651,7 +651,7 @@
         {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
         {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
         {"@odata.id",
-         "/redfish/v1/Systems/system/LogServices/EventLog/Entries/#" +
+         "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
              logEntryID},
         {"Name", "System Event Log Entry"},
         {"Id", logEntryID},
@@ -768,6 +768,83 @@
     }
 };
 
+class JournalEventLogEntry : public Node
+{
+  public:
+    JournalEventLogEntry(CrowApp &app) :
+        Node(app,
+             "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
+             std::string())
+    {
+        entityPrivileges = {
+            {boost::beast::http::verb::get, {{"Login"}}},
+            {boost::beast::http::verb::head, {{"Login"}}},
+            {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::put, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
+            {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
+    }
+
+  private:
+    void doGet(crow::Response &res, const crow::Request &req,
+               const std::vector<std::string> &params) override
+    {
+        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+        if (params.size() != 1)
+        {
+            messages::internalError(asyncResp->res);
+            return;
+        }
+        const std::string &targetID = params[0];
+
+        // Go through the log files and check the unique ID for each entry to
+        // find the target entry
+        std::vector<std::filesystem::path> redfishLogFiles;
+        getRedfishLogFiles(redfishLogFiles);
+        std::string logEntry;
+
+        // Oldest logs are in the last file, so start there and loop backwards
+        for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
+             it++)
+        {
+            std::ifstream logStream(*it);
+            if (!logStream.is_open())
+            {
+                continue;
+            }
+
+            // Reset the unique ID on the first entry
+            bool firstEntry = true;
+            while (std::getline(logStream, logEntry))
+            {
+                std::string idStr;
+                if (!getUniqueEntryID(logEntry, idStr, firstEntry))
+                {
+                    continue;
+                }
+
+                if (firstEntry)
+                {
+                    firstEntry = false;
+                }
+
+                if (idStr == targetID)
+                {
+                    if (fillEventLogEntryJson(idStr, logEntry,
+                                              asyncResp->res.jsonValue) != 0)
+                    {
+                        messages::internalError(asyncResp->res);
+                        return;
+                    }
+                    return;
+                }
+            }
+        }
+        // Requested ID was not found
+        messages::resourceMissingAtURI(asyncResp->res, targetID);
+    }
+};
+
 class DBusEventLogEntryCollection : public Node
 {
   public: