Report: make dbus properties writable

ReadingParametersFutureVersion interface for Reports is enhanced from
read-only to read write and supports modification of readingParameteres,
which is done by updating metrics at the first place and re-registering
them for sensors updates. Similar ReportActions interface is enhanced to
read write. Additionally reportActions policy was modified that reports
always contain 'LogToMetricReportsCollection' action. The whole change
enables Redfish support for PATCH method added on webserver side.

Tested:
- New unit tests were created, ran all new and previous UTs, all passed
- Tested under QEMU, interface was checked for RW via dbus cli commands,
  checked if Reading Parameters of the Report can be read or written,
  if metrics are properly updated and registration/unregistration for
  updates works properly, checked if actions of Report can be read or
  written, if actions are properly updated and related action behavior
  follows the change accordingly
- Tested under QEMU, verified if 'LogToMetricReportsCollection' action
  is always added when Report is created or when actions of Report are
  updated by dbus interface
- Tested via webserver if it communicates properly with dbus interfaces
  of Telemetry and read/write operations via Redfish can be successfully
  executed

Signed-off-by: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com>
Change-Id: I7f2fe8eae1631c436cf61a516d5fd0b8358a76bd
diff --git a/src/report.cpp b/src/report.cpp
index 18ca9c1..12d88c7 100644
--- a/src/report.cpp
+++ b/src/report.cpp
@@ -24,10 +24,11 @@
                interfaces::ReportManager& reportManager,
                interfaces::JsonStorage& reportStorageIn,
                std::vector<std::shared_ptr<interfaces::Metric>> metricsIn,
+               const interfaces::ReportFactory& reportFactory,
                const bool enabledIn, std::unique_ptr<interfaces::Clock> clock) :
     id(reportId),
     name(reportName), reportingType(reportingTypeIn), interval(intervalIn),
-    reportActions(std::move(reportActionsIn)),
+    reportActions(reportActionsIn.begin(), reportActionsIn.end()),
     sensorCount(getSensorCount(metricsIn)),
     appendLimit(deduceAppendLimit(appendLimitIn)),
     reportUpdates(reportUpdatesIn),
@@ -51,6 +52,8 @@
                 std::get<1>(sensorData.front()));
         });
 
+    reportActions.insert(ReportAction::logToMetricReportsCollection);
+
     deleteIface = objServer->add_unique_interface(
         getPath(), deleteIfaceName,
         [this, &ioc, &reportManager](auto& dbusIface) {
@@ -66,7 +69,7 @@
         });
 
     persistency = storeConfiguration();
-    reportIface = makeReportInterface();
+    reportIface = makeReportInterface(reportFactory);
 
     if (reportingType == ReportingType::periodic)
     {
@@ -153,22 +156,28 @@
     }
 }
 
+void Report::setReadingBuffer(const ReportUpdates newReportUpdates)
+{
+    if (reportingType != ReportingType::onRequest &&
+        (reportUpdates == ReportUpdates::overwrite ||
+         newReportUpdates == ReportUpdates::overwrite))
+    {
+        readingsBuffer.clearAndResize(
+            deduceBufferSize(newReportUpdates, reportingType));
+    }
+}
+
 void Report::setReportUpdates(const ReportUpdates newReportUpdates)
 {
     if (reportUpdates != newReportUpdates)
     {
-        if (reportingType != ReportingType::onRequest &&
-            (reportUpdates == ReportUpdates::overwrite ||
-             newReportUpdates == ReportUpdates::overwrite))
-        {
-            readingsBuffer.clearAndResize(
-                deduceBufferSize(newReportUpdates, reportingType));
-        }
+        setReadingBuffer(newReportUpdates);
         reportUpdates = newReportUpdates;
     }
 }
 
-std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface()
+std::unique_ptr<sdbusplus::asio::dbus_interface>
+    Report::makeReportInterface(const interfaces::ReportFactory& reportFactory)
 {
     auto dbusIface =
         objServer->add_unique_interface(getPath(), reportIfaceName);
@@ -241,29 +250,69 @@
         },
         [this](const auto&) { return persistency; });
 
-    auto readingsFlag = sdbusplus::vtable::property_::none;
-    if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
-    {
-        readingsFlag = sdbusplus::vtable::property_::emits_change;
-    }
-    dbusIface->register_property_r("Readings", readings, readingsFlag,
+    dbusIface->register_property_r("Readings", readings,
+                                   sdbusplus::vtable::property_::emits_change,
                                    [this](const auto&) { return readings; });
-    dbusIface->register_property_r(
-        "ReportingType", std::string(), sdbusplus::vtable::property_::const_,
+    dbusIface->register_property_rw(
+        "ReportingType", std::string(),
+        sdbusplus::vtable::property_::emits_change,
+        [this](auto newVal, auto& oldVal) {
+            ReportingType tmp = utils::toReportingType(newVal);
+            if (tmp != reportingType)
+            {
+                if (tmp == ReportingType::onChange)
+                {
+                    throw sdbusplus::exception::SdBusError(
+                        static_cast<int>(std::errc::invalid_argument),
+                        "Invalid reportingType");
+                }
+                if (tmp == ReportingType::periodic)
+                {
+                    if (interval < ReportManager::minInterval)
+                    {
+                        throw sdbusplus::exception::SdBusError(
+                            static_cast<int>(std::errc::invalid_argument),
+                            "Invalid interval");
+                    }
+                    if (enabled == true)
+                    {
+                        scheduleTimer(interval);
+                    }
+                }
+                else
+                {
+                    timer.cancel();
+                }
+                reportingType = tmp;
+                setReadingBuffer(reportUpdates);
+                persistency = storeConfiguration();
+            }
+            oldVal = std::move(newVal);
+            return 1;
+        },
         [this](const auto&) { return utils::enumToString(reportingType); });
     dbusIface->register_property_r(
         "ReadingParameters", readingParametersPastVersion,
         sdbusplus::vtable::property_::const_,
         [this](const auto&) { return readingParametersPastVersion; });
-    dbusIface->register_property_r(
+    dbusIface->register_property_rw(
         "ReadingParametersFutureVersion", readingParameters,
-        sdbusplus::vtable::property_::const_,
+        sdbusplus::vtable::property_::emits_change,
+        [this, &reportFactory](auto newVal, auto& oldVal) {
+            reportFactory.updateMetrics(metrics, enabled, newVal);
+            readingParameters = toReadingParameters(
+                utils::transform(metrics, [](const auto& metric) {
+                    return metric->dumpConfiguration();
+                }));
+            persistency = storeConfiguration();
+            oldVal = std::move(newVal);
+            return 1;
+        },
         [this](const auto&) { return readingParameters; });
     dbusIface->register_property_r(
-        "EmitsReadingsUpdate", bool{}, sdbusplus::vtable::property_::const_,
+        "EmitsReadingsUpdate", bool{}, sdbusplus::vtable::property_::none,
         [this](const auto&) {
-            return utils::contains(reportActions,
-                                   ReportAction::emitsReadingsUpdate);
+            return reportActions.contains(ReportAction::emitsReadingsUpdate);
         });
     dbusIface->register_property_r("Name", std::string{},
                                    sdbusplus::vtable::property_::const_,
@@ -271,15 +320,32 @@
     dbusIface->register_property_r(
         "LogToMetricReportsCollection", bool{},
         sdbusplus::vtable::property_::const_, [this](const auto&) {
-            return utils::contains(reportActions,
-                                   ReportAction::logToMetricReportsCollection);
+            return reportActions.contains(
+                ReportAction::logToMetricReportsCollection);
         });
-    dbusIface->register_property_r(
+    dbusIface->register_property_rw(
         "ReportActions", std::vector<std::string>{},
-        sdbusplus::vtable::property_::const_, [this](const auto&) {
-            return utils::transform(reportActions, [](const auto reportAction) {
-                return utils::enumToString(reportAction);
-            });
+        sdbusplus::vtable::property_::emits_change,
+        [this](auto newVal, auto& oldVal) {
+            auto tmp = utils::transform<std::unordered_set>(
+                newVal, [](const auto& reportAction) {
+                    return utils::toReportAction(reportAction);
+                });
+            tmp.insert(ReportAction::logToMetricReportsCollection);
+
+            if (tmp != reportActions)
+            {
+                reportActions = tmp;
+                persistency = storeConfiguration();
+                oldVal = std::move(newVal);
+            }
+            return 1;
+        },
+        [this](const auto&) {
+            return utils::transform<std::vector>(
+                reportActions, [](const auto reportAction) {
+                    return utils::enumToString(reportAction);
+                });
         });
     dbusIface->register_property_r(
         "AppendLimit", appendLimit.value_or(sensorCount),
@@ -366,7 +432,10 @@
             .count(),
         std::vector<ReadingData>(readingsBuffer.begin(), readingsBuffer.end())};
 
-    reportIface->signal_property("Readings");
+    if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
+    {
+        reportIface->signal_property("Readings");
+    }
 }
 
 bool Report::storeConfiguration() const