Support the $filter query params for SSE stream

This commit adds the support for $filter query
paramater in the URI for SSE stream.

Method: GET
URI: /redfish/v1/EventService/Subscriptions/SSE?
$filter=(MessageIds%20eq%20DCPowerOn) or
(MessageIds%20eq%20DCPowerOn)

Tested:
- From browser sent request using SSE URI along with filter query param
      - query params were read and parsed successfully.
      - used SubmitTestEvent and could see test events coming to BMC.
- Ran redfish validator successfully.
- Performed GET on Subscription collections and Subscription/<id> URI
  and checked for valid data.

Change-Id: Ie18546749495175ede918ab933ff8dd1d65b775f
Signed-off-by: Ayushi Smriti <smriti.ayushi@linux.intel.com>
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
index 03d5dc3..801a7ad 100644
--- a/redfish-core/include/event_service_manager.hpp
+++ b/redfish-core/include/event_service_manager.hpp
@@ -267,6 +267,95 @@
 } // namespace event_log
 #endif
 
+bool isFilterQuerySpecialChar(char c)
+{
+    switch (c)
+    {
+        case '(':
+        case ')':
+        case '\'':
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool readSSEQueryParams(std::string sseFilter, std::string& formatType,
+                        std::vector<std::string>& messageIds,
+                        std::vector<std::string>& registryPrefixes,
+                        std::vector<nlohmann::json>& metricReportDefinitions)
+{
+    sseFilter.erase(std::remove_if(sseFilter.begin(), sseFilter.end(),
+                                   isFilterQuerySpecialChar),
+                    sseFilter.end());
+
+    std::vector<std::string> result;
+    boost::split(result, sseFilter, boost::is_any_of(" "),
+                 boost::token_compress_on);
+
+    BMCWEB_LOG_DEBUG << "No of tokens in SEE query: " << result.size();
+
+    constexpr uint8_t divisor = 4;
+    constexpr uint8_t minTokenSize = 3;
+    if (result.size() % divisor != minTokenSize)
+    {
+        BMCWEB_LOG_ERROR << "Invalid SSE filter specified.";
+        return false;
+    }
+
+    for (std::size_t i = 0; i < result.size(); i += divisor)
+    {
+        std::string& key = result[i];
+        std::string& op = result[i + 1];
+        std::string& value = result[i + 2];
+
+        if ((i + minTokenSize) < result.size())
+        {
+            std::string& seperator = result[i + minTokenSize];
+            // SSE supports only "or" and "and" in query params.
+            if ((seperator != "or") && (seperator != "and"))
+            {
+                BMCWEB_LOG_ERROR
+                    << "Invalid group operator in SSE query parameters";
+                return false;
+            }
+        }
+
+        // SSE supports only "eq" as per spec.
+        if (op != "eq")
+        {
+            BMCWEB_LOG_ERROR
+                << "Invalid assignment operator in SSE query parameters";
+            return false;
+        }
+
+        BMCWEB_LOG_DEBUG << key << " : " << value;
+        if (key == "EventFormatType")
+        {
+            formatType = value;
+        }
+        else if (key == "MessageId")
+        {
+            messageIds.push_back(value);
+        }
+        else if (key == "RegistryPrefix")
+        {
+            registryPrefixes.push_back(value);
+        }
+        else if (key == "MetricReportDefinition")
+        {
+            metricReportDefinitions.push_back(value);
+        }
+        else
+        {
+            BMCWEB_LOG_ERROR << "Invalid property(" << key
+                             << ")in SSE filter query.";
+            return false;
+        }
+    }
+    return true;
+}
+
 class Subscription
 {
   public:
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index adb6238..da537bf 100644
--- a/redfish-core/lib/event_service.hpp
+++ b/redfish-core/lib/event_service.hpp
@@ -72,6 +72,14 @@
             retryTimeoutInterval;
         asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes;
         asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
+
+        nlohmann::json supportedSSEFilters = {
+            {"EventFormatType", true},        {"MessageId", true},
+            {"MetricReportDefinition", true}, {"RegistryPrefix", true},
+            {"OriginResource", false},        {"ResourceType", false}};
+
+        asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
+            supportedSSEFilters;
     }
 
     void doPatch(crow::Response& res, const crow::Request& req,
@@ -432,19 +440,61 @@
 
         // GET on this URI means, Its SSE subscriptionType.
         subValue->subscriptionType = "SSE";
+
+        // Default values
         subValue->protocol = "Redfish";
+        subValue->retryPolicy = "TerminateAfterRetries";
 
         char* filters = req.urlParams.get("$filter");
         if (filters == nullptr)
         {
             subValue->eventFormatType = "Event";
-            subValue->retryPolicy = "TerminateAfterRetries";
         }
         else
         {
-            // TODO: Need to read this from query params.
-            subValue->eventFormatType = "Event";
-            subValue->retryPolicy = "TerminateAfterRetries";
+            // Reading from query params.
+            bool status = readSSEQueryParams(
+                filters, subValue->eventFormatType, subValue->registryMsgIds,
+                subValue->registryPrefixes, subValue->metricReportDefinitions);
+
+            if (!status)
+            {
+                messages::invalidObject(res, filters);
+                return;
+            }
+
+            if (!subValue->eventFormatType.empty())
+            {
+                if (std::find(supportedEvtFormatTypes.begin(),
+                              supportedEvtFormatTypes.end(),
+                              subValue->eventFormatType) ==
+                    supportedEvtFormatTypes.end())
+                {
+                    messages::propertyValueNotInList(
+                        res, subValue->eventFormatType, "EventFormatType");
+                    return;
+                }
+            }
+            else
+            {
+                // If nothing specified, using default "Event"
+                subValue->eventFormatType.assign({"Event"});
+            }
+
+            if (!subValue->registryPrefixes.empty())
+            {
+                for (const std::string& it : subValue->registryPrefixes)
+                {
+                    if (std::find(supportedRegPrefixes.begin(),
+                                  supportedRegPrefixes.end(),
+                                  it) == supportedRegPrefixes.end())
+                    {
+                        messages::propertyValueNotInList(res, it,
+                                                         "RegistryPrefixes");
+                        return;
+                    }
+                }
+            }
         }
 
         std::string id =