libweave: Add more methods to modify weave::Command

Added SetProgress, SetResults, Abort, Cancel and Done methods.
base::DictionaryValue is used as type for complex values.
Switched DBusCommandProxy to weave::Command interface.

BUG=brillo:1245
TEST='FEATURES=test emerge-gizmo buffet'

Change-Id: I3a4a7f2ee63041c9f6b60b0af18d8d3d2c0cc7e6
Reviewed-on: https://chromium-review.googlesource.com/288250
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/libweave/include/weave/command.h b/libweave/include/weave/command.h
index 4a3af6b..3274664 100644
--- a/libweave/include/weave/command.h
+++ b/libweave/include/weave/command.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include <base/values.h>
+#include <chromeos/errors/error.h>
 
 namespace weave {
 
@@ -53,6 +54,25 @@
   // Returns the command results.
   virtual std::unique_ptr<base::DictionaryValue> GetResults() const = 0;
 
+  // Updates the command progress. The |progress| should match the schema.
+  // Returns false if |progress| value is incorrect.
+  virtual bool SetProgress(const base::DictionaryValue& progress,
+                           chromeos::ErrorPtr* error) = 0;
+
+  // Updates the command results. The |results| should match the schema.
+  // Returns false if |results| value is incorrect.
+  virtual bool SetResults(const base::DictionaryValue& results,
+                          chromeos::ErrorPtr* error) = 0;
+
+  // Aborts command execution.
+  virtual void Abort() = 0;
+
+  // Cancels command execution.
+  virtual void Cancel() = 0;
+
+  // Marks the command as completed successfully.
+  virtual void Done() = 0;
+
   // Returns JSON representation of the command.
   virtual std::unique_ptr<base::DictionaryValue> ToJson() const = 0;
 
diff --git a/libweave/src/base_api_handler.cc b/libweave/src/base_api_handler.cc
index 5e3f559..72f9dd4 100644
--- a/libweave/src/base_api_handler.cc
+++ b/libweave/src/base_api_handler.cc
@@ -32,7 +32,7 @@
 }
 
 void BaseApiHandler::UpdateBaseConfiguration(CommandInstance* command) {
-  command->SetProgress({});
+  command->SetProgress(base::DictionaryValue{}, nullptr);
 
   const BuffetConfig& config{device_info_->GetConfig()};
   std::string anonymous_access_role{config.local_anonymous_access_role()};
@@ -62,7 +62,7 @@
 }
 
 void BaseApiHandler::UpdateDeviceInfo(CommandInstance* command) {
-  command->SetProgress({});
+  command->SetProgress(base::DictionaryValue{}, nullptr);
 
   const BuffetConfig& config{device_info_->GetConfig()};
   std::string name{config.name()};
diff --git a/libweave/src/commands/cloud_command_proxy_unittest.cc b/libweave/src/commands/cloud_command_proxy_unittest.cc
index 06ff331..bd45e25 100644
--- a/libweave/src/commands/cloud_command_proxy_unittest.cc
+++ b/libweave/src/commands/cloud_command_proxy_unittest.cc
@@ -200,8 +200,8 @@
   EXPECT_CALL(cloud_updater_,
               UpdateCommand(kCmdID, MatchJson("{'state':'inProgress'}"), _, _))
       .WillOnce(SaveArg<2>(&on_success));
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("ready")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
 
   // Now simulate the first request completing.
   // The second request should be sent now.
@@ -217,8 +217,8 @@
   //    state=inProgress
   //    progress={...}
   // Both updates will be held until device state is updated.
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("ready")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
 
   // Now simulate the device state updated. Both updates should come in one
   // request.
@@ -235,8 +235,8 @@
   const char expect1[] = "{'state':'inProgress'}";
   EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect1), _, _))
       .WillOnce(SaveArg<3>(&on_error));
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("ready")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
 
   // Now pretend the first command update request has failed.
   // We should retry with both state and progress fields updated this time,
@@ -273,11 +273,11 @@
 
 TEST_F(CloudCommandProxyTest, GateOnStateUpdates) {
   current_state_update_id_ = 20;
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("ready")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
   current_state_update_id_ = 21;
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("busy")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'busy'}"), nullptr));
   current_state_update_id_ = 22;
   command_instance_->Done();
 
@@ -312,11 +312,11 @@
 
 TEST_F(CloudCommandProxyTest, CombineSomeStates) {
   current_state_update_id_ = 20;
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("ready")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
   current_state_update_id_ = 21;
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("busy")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'busy'}"), nullptr));
   current_state_update_id_ = 22;
   command_instance_->Done();
 
@@ -341,11 +341,11 @@
 
 TEST_F(CloudCommandProxyTest, CombineAllStates) {
   current_state_update_id_ = 20;
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("ready")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
   current_state_update_id_ = 21;
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("busy")}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'busy'}"), nullptr));
   current_state_update_id_ = 22;
   command_instance_->Done();
 
@@ -360,13 +360,14 @@
 
 TEST_F(CloudCommandProxyTest, CoalesceUpdates) {
   current_state_update_id_ = 20;
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("ready")}});
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("busy")}});
-  command_instance_->SetProgress(
-      {{"status", unittests::make_string_prop_value("finished")}});
-  command_instance_->SetResults({{"sum", unittests::make_int_prop_value(30)}});
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'busy'}"), nullptr));
+  EXPECT_TRUE(command_instance_->SetProgress(
+      *CreateDictionaryValue("{'status': 'finished'}"), nullptr));
+  EXPECT_TRUE(command_instance_->SetResults(
+      *CreateDictionaryValue("{'sum': 30}"), nullptr));
   command_instance_->Done();
 
   const char expected[] = R"({
diff --git a/libweave/src/commands/command_instance.cc b/libweave/src/commands/command_instance.cc
index 5782695..34ed8dc 100644
--- a/libweave/src/commands/command_instance.cc
+++ b/libweave/src/commands/command_instance.cc
@@ -74,6 +74,42 @@
   return TypedValueToJson(results_, nullptr);
 }
 
+bool CommandInstance::SetProgress(const base::DictionaryValue& progress,
+                                  chromeos::ErrorPtr* error) {
+  ObjectPropType obj_prop_type;
+  obj_prop_type.SetObjectSchema(command_definition_->GetProgress()->Clone());
+
+  ValueMap obj;
+  if (!TypedValueFromJson(&progress, &obj_prop_type, &obj, error))
+    return false;
+
+  // Change status even if progress unchanged, e.g. 0% -> 0%.
+  SetStatus(kStatusInProgress);
+  if (obj != progress_) {
+    progress_ = obj;
+    for (auto observer : observers_)
+      observer->OnProgressChanged();
+  }
+  return true;
+}
+
+bool CommandInstance::SetResults(const base::DictionaryValue& results,
+                                 chromeos::ErrorPtr* error) {
+  ObjectPropType obj_prop_type;
+  obj_prop_type.SetObjectSchema(command_definition_->GetResults()->Clone());
+
+  ValueMap obj;
+  if (!TypedValueFromJson(&results, &obj_prop_type, &obj, error))
+    return false;
+
+  if (obj != results_) {
+    results_ = obj;
+    for (auto observer : observers_)
+      observer->OnResultsChanged();
+  }
+  return true;
+}
+
 namespace {
 
 // Helper method to retrieve command parameters from the command definition
@@ -198,27 +234,6 @@
   observers_.push_back(observer);
 }
 
-bool CommandInstance::SetResults(const ValueMap& results) {
-  // TODO(antonm): Add validation.
-  if (results != results_) {
-    results_ = results;
-    for (auto observer : observers_)
-      observer->OnResultsChanged();
-  }
-  return true;
-}
-
-bool CommandInstance::SetProgress(const ValueMap& progress) {
-  // Change status even if progress unchanged, e.g. 0% -> 0%.
-  SetStatus(kStatusInProgress);
-  if (progress != progress_) {
-    progress_ = progress;
-    for (auto observer : observers_)
-      observer->OnProgressChanged();
-  }
-  return true;
-}
-
 void CommandInstance::Abort() {
   SetStatus(kStatusAborted);
   RemoveFromQueue();
diff --git a/libweave/src/commands/command_instance.h b/libweave/src/commands/command_instance.h
index 0fdc643..e911ede 100644
--- a/libweave/src/commands/command_instance.h
+++ b/libweave/src/commands/command_instance.h
@@ -50,6 +50,13 @@
   std::unique_ptr<base::DictionaryValue> GetParameters() const override;
   std::unique_ptr<base::DictionaryValue> GetProgress() const override;
   std::unique_ptr<base::DictionaryValue> GetResults() const override;
+  bool SetProgress(const base::DictionaryValue& progress,
+                   chromeos::ErrorPtr* error) override;
+  bool SetResults(const base::DictionaryValue& results,
+                  chromeos::ErrorPtr* error) override;
+  void Abort() override;
+  void Cancel() override;
+  void Done() override;
 
   // Returns command definition.
   const CommandDefinition* GetCommandDefinition() const {
@@ -77,21 +84,6 @@
   // Sets the pointer to queue this command is part of.
   void SetCommandQueue(CommandQueue* queue) { queue_ = queue; }
 
-  // Updates the command progress. The |progress| should match the schema.
-  // Returns false if |results| value is incorrect.
-  bool SetProgress(const ValueMap& progress);
-
-  // Updates the command results. The |results| should match the schema.
-  // Returns false if |results| value is incorrect.
-  bool SetResults(const ValueMap& results);
-
-  // Aborts command execution.
-  void Abort();
-  // Cancels command execution.
-  void Cancel();
-  // Marks the command as completed successfully.
-  void Done();
-
   // Values for command execution status.
   static const char kStatusQueued[];
   static const char kStatusInProgress[];
diff --git a/libweave/src/commands/command_instance_unittest.cc b/libweave/src/commands/command_instance_unittest.cc
index 9fbe315..4a7972a 100644
--- a/libweave/src/commands/command_instance_unittest.cc
+++ b/libweave/src/commands/command_instance_unittest.cc
@@ -41,7 +41,8 @@
               'enum': ['_withAirFlip', '_withSpin', '_withKick']
             }
           },
-          'results': {}
+          'progress': {'progress': 'integer'},
+          'results': {'testResult': 'integer'}
         },
         'speak': {
           'parameters': {
@@ -56,7 +57,7 @@
               'maximum': 10
             }
           },
-          'results': {}
+          'results': {'foo': 'integer'}
         }
       }
     })");
@@ -77,9 +78,8 @@
   CommandInstance instance{
       "robot.speak", "cloud", dict_.FindCommand("robot.speak"), params};
 
-  ValueMap results;
-  results["foo"] = int_prop.CreateValue(239, nullptr);
-  instance.SetResults(results);
+  EXPECT_TRUE(
+      instance.SetResults(*CreateDictionaryValue("{'foo': 239}"), nullptr));
 
   EXPECT_EQ("", instance.GetID());
   EXPECT_EQ("robot.speak", instance.GetName());
@@ -211,14 +211,13 @@
   })");
   auto instance =
       CommandInstance::FromJson(json.get(), "cloud", dict_, nullptr, nullptr);
-  instance->SetProgress(
-      ValueMap{{"progress", unittests::make_int_prop_value(15)}});
-  instance->SetProgress(
-      ValueMap{{"progress", unittests::make_int_prop_value(15)}});
+  EXPECT_TRUE(instance->SetProgress(*CreateDictionaryValue("{'progress': 15}"),
+                                    nullptr));
+  EXPECT_TRUE(instance->SetProgress(*CreateDictionaryValue("{'progress': 15}"),
+                                    nullptr));
   instance->SetID("testId");
-  ValueMap results;
-  instance->SetResults(
-      ValueMap{{"testResult", unittests::make_int_prop_value(17)}});
+  EXPECT_TRUE(instance->SetResults(*CreateDictionaryValue("{'testResult': 17}"),
+                                   nullptr));
 
   json->MergeDictionary(CreateDictionaryValue(R"({
     'id': 'testId',
diff --git a/libweave/src/commands/dbus_command_proxy.cc b/libweave/src/commands/dbus_command_proxy.cc
index aadf781..782a9b3 100644
--- a/libweave/src/commands/dbus_command_proxy.cc
+++ b/libweave/src/commands/dbus_command_proxy.cc
@@ -7,11 +7,7 @@
 #include <chromeos/dbus/async_event_sequencer.h>
 #include <chromeos/dbus/exported_object_manager.h>
 
-#include "libweave/src/commands/command_definition.h"
-#include "libweave/src/commands/command_instance.h"
-#include "libweave/src/commands/object_schema.h"
-#include "libweave/src/commands/prop_constraints.h"
-#include "libweave/src/commands/prop_types.h"
+#include "libweave/src/commands/dbus_conversion.h"
 
 using chromeos::dbus_utils::AsyncEventSequencer;
 using chromeos::dbus_utils::ExportedObjectManager;
@@ -20,9 +16,9 @@
 
 DBusCommandProxy::DBusCommandProxy(ExportedObjectManager* object_manager,
                                    const scoped_refptr<dbus::Bus>& bus,
-                                   CommandInstance* command_instance,
+                                   Command* command,
                                    std::string object_path)
-    : command_instance_{command_instance},
+    : command_{command},
       dbus_object_{object_manager, bus, dbus::ObjectPath{object_path}} {
 }
 
@@ -31,17 +27,17 @@
   dbus_adaptor_.RegisterWithDBusObject(&dbus_object_);
 
   // Set the initial property values before registering the DBus object.
-  dbus_adaptor_.SetName(command_instance_->GetName());
-  dbus_adaptor_.SetCategory(command_instance_->GetCategory());
-  dbus_adaptor_.SetId(command_instance_->GetID());
-  dbus_adaptor_.SetStatus(command_instance_->GetStatus());
+  dbus_adaptor_.SetName(command_->GetName());
+  dbus_adaptor_.SetCategory(command_->GetCategory());
+  dbus_adaptor_.SetId(command_->GetID());
+  dbus_adaptor_.SetStatus(command_->GetStatus());
   dbus_adaptor_.SetProgress(
-      DictionaryToDBusVariantDictionary(*command_instance_->GetProgress()));
-  dbus_adaptor_.SetOrigin(command_instance_->GetOrigin());
+      DictionaryToDBusVariantDictionary(*command_->GetProgress()));
+  dbus_adaptor_.SetOrigin(command_->GetOrigin());
   dbus_adaptor_.SetParameters(
-      DictionaryToDBusVariantDictionary(*command_instance_->GetParameters()));
+      DictionaryToDBusVariantDictionary(*command_->GetParameters()));
   dbus_adaptor_.SetResults(
-      DictionaryToDBusVariantDictionary(*command_instance_->GetResults()));
+      DictionaryToDBusVariantDictionary(*command_->GetResults()));
 
   // Register the command DBus object and expose its methods and properties.
   dbus_object_.RegisterAsync(completion_callback);
@@ -49,16 +45,16 @@
 
 void DBusCommandProxy::OnResultsChanged() {
   dbus_adaptor_.SetResults(
-      DictionaryToDBusVariantDictionary(*command_instance_->GetResults()));
+      DictionaryToDBusVariantDictionary(*command_->GetResults()));
 }
 
 void DBusCommandProxy::OnStatusChanged() {
-  dbus_adaptor_.SetStatus(command_instance_->GetStatus());
+  dbus_adaptor_.SetStatus(command_->GetStatus());
 }
 
 void DBusCommandProxy::OnProgressChanged() {
   dbus_adaptor_.SetProgress(
-      DictionaryToDBusVariantDictionary(*command_instance_->GetProgress()));
+      DictionaryToDBusVariantDictionary(*command_->GetProgress()));
 }
 
 void DBusCommandProxy::OnCommandDestroyed() {
@@ -68,49 +64,40 @@
 bool DBusCommandProxy::SetProgress(
     chromeos::ErrorPtr* error,
     const chromeos::VariantDictionary& progress) {
-  LOG(INFO) << "Received call to Command<" << command_instance_->GetName()
+  LOG(INFO) << "Received call to Command<" << command_->GetName()
             << ">::SetProgress()";
-
-  auto progress_schema =
-      command_instance_->GetCommandDefinition()->GetProgress();
-  ValueMap obj;
-  if (!ObjectFromDBusVariant(progress_schema, progress, &obj, error))
+  auto dictionary = DictionaryFromDBusVariantDictionary(progress, error);
+  if (!dictionary)
     return false;
-
-  command_instance_->SetProgress(obj);
-  return true;
+  return command_->SetProgress(*dictionary, error);
 }
 
 bool DBusCommandProxy::SetResults(chromeos::ErrorPtr* error,
                                   const chromeos::VariantDictionary& results) {
-  LOG(INFO) << "Received call to Command<" << command_instance_->GetName()
+  LOG(INFO) << "Received call to Command<" << command_->GetName()
             << ">::SetResults()";
-
-  auto results_schema = command_instance_->GetCommandDefinition()->GetResults();
-  ValueMap obj;
-  if (!ObjectFromDBusVariant(results_schema, results, &obj, error))
+  auto dictionary = DictionaryFromDBusVariantDictionary(results, error);
+  if (!dictionary)
     return false;
-
-  command_instance_->SetResults(obj);
-  return true;
+  return command_->SetResults(*dictionary, error);
 }
 
 void DBusCommandProxy::Abort() {
-  LOG(INFO) << "Received call to Command<" << command_instance_->GetName()
+  LOG(INFO) << "Received call to Command<" << command_->GetName()
             << ">::Abort()";
-  command_instance_->Abort();
+  command_->Abort();
 }
 
 void DBusCommandProxy::Cancel() {
-  LOG(INFO) << "Received call to Command<" << command_instance_->GetName()
+  LOG(INFO) << "Received call to Command<" << command_->GetName()
             << ">::Cancel()";
-  command_instance_->Cancel();
+  command_->Cancel();
 }
 
 void DBusCommandProxy::Done() {
-  LOG(INFO) << "Received call to Command<" << command_instance_->GetName()
+  LOG(INFO) << "Received call to Command<" << command_->GetName()
             << ">::Done()";
-  command_instance_->Done();
+  command_->Done();
 }
 
 }  // namespace weave
diff --git a/libweave/src/commands/dbus_command_proxy.h b/libweave/src/commands/dbus_command_proxy.h
index 62225c7..66ae17b 100644
--- a/libweave/src/commands/dbus_command_proxy.h
+++ b/libweave/src/commands/dbus_command_proxy.h
@@ -29,7 +29,7 @@
  public:
   DBusCommandProxy(chromeos::dbus_utils::ExportedObjectManager* object_manager,
                    const scoped_refptr<dbus::Bus>& bus,
-                   CommandInstance* command_instance,
+                   Command* command,
                    std::string object_path);
   ~DBusCommandProxy() override = default;
 
@@ -57,7 +57,7 @@
   // Handles calls to org.chromium.Buffet.Command.Done().
   void Done() override;
 
-  CommandInstance* command_instance_;
+  Command* command_;
   org::chromium::Buffet::CommandAdaptor dbus_adaptor_{this};
   chromeos::dbus_utils::DBusObject dbus_object_;
 
diff --git a/libweave/src/device_registration_info_unittest.cc b/libweave/src/device_registration_info_unittest.cc
index 9ef7731..9c45afd 100644
--- a/libweave/src/device_registration_info_unittest.cc
+++ b/libweave/src/device_registration_info_unittest.cc
@@ -24,9 +24,10 @@
 
 namespace weave {
 
-using chromeos::http::request_header::kAuthorization;
 using chromeos::http::fake::ServerRequest;
 using chromeos::http::fake::ServerResponse;
+using chromeos::http::request_header::kAuthorization;
+using unittests::CreateDictionaryValue;
 
 namespace {
 
@@ -522,6 +523,7 @@
     'robot': {
       '_jump': {
         'parameters': {'_height': 'integer'},
+        'progress': {'progress': 'integer'},
         'results': {'status': 'string'},
         'minimalRole': 'user'
       }
@@ -543,9 +545,6 @@
   PublishCommands(*command_list);
   auto command = command_manager_->FindCommand("1234");
   ASSERT_NE(nullptr, command);
-  StringPropType string_type;
-  ValueMap results{
-      {"status", string_type.CreateValue(std::string{"Ok"}, nullptr)}};
 
   // UpdateCommand when setting command results.
   auto update_command_results = [](const ServerRequest& request,
@@ -559,7 +558,8 @@
   transport_->AddHandler(command_url, chromeos::http::request_type::kPatch,
                          base::Bind(update_command_results));
 
-  command->SetResults(results);
+  EXPECT_TRUE(
+      command->SetResults(*CreateDictionaryValue("{'status': 'Ok'}"), nullptr));
 
   // UpdateCommand when setting command progress.
   int count = 0;  // This will be called twice...
@@ -579,8 +579,8 @@
   transport_->AddHandler(command_url, chromeos::http::request_type::kPatch,
                          base::Bind(update_command_progress));
 
-  ValueMap progress{{"progress", unittests::make_int_prop_value(18)}};
-  command->SetProgress(progress);
+  EXPECT_TRUE(command->SetProgress(*CreateDictionaryValue("{'progress': 18}"),
+                                   nullptr));
 
   // UpdateCommand when changing command status.
   auto update_command_state = [](const ServerRequest& request,